c++实现的ini文件读写

//
// Created by DangXS on 2022/5/28.
//

#ifndef INIRW_H
#define INIRW_H

/**
* 文件:inirw.h
* 版本:1.0
*
* 说明:ini配置文件读写
* 1、支持;和#注释符号,支持行尾注释。
* 2、支持带引号'或"成对匹配的字符串,提取时自动去引号。引号中可带其它引号或;#注释符。
* 3、支持无section或空section(名称为空)。
* 4、支持10、16、8进制数,0x开头为16进制数,0开头为8进制。
* 5、支持section、key或=号前后带空格。
* 6、支持n、r、rn或nr换行格式。
* 7、不区分section、key大小写,但写入时以新串为准,并保持其大小写。
* 8、新增数据时,若section存在则在该节最后一个有效数据后添加,否则在文件尾部添加。
* 9、支持指定key所在整行删除,即删除该键值,包括注释。
* 10、可自动跳过格式错误行,修改时仍然保留。
* 11、修改时保留原注释:包括整行注释、行尾注释(包括前面空格)。
* 12、修改时保留原空行。以上三点主要是尽量保留原格式。
*
xx.ini的格式举例:
[section1]
name='a'

[global]
fps=4
width=960
height=576
a_time=3
b_time=3
v_time=3
d=1
e_time=10
f_time=8

[section1]
region_x1=0
region_y1=0
region_w=200
region_h=200

*/

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#define SIZE_LINE 1024 //每行最大长度
#define SIZE_FILENAME 256 //文件名最大长度
#define min(x, y) (x <= y) ? x : y
typedef enum _ELineType_ {
    LINE_IDLE, //未处理行
    LINE_ERROR, //错误行
    LINE_EMPTY, //空白行或注释行
    LINE_SECTION, //节定义行
    LINE_VALUE //值定义行
} ELineType;
static char gFilename[SIZE_FILENAME];
static char *gBuffer;
static int gBuflen;

//去除串首尾空格,原串被改写
static char *StrStrip(char *s) {
    size_t size;
    char *p1, *p2;
    size = strlen(s);
    if (!size)
        return s;
    p2 = s + size - 1;
    while ((p2 >= s) && isspace(*p2))
        p2--;
    *(p2 + 1) = '\0';
    p1 = s;
    while (*p1 && isspace(*p1))
        p1++;
    if (s != p1)
        memmove(s, p1, p2 - p1 + 2);
    return s;
}

//不区分大小写比较字符串
static int StriCmp(const char *s1, const char *s2) {
    int ch1, ch2;
    do {
        ch1 = (unsigned char) *(s1++);
        if ((ch1 >= 'A') && (ch1 <= 'Z'))
            ch1 += 0x20;
        ch2 = (unsigned char) *(s2++);
        if ((ch2 >= 'A') && (ch2 <= 'Z'))
            ch2 += 0x20;
    } while (ch1 && (ch1 == ch2));
    return (ch1 - ch2);
}

//取一行
//输入:数据区(指针及长度)
//输出:行类型、有效内容串(去首尾空格)、注释首、注释尾、下一行首(行尾与下一行首间为换行符)
// 有效内容位置为[buf, rem1)
static int GetLine(char *buf, int buflen, char *content, char **rem1, char **rem2, char **nextline) {
    char *cont1, *cont2;
    int cntblank, cntCR, cntLF; //连续空格、换行符数量
    char isQuot1, isQuot2; //引号
    int i;
    char *p;
//首先断行断注释,支持如下换行符:r、n、rn、nr
    cntblank = 0;
    cntCR = cntLF = 0;
    isQuot1 = isQuot2 = 0;
    cont1 = *rem1 = 0;
    content[0] = 0;
    for (i = 0, p = buf; i < buflen; i++, p++) {
        if (*p == 0) {
            p++;
            break;
        }
//2个CR或LF,行结束
        if (cntCR == 2 || cntLF == 2) {
            p--; //回溯1
            break;
        }
//CR或LF各1个之后任意字符,行结束
        if (cntCR + cntLF >= 2) {
            break;
        }
//CR或LF之后出现其它字符,行结束
        if ((cntCR || cntLF) && *p != '\r' && *p != '\n')
            break;
        switch (*p) {
            case '\r':
                cntCR++;
                break;
            case '\n':
                cntLF++;
                break;
            case '\'':
                if (!isQuot2)
                    isQuot1 = 1 - isQuot1;
                break;
            case '"':
                if (!isQuot1)
                    isQuot2 = 1 - isQuot2;
                break;
            case ';':
            case '#':
                if (isQuot1 || isQuot2)
                    break;
                if (*rem1 == NULL)
                    *rem1 = p - cntblank;
                break;
            default:
                if (isspace((unsigned char) *p)) {
                    cntblank++;
                } else {
                    cntblank = 0;
                    if ((*rem1 == NULL) && (cont1 == NULL))
                        cont1 = p;
                }
                break;
        }
    }
    *nextline = p;
    *rem2 = p - cntCR - cntLF;
    if (*rem1 == NULL)
        *rem1 = *rem2;
    cont2 = *rem1 - cntblank;
    if (cont1 == NULL) {
        cont1 = cont2;
        return LINE_EMPTY;
    }
    i = (int) (cont2 - cont1);
    if (i >= SIZE_LINE)
        return LINE_ERROR;
//内容头尾已无空格
    memcpy(content, cont1, i);
    content[i] = 0;
    if (content[0] == '[' && content[i - 1] == ']')
        return LINE_SECTION;
    if (strchr(content, '=') != NULL)
        return LINE_VALUE;
    return LINE_ERROR;
}

//获取字符串,不带引号
static int iniGetString(const char *section, const char *key, char *value, int maxlen, const char *defvalue) {
    int ret;
    int len;
    ret = iniGetValue(section, key, value, maxlen, defvalue);
    if (!ret)
        return ret;
//去首尾空格
    len = strlen(value);
    if (value[0] == '\'' && value[len - 1] == '\'') {
        value[len - 1] = 0;
        memmove(value, value + 1, len - 1);
    } else if (value[0] == '"' && value[len - 1] == '"') {
        value[len - 1] = 0;
        memmove(value, value + 1, len - 1);
    }
    return ret;
}

//获取整数值
static int iniGetInt(const char *section, const char *key, int defvalue) {
    char valstr[64];
    if (iniGetValue(section, key, valstr, sizeof(valstr), NULL))
        return (int) strtol(valstr, NULL, 0);
    return defvalue;
}

//获取浮点数
static double iniGetDouble(const char *section, const char *key, double defvalue) {
    char valstr[64];
    if (iniGetValue(section, key, valstr, sizeof(valstr), NULL))
        return (int) atof(valstr);
    return defvalue;
}

//设置字符串:若value为NULL,则删除该key所在行,包括注释
static int iniSetString(const char *section, const char *key, const char *value) {
    FILE *file;
    char *sect1, *sect2, *cont1, *cont2, *nextsect;
    char *p;
    int len, type;
    char content[SIZE_LINE];
    char *key0, *value0;
    char *rem1, *rem2, *nextline;
    if (gBuffer == NULL) {
        return 0;
    }
    if (FindSection(section, &sect1, &sect2, &cont1, &cont2, &nextsect) == 0) {
//未找到节
//value无效则返回
        if (value == NULL)
            return 0;
//在文件尾部添加
        file = fopen(gFilename, "ab");
        if (file == NULL)
            return 0;
        fprintf(file, "n[%s]n%s = %sn", section, key, value);
        fclose(file);
        iniFileLoad(gFilename);
        return 1;
    }
//找到节,则节内查找key
    p = cont1;
    len = (int) (cont2 - cont1);
    while (len > 0) {
        type = GetLine(p, len, content, &rem1, &rem2, &nextline);
        if (LINE_VALUE == type) {
            GetKeyValue(content, &key0, &value0);
            if (StriCmp(key0, key) == 0) {
//找到key
                file = fopen(gFilename, "wb");
                if (file == NULL)
                    return 0;
                len = (int) (p - gBuffer);
                fwrite(gBuffer, 1, len, file); //写入key之前部分
                if (value == NULL) {
//value无效,删除
                    len = (int) (nextline - gBuffer); //整行连同注释一并删除
                } else {
//value有效,改写
                    fprintf(file, "%s = %s", key, value);
                    len = (int) (rem1 - gBuffer); //保留尾部原注释!
                }
                fwrite(gBuffer + len, 1, gBuflen - len, file); //写入key所在行含注释之后部分
                fclose(file);
                iniFileLoad(gFilename);
                return 1;
            }
        }
        len -= (int) (nextline - p);
        p = nextline;
    }
//未找到key
//value无效则返回
    if (value == NULL)
        return 0;
//在文件尾部添加
    file = fopen(gFilename, "wb");
    if (file == NULL)
        return 0;
    len = (int) (cont2 - gBuffer);
    fwrite(gBuffer, 1, len, file); //写入key之前部分
    fprintf(file, "%s = %sn", key, value);
    fwrite(gBuffer + len, 1, gBuflen - len, file); //写入key之后部分
    fclose(file);
    iniFileLoad(gFilename);
    return 1;
}

//设置整数值:base取值10、16、8,分别表示10、16、8进制,缺省为10进制
static int iniSetInt(const char *section, const char *key, int value, int base) {
    char valstr[64];
    switch (base) {
        case 16:
            sprintf(valstr, "0x%x", value);
            return iniSetString(section, key, valstr);
        case 8:
            sprintf(valstr, "0%o", value);
            return iniSetString(section, key, valstr);
        default: //10
            sprintf(valstr, "%d", value);
            return iniSetString(section, key, valstr);
    }
}


//取一节section
//输入:节名称
//输出:成功与否、节名称首、节名称尾、节内容首、节内容尾(含换行)、下一节首(节尾与下一节首间为空行或注释行)
static int FindSection(const char *section, char **sect1, char **sect2, char **cont1, char **cont2, char **nextsect) {
    int type;
    char content[SIZE_LINE];
    char *rem1, *rem2, *nextline;
    char *p;
    char *empty;
    int uselen = 0;
    char found = 0;
    if (gBuffer == NULL) {
        return 0;
    }
    while (gBuflen - uselen > 0) {
        p = gBuffer + uselen;
        type = GetLine(p, gBuflen - uselen, content, &rem1, &rem2, &nextline);
        uselen += (int) (nextline - p);
        if (LINE_SECTION == type) {
            if (found || section == NULL) break; //发现另一section
            content[strlen(content) - 1] = 0; //去尾部]
            StrStrip(content + 1); //去首尾空格
            if (StriCmp(content + 1, section) == 0) {
                found = 1;
                *sect1 = p;
                *sect2 = rem1;
                *cont1 = nextline;
            }
            empty = nextline;
        } else if (LINE_VALUE == type) {
            if (!found && section == NULL) {
                found = 1;
                *sect1 = p;
                *sect2 = p;
                *cont1 = p;
            }
            empty = nextline;
        }
    }
    if (!found) return 0;
    *cont2 = empty;
    *nextsect = nextline;
    return 1;
}

//从一行取键、值
//输入:内容串(将被改写)
//输出:键串、值串
static void GetKeyValue(char *content, char **key, char **value) {
    char *p;
    p = strchr(content, '=');
    *p = 0;
    StrStrip(content);
    StrStrip(p + 1);
    *key = content;
    *value = p + 1;
}

//释放ini文件所占资源
static void iniFileFree() {
    if (gBuffer != NULL) {
        free(gBuffer);
        gBuffer = 0;
        gBuflen = 0;
    }
}


//加载ini文件至内存
static int iniFileLoad(const char *filename) {
    FILE *file;
    int len;
    iniFileFree();
    if (strlen(filename) >= sizeof(gFilename))
        return 0;
    strcpy(gFilename, filename);
    file = fopen(gFilename, "rb");
    if (file == NULL)
        return 0;
    fseek(file, 0, SEEK_END);
    len = ftell(file);
    gBuffer = static_cast<char *>(malloc(len));
    if (gBuffer == NULL) {
        fclose(file);
        return 0;
    }
    fseek(file, 0, SEEK_SET);
    len = fread(gBuffer, 1, len, file);
    fclose(file);
    gBuflen = len;
    return 1;
}

//读取值原始串
static int iniGetValue(const char *section, const char *key, char *value, int maxlen, const char *defvalue) {
    int type;
    char content[SIZE_LINE];
    char *rem1, *rem2, *nextline;
    char *key0, *value0;
    char *p;
    int uselen = 0;
    char found = 0;
    int len;
    if (gBuffer == NULL || key == NULL) {
        if (value != NULL)
            value[0] = 0;
        return 0;
    }
    while (gBuflen - uselen > 0) {
        p = gBuffer + uselen;
        type = GetLine(p, gBuflen - uselen, content, &rem1, &rem2, &nextline);
        uselen += (int) (nextline - p);
        if (LINE_SECTION == type) {
            if (found || section == NULL) break; //发现另一section
            content[strlen(content) - 1] = 0; //去尾部]
            StrStrip(content + 1); //去首尾空格
            if (StriCmp(content + 1, section) == 0) {
                found = 1;
            }
        } else if (LINE_VALUE == type) {
            if (!found && section == NULL) {
                found = 1;
            }
            if (!found)
                continue;
            GetKeyValue(content, &key0, &value0);
            if (StriCmp(key0, key) == 0) {
                len = strlen(value0);
                if (len == 0) break; //空值视为无效
                if (value != NULL) {
                    len = min(len, maxlen - 1);
                    strncpy(value, value0, len);
                    value[len] = 0;
                }
                return 1;
            }
        }
    }
//未发现键值取缺省
    if (value != NULL) {
        if (defvalue != NULL) {
            len = min(strlen(defvalue), maxlen - 1);
            strncpy(value, defvalue, len);
            value[len] = 0;
        } else {
            value[0] = 0;
        }
    }
    return 0;
}

//用法举例:
static void test_demo(void) {
    char *sect;
    char *key;
    char value[256];
    char stadate[10];
    char enddate[10];
    //==================加载配置文件
    const char *file = "config.ini";
    printf("load config file %snn", file);
    if (1 != iniFileLoad(file)) {
        printf("load config file is failed.\n");
    }
    //加载IP地址和端口
    sect = "IpConfig";
    key = "IP";
    char IPADDR[100];
    iniGetString(sect, key, IPADDR, sizeof(IPADDR), "notfound!");
    printf("[%11s] %11s = %sn", sect, key, IPADDR);
    key = "PORT";
    int PORT = iniGetInt(sect, key, 5035);
    printf("[%11s] %11s = %dn", sect, key, PORT);

    iniFileFree();
}


#endif //INIRW_H

 

posted @ 2022-06-23 21:14  dangxusheng  阅读(777)  评论(0编辑  收藏  举报