3

C语言读写ini配置文件 - 迷途小书童的Note迷途小书童的Note

 1 year ago
source link: https://xugaoxiang.com/2023/06/15/read-write-ini-config-file-in-c-language/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
  • windows 10 64bit
  • Clion 2023.1

ini简介

ini 文件格式是一种用于保存配置信息的简单文本格式。它通常由多个节(section)组成,每个节包含多个键值对(key-value pair)。

下面是 ini 文件的基本语法规则

  • 一个ini文件由多个节组成,每个节用方括号([])括起来
  • 节后面可以跟一个或多个键值对,每个键值对占一行,格式为key=value
  • 键值对中,键(key)和值(value)之间用等号(=)分隔,键和值之间可以有空格。键和值都是字符串,可以包含任何字符,但是如果键或值中包含等号(=)或分号(;)这样的特殊字符,需要进行转义
  • 注释以井号(#)或分号(;)开头,注释可以出现在任何位置,包括行首、行尾或键值对之间

下面是一个 ini 示例文件,后面代码也会去解析它

# 服务器配置
[server]
ip = 192.168.1.100
port = 8080

;数据库配置
[database]
host = localhost
port = 3306
username = username
password = password

在这个示例文件中,有两个节:serverdatabaseserver 包含2个键值对:ipport,而 database 包含4个键值对,hostportusernamepassword。最后,还有2行注释,一行以 # 开头,另一行以 ; 开头。

这里使用 Clion 集成开发环境,编译器使用自带的 MinGW,首先创建一个 C 可执行项目,命名为 Demo

创建完成后,添加一个头文件 ini_parser.h,内容如下

#ifndef DEMO_INI_PARSER_H
#define DEMO_INI_PARSER_H

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

// 获取key对应的值
int GetIniKeyString(char *section,char *key,char *filename,char *buf);

// 修改key对应的值
int PutIniKeyString(char *section,char *key,char *val,char *filename);

#endif //DEMO_INI_PARSER_H

接着新建一个 C 源码文件 ini_parser.c,内容如下

#include <stdio.h>
#include <string.h>
#include "errno.h"

/*
* 参数:
* section:  配置文件中的节sectin
* key:      配置项的标识
* filename: ini配置文件路径
*
* 返回值:    找到需要的值返回结果0,否则返回-1
*/
int GetIniKeyString(char *section, char *key, char *filename, char *buf)
{
    FILE *fp;

    // 用来标记是否找到section
    int flag = 0;
    char sSection[64], *wTmp;
    char sLine[1024];

    // 节section字符串
    sprintf(sSection, "[%s]", section);

    if (NULL == (fp = fopen(filename, "r")))
    {
        printf("open %s failed.\n", filename);
        return -1;
    }

    // 读取ini中的每一行
    while (NULL != fgets(sLine, 1024, fp))
    {
        // 处理ini文件中的注释行
        if ('#' == sLine[0])
            continue;

        if (';' == sLine[0])
            continue;

        // 定位=的位置
        wTmp = strchr(sLine, '=');
        if ((NULL != wTmp) && (1 == flag))
        {
            if (0 == strncmp(key, sLine, strlen(key)))
            {
                sLine[strlen(sLine) - 1] = '\0';

                while (*(wTmp + 1) == ' ')
                {
                    wTmp++;
                }

                // 获取key对应的value
                strcpy(buf, wTmp + 1);

                fclose(fp);
                return 0;
            }
        }
        else
        {
            if (0 == strncmp(sSection, sLine, strlen(sSection)))
            {
                // 不存在键值对的情况下,标记flag
                flag = 1;
            }
        }
    }

    fclose(fp);
    return -1;
}

/*
* 参数:
* section:  配置文件中的节sectin
* key:      配置项的标识
* val:      配置项标识对应的值
* filename: ini配置文件路径
*
* 返回值:    成功返回结果0,否则返回-1
*/
int PutIniKeyString(char *section, char *key, char *val, char *filename)
{
    FILE *fpr, *fpw;
    int flag = 0;
    int ret;
    char sLine[1024], sSection[32], *wTmp;

    sprintf(sSection, "[%s]", section);

    if (NULL == (fpr = fopen(filename, "r")))
        return -1;

    // 临时文件名
    sprintf(sLine, "%s.tmp", filename);

    fpw = fopen(sLine, "w");
    if (NULL == fpw)
        return -1;

    while (NULL != fgets(sLine, 1024, fpr))
    {
        if (2 != flag)
        {
            wTmp = strchr(sLine, '=');
            if ((NULL != wTmp) && (1 == flag))
            {
                if (0 == strncmp(key, sLine, strlen(key)))
                {
                    // 找到对应的key
                    flag = 2;
                    sprintf(wTmp + 1, " %s\n", val);
                }
            }
            else
            {
                if (0 == strncmp(sSection, sLine, strlen(sSection)))
                {
                    // 找到section的位置
                    flag = 1;
                }
            }
        }

        // 写入临时文件
        fputs(sLine, fpw);
    }

    fclose(fpr);
    fclose(fpw);

    sprintf(sLine, "%s.tmp", filename);

    // rename函数在windows上和linux上表现有差异,看文章中的备注
    ret = rename(sLine, filename);
    if (ret != 0)
    {
        if (errno == EEXIST)
        {
            // 如果目标文件已经存在,需要先删除,再重命名
            if (remove(filename) == 0)
            {
                if (rename(sLine, filename) == 0)
                {
                    // printf("File %s has been renamed to %s\n", sLine, filename);
                    return 0;
                }
            }
        }
    }

    return ret;
}

最后编辑工程入口文件 main.c

#include <stdio.h>
#include "ini_parser.h"

int main(int argc, char const *argv[]) {
    char buff[128];
    int ret;

    ret = GetIniKeyString("server", "ip", "config.ini", buff);
    printf("get ret:%d,value: %s\n", ret, buff);

    memset(buff, 0, sizeof(buff));
    ret = GetIniKeyString("database", "db", "config.ini", buff);
    printf("get ret:%d, value: %s\n", ret, buff);

    ret = PutIniKeyString("server", "port", "80", "config.ini");
    printf("put ret:%d\n", ret);

    memset(buff, 0, sizeof(buff));
    ret = GetIniKeyString("server", "port", "config.ini", buff);
    printf("get ret:%d, value: %s\n", ret, buff);
    return 0;
}

使用 IDE 的好处就是,在添加各个文件时,它会帮你修改 CMakeLists.txt,不需要自己手动去添加

cmake_minimum_required(VERSION 3.25)
project(Demo C)

set(CMAKE_C_STANDARD 99)

add_executable(Demo main.c ini_parser.h ini_parser.c)

然后,就可以编译整个工程了,选中 IDE 顶部菜单栏中的 Build –> Build Project,完成后,会在目录 cmake-build-debug 下生成可执行文件 Demo.exe,在运行之前,还需要将配置 config.ini(也就是上文提到的ini示例文件) 也放到 cmake-build-debug

最后,按下快捷键 Shift+F10 来运行一下

ini in c lanuage

rename 方法在 windowslinux 上的表现不一样,如果目标文件存在,linux 可以直接覆盖。但是,在 windows 上,返回值就是-1,errno 提示 File Exists


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK