一只简单的网络爬虫(基于linux C/C++)————配置文件设计及读取

一般来说linux下比较大型的程序都是以配置文件作为参数介质传递的,该爬虫也采用配置文件的方式来获取参数,配置文件格式大致如下:

max_job_num=1
#seeds=https://www.baidu.com
#seeds=http://bbs.scut.edu.cn/classic/
logfile=spiderq.log

以“=”作为分割符,左边为key,右边为valve,主要包含的参数内容为:最大的任务数,种子,日志文件,日志等级,深度,模块路径,支持的文件类型,等,以“#”开头的就作为注释。
配置文件结构体如下:

Config * initconfig()
{
    Config *conf = (Config *)malloc(sizeof(Config));

    conf->max_job_num = 10;
    conf->seeds = NULL;
    conf->include_prefixes = NULL;
    conf->exclude_prefixes = NULL;
    conf->logfile = NULL;
    conf->log_level = 0;
    conf->max_depth = INF;
    conf->make_hostdir = 0;
    conf->module_path = NULL;
    conf->stat_interval = 0;
    //conf->modules

    return conf;
}

是一个全局的结构体,若以类的方式实现则可以设计成单例类,里面的参数主要保存从配置文件中读取到的东西。加载配置文件后,配置文件的参数设置被保存到了Config结构体的,供后面的函数使用
加载配置文件的函数如下:

void loadconfig(Config *conf)
{

    FILE *fp = NULL;
    char buf[MAX_CONF_LEN+1];
    int argc = 0;
    char **argv = NULL;
    int linenum = 0;
    char *line = NULL;
    const char *err = NULL;

    if ((fp = fopen(CONF_FILE, "r")) == NULL) //打开配置文件
    {
        SPIDER_LOG(SPIDER_LEVEL_ERROR, "Can't load conf_file %s", CONF_FILE);   
    } 

    while (fgets(buf, MAX_CONF_LEN+1, fp) != NULL)//获取一行到buf
    {
        linenum++;
        line = strim(buf);//去除空格,在qstring中
        //过滤#
        if (line[0] == '#' || line[0] == '\0') continue;
        //count: 分割后的字符串长度
        //limit: 分割多少次
        argv = strsplit(line, '=', &argc, 1);
        if (argc == 2) {
            if (strcasecmp(argv[0], "max_job_num") == 0) {
                conf->max_job_num = atoi(argv[1]);
            } 
//extern char *strdup(char *s);
//功 能: 将串拷贝到新建的位置处
//strdup()在内部调用了malloc()为变量分配内存,
//不需要使用返回的字符串时,需要用free()释放相应的内存空间,
//否则会造成内存泄漏
            //strcasecmp(忽略大小写比较字符串)
            else if (strcasecmp(argv[0], "logfile") == 0) {
                conf->logfile = strdup(argv[1]);
            } else if (strcasecmp(argv[0], "include_prefixes") == 0) {
                conf->include_prefixes = strdup(argv[1]);
            } else if (strcasecmp(argv[0], "exclude_prefixes") == 0) {
                conf->exclude_prefixes = strdup(argv[1]);
            } else if (strcasecmp(argv[0], "seeds") == 0) {
                conf->seeds = strdup(argv[1]);
            } else if (strcasecmp(argv[0], "module_path") == 0) {
                conf->module_path = strdup(argv[1]);
            } else if (strcasecmp(argv[0], "load_module") == 0) {
                conf->modules.push_back(strdup(argv[1]));
            } else if (strcasecmp(argv[0], "log_level") == 0) {
                conf->log_level = atoi(argv[1]);
            } else if (strcasecmp(argv[0], "max_depth") == 0) {
                conf->max_depth = atoi(argv[1]);
            } else if (strcasecmp(argv[0], "stat_interval") == 0) {
                conf->stat_interval = atoi(argv[1]);
            } else if (strcasecmp(argv[0], "make_hostdir") == 0) {
                conf->make_hostdir = yesnotoi(argv[1]);
            } else if (strcasecmp(argv[0], "accept_types") == 0) {
                conf->accept_types.push_back(strdup(argv[1]));
            } else {
                err = "Unknown directive"; goto conferr;
            }
        } else {
            err = "directive must be 'key=value'"; goto conferr;
        }

    }

    return;

conferr:
    SPIDER_LOG(SPIDER_LEVEL_ERROR, "Bad directive in %s[line:%d] %s", CONF_FILE, linenum, err); 
}

下面介绍几个函数:

(1)fgets函数是用来获取行的,该函数可读取文件中的一行,并且会包含换行符
(2)strim函数用于去除空格,其实现如下:参数是行指针

//去除空格
char * strim(char *str)
{
    char *end, *sp, *ep;
    size_t len;

    sp = str;
    end = ep = str+strlen(str)-1;
    //从行首开始
    while(sp <= end && isspace(*sp)) sp++;
    //从行尾开始
    while(ep >= sp && isspace(*ep)) ep--;
    //计算长度
    len = (ep < sp) ? 0 : (ep-sp)+1;
    sp[len] = '\0';
    return sp;
}

(3)strsplit函数用于分割字符串,实现方法如下:

//切割字符串,函数内申请,函数外释放
//count: 分割后的字符串长度
//limit: 分割多少次
char ** strsplit(char *line, char delimeter, int *count, int limit)
{
    char *ptr = NULL, *str = line;
    char **vector = NULL;

    *count = 0;
    //strchr函数原型:extern char *strchr(const char *s,char c);查找字符串s中首次出现字符c的位置。
    while((ptr = strchr(str, delimeter))) 
    {
        *ptr = '\0';
        vector = (char **)realloc(vector,((*count)+1)*sizeof(char *));
        vector[*count] = strim(str);//等号前面部分去除空格后放入vector
        str = ptr+1;//此时str指向等号后面部分(会继续查找后面的等号)
        (*count)++; 
        if (--limit == 0) break;
    }
    if (*str != '\0') 
    {
        vector = (char **)realloc(vector,((*count)+1)*sizeof(char *));
        vector[*count] = strim(str);//分割空格
        (*count)++;
    }
    return vector;
}

(4)strcasecmp函数是忽略大小写比较字符串,该函数用来比较配置文件的key值
定义函数 int strcasecmp (const char *s1, const char *s2);
函数说明 strcasecmp()用来比较参数s1和s2字符串,比较时会自动忽略大小写的差异。
返回值 若参数s1和s2字符串相等则返回0。s1大于s2则返回大于0 的值,s1 小于s2 则返回小于0的值。
(5)strdup函数
extern char *strdup(char *s);
功 能: 将串拷贝到新建的位置处,strdup()在内部调用了malloc()为变量分配内存,不需要使用返回的字符串时,需要用free()释放相应的内存空间,否则会造成内存泄漏

posted @ 2015-09-22 11:02  sigma0  阅读(330)  评论(0编辑  收藏  举报