Linux C++ 编程配置文件加载方法

前段时间研究了如何在Linux上读取配置文件,基于手头上的项目也成功实现了,主要通过在C++文件中用定义需从配置文件读取的配置属性为全局变量,然后通过读取函数来更新配置属性。
其中也附有日志等级函数的打印输出。
————————————————————————

目录

  1. 用宏定义配置文件的路径
  2. 将配置属性定义为全局变量
  3. 相关全局变量
  4. 配置文件读取函数及其相关函数的初始化
  5. 函数的具体定义
  6. 配置文件作用方法
  7. 配置文件config.ini内容
    —————————————————————————

1. 用宏定义配置文件的路径

//***********[START]相关宏定义[START]***************
//配置文件路径
#define CONFIG_FILE_PATH "./config.ini"
//日志等级的宏定义
#define LOG_ERR -1
#define LOG_INF 0
#define LOG_DBG 1
//配置文件关键字长度限制
#define KEYVALLEN 256
//是(1)否(0)使用线程锁,调试发现不使用锁就不会有延迟
#define USE_PTHREAD_LOCK 0
//*************[END]相关宏定义[END]*****************

2. 将配置属性定义为全局变量

//*****[START]从配置文件中获取的全局变量及其默认值[START]******
//------日志配置参数------
//日志路径,因为一般将结果保存在“result”文件夹中,所以要使用“result/***.log”的格式
char gconf_szLogPath[256] = "result/testResult.log";
//设置程序日志等级:1 表示打印所有(调试,一般,错误信息),0 表示打印运行(一般,错误)信息,-1 表示仅打印错误信息.
int gconf_nLogLevel = 1;
//设置程序日志大小上限(单位为bytes,0表示不设置),超过后日志清空,最大2,147,483,647,建议不要超过104,857,600(即100*1024*1024)
int gconf_lMaxLogSize = 104857600;
//程序开始运行时,日志是(1)否(0)清空
int gconf_nLogReset = 1;
//------其余配置参数------
//设置程序的运行时间,达到时间后终止程序,方便调试.(单位为秒,0表示一直运行),原为DURATION_TIME
int gconf_nProgramDurationTime = 0;
//*****[END]从配置文件中获取的全局变量及其默认值[END]******

3. 相关全局变量

//********[START]相关全局变量[START]****************
//程序首次运行标记
bool g_bFirstRun = true;
//配置文件加载次数
int g_nCountLoadConfig = 0;
//用于获取配置文件属性
struct stat g_statConfigFile = {0};
//定义车牌及其数目容器
static vector<string> gs_szVecLicense;
static vector<int> gs_nVecCount;
//时间戳的全局变量
clock_t g_clockLast;
//线程ID
pthread_t g_pthreadRunIPCameraId;
//线程锁
pthread_mutex_t mutex;
//**********[END]相关全局变量[END]******************

4. 配置文件读取函数及其相关函数的初始化

//**********[START]函数的初始化[START]**************

//-------------结果目录创建函数----------------
//-功能: 处理输入的结果路径,截取相应的目录名,在工作目录上创建相应目录,用于保存结果.
int CreateDir(char* szPath,char* szDirPath);

//-------------日志打印函数--------------------
//-功能: 把日志信息保存到日志文件中,路径为从配置文件中获取的全局变量gconf_szLogPath所定义.
void PrintLOG(int nType,const char* ms, ... );

//-------------结果输出函数--------------------
//-功能: 将结果识别结果存放到文本文件中,路径为从配置文件中获取的全局变量gconf_szPlateResultPath所定义.
void PrintResult(const char* ms, ... );

//---------配置文件读取辅助函数,删左空格-------
//-功能: 删除从配置文件中读取到的字符串的左空格.
char * l_trim(char * szOutput, const char *szInput);

//---------配置文件读取辅助函数,删右空格-------
//-功能: 删除从配置文件中读取到的字符串的右空格.
char *r_trim(char *szOutput, const char *szInput);

//---------配置文件读取辅助函数,删两边空格-------
//-功能: 删除从配置文件中读取到的字符串的两边空格.
char * a_trim(char * szOutput, const char * szInput);

//---------------配置文件检查函数-------------
//-功能: 检查配置文件是否被修改,如果被修改则返回1,没变返回0,错误返回-1.
static int CheckConfigFileSizeTime();

//---------------配置值读取函数----------------
//-功能: 从配置文件中读取对应配置值.
int GetConfigValue(char *szLabelInfoName, char *szKeyName, char *szKeyVal );

//---------------配置值读取函数(重载)---------
//-功能: 从配置文件中读取对应配置值,为int型重载.
int GetConfigValue(char *szLabelInfoName, char *szKeyName, int *pnKeyVal );

//---------------配置文件作用函数--------------
//-功能: 从配置文件中读取配置,并使之生效.
int LoadConfigFile();

//-------------配置文件重新作用函数------------
//-功能: 从配置文件中重新读取配置,并通过线程的重启来生效.
//       (此函数功能并未完善,程序也未调用此函数)
int ReloadConfigFile();

//************[END]函数的初始化[END]****************

5. 函数的具体定义


//**********[STAT]函数具体定义[STAT]**************

//-------------结果目录创建函数----------------
//-功能: 处理输入的结果路径,截取相应的目录名,在工作目录上创建相应目录,用于保存结果.
//-Input: 结果路径
//-Output: 创建相应目录
//-Return: 如果成功返回1,错误返回-1
//--------------------------------------------
int CreateDir(char* szPath,char* szDirPath)
{
    char* pTmpPathStart;
    char* pTmpPathEnd;
    pTmpPathStart = szPath;
    while(pTmpPathEnd = strchr(pTmpPathStart,'/'))
    {
        memset(szDirPath,0,sizeof(szDirPath));
        strncpy(szDirPath,szPath,(size_t)(pTmpPathEnd-szPath));
        pTmpPathStart = pTmpPathEnd + 1;
        //printf("%s\n",szDirPath);
        if(access(szDirPath,0)!=0)
        {
            if(mkdir(szDirPath,S_IRWXU)==-1)
            {
                //PrintLOG(LOG_INF,"创建相应目录出错:%d!请检查路径是否正确.",errno);
                return -1;
            }
        }

    }
    return 1;
}

//-------------日志打印函数--------------------
//-功能: 把日志信息保存到日志文件中,路径为从配置文件中获取的全局变量gconf_szLogPath所定义.
//-Input: 1.日志信息类型
//        2.与printf函数相似的格式化字符串
//-Output: 打印到日志文件的结果
//-Return: 无
//--------------------------------------------
void PrintLOG(int nLogType,const char* ms, ... )
{
    char wzLog[1024] = {0};
    char buffer[1024] = {0};
    char szNULL[256] = "";
    va_list args;
    va_start(args, ms);
    vsprintf( wzLog ,ms,args);
    va_end(args);

    struct stat statLogFile = {0};

    time_t now;
    time(&now);
    struct tm *local;
    struct timeval tv;
    local = localtime(&now);
    gettimeofday(&tv, NULL);

    //mkdir("result",S_IRWXU);
    CreateDir(gconf_szLogPath,szNULL);
    FILE* file = fopen(gconf_szLogPath,"a+");
    if(gconf_nLogReset == 1 && g_bFirstRun)
    {
        fclose(file);
        unlink(gconf_szLogPath);
        file = fopen(gconf_szLogPath,"a+");
        g_bFirstRun = false;
    }
    if (stat (gconf_szLogPath, &statLogFile) == -1)
    {
        PrintLOG(LOG_ERR,"获取日志文件(%s)状态失败:%s.\n", gconf_szLogPath, strerror (errno));
        return;
    }
    if (S_ISDIR(statLogFile.st_mode))
    {
        PrintLOG(LOG_ERR,"此日志文件路径(%s)是文件夹.错误代码为:%s!", gconf_szLogPath, strerror (errno));
        return;
    }
    if (S_ISREG(statLogFile.st_mode))
    {
        ;//PrintLOG(LOG_INF,"日志文件(%s)大小为:%ld bytes, 最近修改时间为: %s.",gconf_szLogPath, statLogFile.st_size, ctime (&statLogFile.st_mtime));
    }
    if(statLogFile.st_size >= gconf_lMaxLogSize && gconf_lMaxLogSize != 0){
        fclose(file);
        //printf("日志文件已满,程序终止/n.");
        //exit(1);
        unlink(gconf_szLogPath);
        file = fopen(gconf_szLogPath,"a+");
    }

    if(gconf_nLogLevel == 1){
        if(nLogType == 1){
            sprintf(buffer,"[%04d-%02d-%02d %02d:%02d:%02d.%03ld][DBG] %s\n",local->tm_year+1900, local->tm_mon,local->tm_mday, local->tm_hour, local->tm_min, local->tm_sec,tv.tv_usec/1000,wzLog);
        }
        if(nLogType == 0){
            sprintf(buffer,"[%04d-%02d-%02d %02d:%02d:%02d.%03ld][INF] %s\n",local->tm_year+1900, local->tm_mon,local->tm_mday, local->tm_hour, local->tm_min, local->tm_sec,tv.tv_usec/1000,wzLog);
        }
        if(nLogType == -1){
            sprintf(buffer,"[%04d-%02d-%02d %02d:%02d:%02d.%03ld][ERR] %s\n",local->tm_year+1900, local->tm_mon,local->tm_mday, local->tm_hour, local->tm_min, local->tm_sec,tv.tv_usec/1000,wzLog);
        }
    }
    if(nLogType <= gconf_nLogLevel){
        fwrite(buffer,1,strlen(buffer),file);
        printf("%s",buffer);
        fclose(file);
    }

    //  syslog(LOG_INFO,wzLog);
    return ;
}

//-------------结果输出函数--------------------
//-功能: 将结果识别结果存放到文本文件中,路径为从配置文件中获取的全局变量gconf_szPlateResultPath所定义.
//-Input: 1.与printf函数相似的格式化字符串
//-Output: 打印到结果文件的车牌识别结果
//-Return: 无
//--------------------------------------------
void PrintResult(const char* ms, ... )
{
    char wzLog[1024] = {0};
    char buffer[1024] = {0};
    char szNULL[256] = "";
    va_list args;
    va_start(args, ms);
    vsprintf( wzLog ,ms,args);
    va_end(args);

    time_t now;
    time(&now);
    struct tm *local;
    struct timeval tv;
    local = localtime(&now);
    gettimeofday(&tv, NULL);
    sprintf(buffer,"[%04d-%02d-%02d %02d:%02d:%02d.%03ld]%s\n",local->tm_year+1900, local->tm_mon,local->tm_mday, local->tm_hour, local->tm_min, local->tm_sec,tv.tv_usec/1000,wzLog);
    printf("%s",buffer);

    //mkdir("result",S_IRWXU);
    CreateDir(gconf_szPlateResultPath,szNULL);

    FILE* file = fopen(gconf_szPlateResultPath,"a+");
    fwrite(buffer,1,strlen(buffer),file);
    fclose(file);

    return ;
}

//---------配置文件读取辅助函数,删左空格-------
//-功能: 删除从配置文件中读取到的字符串的左空格.
//-Input: 1.被处理后的字符串指针
//        2.待处理的字符串指正
//-Output: 处理完的字符串
//-Return: 字符拷贝函数的返回结果
//--------------------------------------------
char * l_trim(char * szOutput, const char *szInput)
{
    assert(szInput != NULL);
    assert(szOutput != NULL);
    assert(szOutput != szInput);
    for   (NULL; *szInput != '\0' && isspace(*szInput); ++szInput){
        ;
    }
    return strcpy(szOutput, szInput);
}

//---------配置文件读取辅助函数,删右空格-------
//-功能: 删除从配置文件中读取到的字符串的右空格.
//-Input: 1.被处理后的字符串指针
//        2.待处理的字符串指正
//-Output: 处理完的字符串
//-Return: 字符拷贝函数的返回结果
//--------------------------------------------
char *r_trim(char *szOutput, const char *szInput)
{
    char *p = NULL;
    assert(szInput != NULL);
    assert(szOutput != NULL);
    assert(szOutput != szInput);
    strcpy(szOutput, szInput);
    for(p = szOutput + strlen(szOutput) - 1; p >= szOutput && isspace(*p); --p){
        ;
    }
    *(++p) = '\0';
    return szOutput;
}

//---------配置文件读取辅助函数,删两边空格-------
//-功能: 删除从配置文件中读取到的字符串的两边空格.
//-Input: 1.被处理后的字符串指针
//        2.待处理的字符串指正
//-Output: 处理完的字符串
//-Return: 字符拷贝函数的返回结果
//--------------------------------------------
char * a_trim(char * szOutput, const char * szInput)
{
    char *p = NULL;
    assert(szInput != NULL);
    assert(szOutput != NULL);
    l_trim(szOutput, szInput);
    for   (p = szOutput + strlen(szOutput) - 1;p >= szOutput && isspace(*p); --p){
        ;
    }
    *(++p) = '\0';
    return szOutput;
}


//---------------配置文件检查函数-------------
//-功能: 检查配置文件是否被修改,如果被修改则返回1,没变返回0,错误返回-1.
//-Input: 无
//-Output: 无
//-Return: 如果被修改则返回1,没变返回0,错误返回-1
//--------------------------------------------
static int CheckConfigFileSizeTime ()
{
    struct stat statTmpConfigFile = {0};
    if (stat (CONFIG_FILE_PATH, &statTmpConfigFile) == -1)
    {
        PrintLOG(LOG_ERR,"获取配置文件(%s)状态失败:%s.\n", CONFIG_FILE_PATH, strerror (errno));
        return (-1);
    }
    if (S_ISDIR(statTmpConfigFile.st_mode))
    {
        PrintLOG(LOG_ERR,"此配置文件路径(%s)是文件夹. 错误代码为:%s!", CONFIG_FILE_PATH, strerror (errno));
        return (-1);
    }
    if (S_ISREG (statTmpConfigFile.st_mode))
    {
        ;//PrintLOG(LOG_INF,"配置文件(%s)大小为:%ld bytes, 最近修改时间为:%s",CONFIG_FILE_PATH, statTmpConfigFile.st_size, ctime (&statTmpConfigFile.st_mtime));
    }
    //如果配置文件未被修改,则返回0
    if(statTmpConfigFile.st_size == g_statConfigFile.st_size && statTmpConfigFile.st_mtime == g_statConfigFile.st_mtime){
        return 0;
    }
    //如果配置文件被且是首次运行程序修改,则打印相应提示,且调用配置文件读取函数读取相应配置值
    else if(g_bFirstRun){
        PrintLOG(LOG_INF,"加载配置文件.");
        g_statConfigFile = statTmpConfigFile;
        return 1;
    }
    //如果配置文件被且不是首次运行程序修改,则打印提示,且调用配置文件读取函数读取相应配置值
    else{
        PrintLOG(LOG_INF,"重新加载被修改的配置文件.");
        g_statConfigFile = statTmpConfigFile;
        return 1;
    }
}

//---------------配置值读取函数----------------
//-功能: 从配置文件中读取对应配置值.
//-Input: 1.配置文件中待读取的标签信息名
//        2.配置文件中指定标签中的待读取关键字名
//        3.配置文件中指定标签和关键字的对应配置值(char型指针)
//-Output: 相应配置值
//-Return: 如果读取成功返回1,错误返回-1
//--------------------------------------------
int GetConfigValue(char *szLabelInfoName, char *szKeyName, char *szKeyVal )
{
    char szTmpLabelInfoName[32],szTmpKeyName[32];
    char *buf,*c;
    char buf_i[KEYVALLEN], buf_o[KEYVALLEN];
    FILE *fp;
    int found=0; /* 1 szLabelInfoName 2 szKeyName */
    if( (fp=fopen( CONFIG_FILE_PATH,"r" ))==NULL ){
        printf( "openfile [%s] error [%s]\n",CONFIG_FILE_PATH,strerror(errno) );
        return(-1);
    }
    fseek( fp, 0, SEEK_SET );
    memset( szTmpLabelInfoName, 0, sizeof(szTmpLabelInfoName) );
    sprintf( szTmpLabelInfoName,"[%s]", szLabelInfoName );

    while( !feof(fp) && fgets( buf_i, KEYVALLEN, fp )!=NULL ){
        l_trim(buf_o, buf_i);
        if( strlen(buf_o) <= 0 )
            continue;
        buf = NULL;
        buf = buf_o;

        if( found == 0 ){
            if( buf[0] != '[' ) {
                continue;
            } else if ( strncmp(buf,szTmpLabelInfoName,strlen(szTmpLabelInfoName))==0 ){
                found = 1;
                continue;
            }

        } else if( found == 1 ){
            if( buf[0] == '#' ){
                continue;
            } else if ( buf[0] == '[' ) {
                break;
            } else {
                if( (c = (char*)strchr(buf, '=')) == NULL )
                    continue;
                memset( szTmpKeyName, 0, sizeof(szTmpKeyName) );

                sscanf( buf, "%[^=|^ |^\t]", szTmpKeyName );
                if( strcmp(szTmpKeyName, szKeyName) == 0 ){
                    sscanf( ++c, "%[^\n]", szKeyVal );
                    char *szTmpKeyVal_o = (char *)malloc(strlen(szKeyVal) + 1);
                    if(szTmpKeyVal_o != NULL){
                        memset(szTmpKeyVal_o, 0, sizeof(szTmpKeyVal_o));
                        a_trim(szTmpKeyVal_o, szKeyVal);
                        if(szTmpKeyVal_o && strlen(szTmpKeyVal_o) > 0)
                            strcpy(szKeyVal, szTmpKeyVal_o);
                        free(szTmpKeyVal_o);
                        szTmpKeyVal_o = NULL;
                    }
                    found = 2;
                    break;
                } else {
                    continue;
                }
            }
        }
    }
    fclose( fp );
    if( found == 2 )
        return(1);
    else
        return(-1);
}

//---------------配置值读取函数(重载)---------
//-功能: 从配置文件中读取对应配置值,为int型重载.
//-Input: 1.配置文件中待读取的标签信息名
//        2.配置文件中指定标签中的待读取关键字名
//        3.配置文件中指定标签和关键字的对应配置值(int型指针)
//-Output: 相应配置值
//-Return: 如果读取成功返回1,错误返回-1
//--------------------------------------------
int GetConfigValue(char *szLabelInfoName, char *szKeyName, int *pnKeyVal )
{
    char szTmpLabelInfoName[32],szTmpKeyName[32],szTmpKeyVal[32];
    char *buf,*c;
    char buf_i[KEYVALLEN], buf_o[KEYVALLEN];
    FILE *fp;
    int found=0; /* 1 szLabelInfoName 2 szKeyName */
    if( (fp=fopen( CONFIG_FILE_PATH,"r" ))==NULL ){
        printf( "openfile [%s] error [%s]\n",CONFIG_FILE_PATH,strerror(errno) );
        return(-1);
    }
    fseek( fp, 0, SEEK_SET );
    memset( szTmpLabelInfoName, 0, sizeof(szTmpLabelInfoName) );
    sprintf( szTmpLabelInfoName,"[%s]", szLabelInfoName );

    while( !feof(fp) && fgets( buf_i, KEYVALLEN, fp )!=NULL ){
        l_trim(buf_o, buf_i);
        if( strlen(buf_o) <= 0 )
            continue;
        buf = NULL;
        buf = buf_o;

        if( found == 0 ){
            if( buf[0] != '[' ) {
                continue;
            } else if ( strncmp(buf,szTmpLabelInfoName,strlen(szTmpLabelInfoName))==0 ){
                found = 1;
                continue;
            }

        } else if( found == 1 ){
            if( buf[0] == '#' ){
                continue;
            } else if ( buf[0] == '[' ) {
                break;
            } else {
                if( (c = (char*)strchr(buf, '=')) == NULL )
                    continue;
                memset( szTmpKeyName, 0, sizeof(szTmpKeyName) );

                sscanf( buf, "%[^=|^ |^\t]", szTmpKeyName );
                if( strcmp(szTmpKeyName, szKeyName) == 0 ){
                    sscanf( ++c, "%[^\n]", szTmpKeyVal );
                    char *szTmpKeyVal_o = (char *)malloc(strlen(szTmpKeyVal) + 1);
                    if(szTmpKeyVal_o != NULL){
                        memset(szTmpKeyVal_o, 0, sizeof(szTmpKeyVal_o));
                        a_trim(szTmpKeyVal_o, szTmpKeyVal);
                        if(szTmpKeyVal_o && strlen(szTmpKeyVal_o) > 0)
                            strcpy(szTmpKeyVal, szTmpKeyVal_o);
                        free(szTmpKeyVal_o);
                        szTmpKeyVal_o = NULL;
                    }
                    found = 2;
                    break;
                } else {
                    continue;
                }
            }
        }
    }
    *pnKeyVal = atoi(szTmpKeyVal);
    fclose( fp );
    if( found == 2 )
        return(0);
    else
        return(-1);
}

//---------------配置文件作用函数--------------
//-功能: 从配置文件中读取配置,并使之生效.
//-Input: 无
//-Output: 相应配置值
//-Return: 如果成功返回1,错误返回-1
//--------------------------------------------
int LoadConfigFile()
{
    bool bGetLogPath = false;
    bool bGetLogLevel = false;
    bool bGetMaxLogSize = false;
    bool bGetLogReset = false;

    int ret = 0;
    ret = CheckConfigFileSizeTime();
    if(ret != 1){
        return ret;
    }

    //从配置文件中获取日志信息中的日志路径
    if(GetConfigValue((char*)"Log_Info",(char*)"log_path",gconf_szLogPath) == -1){
        PrintLOG(LOG_ERR,"获取日志路径失败!");
    }
    else
    {
        bGetLogPath = true;
        //PrintLOG(LOG_INF,"日志路径为:%s",gconf_szLogPath);
    }
    //从配置文件中获取日志信息中的程序日志级别
    if(GetConfigValue((char*)"Log_Info",(char*)"log_levle",&gconf_nLogLevel) == -1){
        PrintLOG(LOG_ERR,"获取程序日志级别失败!");
    }
    else
    {
        bGetLogLevel = true;
        //PrintLOG(LOG_INF,"程序日志级别为:%d",gconf_nLogLevel);
    }
    //从配置文件中获取日志信息中的日志大小上限
    if(GetConfigValue((char*)"Log_Info",(char*)"log_max_size",&gconf_lMaxLogSize) == -1){
        PrintLOG(LOG_ERR,"获取日志大小上限失败!");
    }
    else
    {
        bGetMaxLogSize = true;
        //PrintLOG(LOG_INF,"日志大小上限为:%d",gconf_lMaxLogSize);
    }
    //从配置文件中获取日志信息中的日志是(1)否(0)清空
    if(GetConfigValue((char*)"Log_Info",(char*)"log_reset",&gconf_nLogReset) == -1){
        PrintLOG(LOG_ERR,"获取日志清空配置信息失败!");
    }
    else
    {
        bGetLogReset = true;
        //PrintLOG(LOG_INF,"日志清空配置为:%d",gconf_nLogReset);
    }

    if(g_nCountLoadConfig == 0 &&
            bGetCameraIP && bGetUserName && bGetCameraPassword && bGetCameraPtzTurn && bGetCameraPtzSpeed &&
            bGetPtzTurnLevel && bGetAccuracyFrequency && bGetMaxPlateVectorSize && bGetIntervalOfClearPlateVector && bGetPlateResultPath &&
            bGetLogPath && bGetLogLevel && bGetMaxLogSize && bGetLogReset){
        g_nCountLoadConfig ++;
        PrintLOG(LOG_INF,"全部配置信息均从配置文件获取.");
    }

    return 1;
}
//************[END]函数具体定义[END]****************

6. 配置文件作用方法

暂时是在主函数中设置循环,不断调用配置文件读取函数,使其不断生效后执行相关操作。

7. 配置文件config.ini内容

[Log_Info]
#日志路径,因为一般将结果保存在“result”文件夹中,所以要使用“result/***.log”的格式
log_path = result/testResult.log
#设置程序日志等级:1 表示打印所有(调试,一般,错误信息),0 表示打印运行(一般,错误)信息,-1 表示仅打印错误信息.
log_levle = 1
#设置程序日志大小上限(单位为bytes,0表示不设置),超过后日志清空,最大2147483647,建议不要超过104857600(100*1024*1024)
log_max_size = 104857600
#程序开始运行时,日志是(1)否(0)清空
log_reset = 1;
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容