一、头文件的设置
a、对外接口提供时需要提供明确的成功和失败的返回值,让人判断成功和失败,
b、需要提供明确的参数描述信息,让人知道这是输入参数还是输出参数
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <pthread.h>
#include <iconv.h>
#ifndef INI_PARSER_H
#define INI_PARSER_H
#define LEN 1000
#define INI_SUCCESS 0
#define FAIL_TO_WRITE_TO_FILE -1
#define FAIL_TO_OPEN_FILE -2
#define LINE_EXCEEDS_LENGTH -3
#define InI_FILE_IS_EMPTY -4
#define RWLOCK_INITIALIZATION_FAILED -5
#define MEMORY_ALLOCATION_FAILED -6
#define NO_SPACES_ALLOWED_FOR_KEY -7
#define KEY_CANNOT_BE_EMPTY -8
#define PARAMETER_ERROR -9
#define SECTION_NOT_FOUND -10
#define ENTRIES_NOT_FOUND -11
#define FAILED_TO_ADD -12
#define FAILED_TO_DELETE -13
#define FAILED_TO_MODIFY -14
#define FAILED_TO_QUERY -15
#define INPUT_EXCEED_LENGTH -16
#define NO_COMMENT_FOR_KRY -17
#define INI_FAILURE -19
typedef enum
{
ENTRY_TYPE_KEY_VALUE,
ENTRY_TYPE_COMMENT,
ENTRY_TYPE_EMPTY_LINE
} EntryType_t;
typedef struct
{
EntryType_t type;
char *p_key;
char *p_value;
char *content; // 用于存储注释或空行内容
} Entry_t;
typedef struct
{
char *p_name;
Entry_t *p_entries;
int entry_count;
} Section_t;
typedef struct
{
Section_t *p_sections;
int section_count;
pthread_rwlock_t lock; // 读写锁
} INIFile_t;
int init(INIFile_t **p_ini_file);
/**
* @brief 初始化INI文件结构体
*
* 该函数用于初始化INI文件结构体,包括分配内存、初始化变量和互斥锁。
*
* @param p_file 用于保存初始化后的INI文件结构体指针的指针
* @return 成功时返回INI_SUCCESS
* 分配内存失败时返回MEMORY_ALLOCATION_FAILED
* 初始化互斥锁失败时返回MUTEX_LOCK_INITIALIZATION_FAILED
*/
int destroy_ini_file(INIFile_t *p_ini_file);
/**
* @brief 销毁INI文件结构体及其资源
*
* 该函数用于销毁INI文件结构体,并释放相关资源,包括互斥锁、各个部分的名称和条目,以及INI文件结构体本身。
*
* @param p_file 要销毁的INI文件结构体指针
* @return 成功时返回INI_SUCCESS
* 文件为空时返回InI_FILE_IS_EMPTY
*/
int parse_ini_file(char *p_filename, INIFile_t **p_ini_file_ptr);
/**
* @brief 解析INI文件
*
* 该函数用于解析INI文件,读取文件中的内容并构建相应的数据结构。line最大为1000,超出返回INI_FAILURE
*
* @param p_filename 要解析的INI文件名
* @param p_ini_filename 用于保存解析后的INI文件结构体指针的指针
* @return 成功时返回INI_SUCCESS
* 文件为空时返回InI_FILE_IS_EMPTY
* 行超出1000时返回LINE_EXCEEDS_LENGTH
* 内存分配失败时返回MEMORY_ALLOCATION_FAILED
*/
int write_ini_file(char *p_filename, INIFile_t *p_ini_file);
/**
* @brief 写入INI文件
*
* 该函数用于将已解析的INI文件数据结构写入到文件中。
*
* @param p_filename 要写入的INI文件名
* @param p_ini_file 已解析的INI文件数据结构指针
* @return 成功时返回INI_SUCCESS
* 打开文件失败时返回FAIL_TO_OPEN_FILE
* key值为空时返回KEY_CANNOT_BE_EMPTY
* 写入文件失败时返回FAIL_TO_WRITE_TO_FILE。
*/
int add_entry(INIFile_t *p_ini_file, const char *p_section, const char *p_key, const char *p_value);
/**
* @brief 添加
*
* 该函数用于添加操作
*
* @param p_ini_file:指向INI文件结构的指针
* @param p_section:要添加的节名称
* @param p_key:要添加的条目的键
* @param p_value:要添加的条目的值
* @return 成功时返回INI_SUCCESS
* 参数p_ini_file、p_section、p_key、p_value为空时返回PARAMETER_ERROR
* key含有空格时返回NO_SPACES_ALLOWED_FOR_KEY
* key为#或者;时返回INI_FAILURE
* 输入行超出长度时返回INPUT_EXCEED_LENGTH
* 内存分配失败时返回MEMORY_ALLOCATION_FAILED
*/
int delete_entry(INIFile_t *p_ini_file, const char *p_section, const char *p_key);
/**
* @brief 删除
*
* 该函数用于删除操作
*
* @param p_ini_file:指向INI文件结构的指针
* @param p_section:要删除的节名称
* @param p_key:要删除的条目的键
* @param p_value:要删除的条目的值
* @return 成功时返回INI_SUCCESS
* 参数p_ini_file、p_section、p_key为空时返回PARAMETER_ERROR
* key含有空格时返回NO_SPACES_ALLOWED_FOR_KEY
* 没有找到节返回SECTION_NOT_FOUND
* 没有条目时返回ENTRIES_NOT_FOUND
*/
int modify_entry(INIFile_t *p_ni_file, const char *p_section, const char *p_key, const char *p_value);
/**
* @brief 修改
*
* 该函数用于修改操作
*
* @param p_ini_file:指向INI文件结构的指针
* @param p_section:要修改的节名称
* @param p_key:要修改的条目的键
* @param p_value:要修改的条目的新的值
* @return 成功时返回INI_SUCCESS
* 参数p_ini_file、p_section、p_key、p_value为空时返回PARAMETER_ERROR
* key含有空格时返回NO_SPACES_ALLOWED_FOR_KEY
* 没有找到节返回SECTION_NOT_FOUND
* 没有条目时返回ENTRIES_NOT_FOUND
*/
const char *query_entry(INIFile_t *p_ini_file, const char *p_section, const char *p_key, int *return_code);
/**
* @brief 查找
*
* 该函数用于查找value操作
*
* @param p_ini_file:指向INI文件结构的指针
* @param p_section:要查找的节名称
* @param p_key:要查找的条目的键
* @param error_code:存放错误码的
* @return 成功时返回查找到的value值,且返回码设为INI_SUCCESS
* 参数p_ini_file、p_section、p_key为空时返回PARAMETER_ERROR
* key含有空格时返回NO_SPACES_ALLOWED_FOR_KEY
* 没有找到节返回SECTION_NOT_FOUND
* 没有条目时返回ENTRIES_NOT_FOUND
*/
#endif // INI_PARSER_H
二、初始化结构体
// 初始化
int init(INIFile_t **p_file)
{
*p_file = malloc(sizeof(INIFile_t));
if (*p_file == NULL)
{
return MEMORY_ALLOCATION_FAILED; // 内存分配失败
}
(*p_file)->p_sections = NULL;
(*p_file)->section_count = 0;
// 初始化读写锁
if (pthread_rwlock_init(&(*p_file)->lock, NULL) != 0)
{
free(*p_file);
return RWLOCK_INITIALIZATION_FAILED; // 读写锁初始化失败
}
return INI_SUCCESS;
}
三、解析ini文件
// 解析
int parse_ini_file(char *p_filename, INIFile_t **p_ini_filename)
{
FILE *p_file = fopen(p_filename, "r");
if (p_file == NULL)
{
return InI_FILE_IS_EMPTY;
}
INIFile_t *p_ini_file = *p_ini_filename;
pthread_rwlock_wrlock(&p_ini_file->lock);
char line[LEN];
Section_t *p_current_section = NULL;
while (fgets(line, sizeof(line), p_file) != NULL)
{
if (strlen(line) >= sizeof(line) - 1)
{
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock);
return LINE_EXCEEDS_LENGTH;
}
line[strcspn(line, "\r\n")] = '\0'; // 找\r\n下标 遇到\r就会返回
char *p_trimmed_line = line;
while (isspace(*p_trimmed_line))
{
p_trimmed_line++;
}
char *p_end = p_trimmed_line + strlen(p_trimmed_line) - 1;
while (p_end > p_trimmed_line && isspace(*p_end))
{
*p_end = '\0';
p_end--;
}
if (*p_trimmed_line == '#' || *p_trimmed_line == ';' || *p_trimmed_line == '\0') // 遇到注释或者空行
{
Entry_t entry = {0};
entry.type = (*p_trimmed_line == '\0') ? ENTRY_TYPE_EMPTY_LINE : ENTRY_TYPE_COMMENT; // 判断是空行还是注释
entry.content = strdup(line);
entry.p_key = NULL;
entry.p_value = NULL;
if (!entry.content)
{
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock);
return MEMORY_ALLOCATION_FAILED;
}
if (p_current_section == NULL)
{
p_ini_file->p_sections = realloc(p_ini_file->p_sections, (p_ini_file->section_count + 1) * sizeof(Section_t));
if (!p_ini_file->p_sections)
{
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock);
return MEMORY_ALLOCATION_FAILED;
}
memset(&p_ini_file->p_sections[p_ini_file->section_count], 0, sizeof(Section_t)); // Initialize the new section
p_current_section = &p_ini_file->p_sections[p_ini_file->section_count++];
p_current_section->p_name = strdup("DEFAULT_SECTION");
if (!p_current_section->p_name)
{
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock);
return MEMORY_ALLOCATION_FAILED;
}
}
Entry_t *p_temp_entries = realloc(p_current_section->p_entries, (p_current_section->entry_count + 1) * sizeof(Entry_t));
if (!p_temp_entries)
{
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock);
return MEMORY_ALLOCATION_FAILED;
}
p_current_section->p_entries = p_temp_entries;
memset(&p_current_section->p_entries[p_current_section->entry_count], 0, sizeof(Entry_t)); // Initialize the new entry
p_current_section->p_entries[p_current_section->entry_count] = entry;
p_current_section->entry_count++;
continue;
}
if (*p_trimmed_line == '[' && *p_end == ']') // section
{
char *p_section_name = p_trimmed_line + 1;
*p_end = '\0';
Section_t *p_temp_sections = realloc(p_ini_file->p_sections, (p_ini_file->section_count + 1) * sizeof(Section_t));
if (!p_temp_sections)
{
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock);
return MEMORY_ALLOCATION_FAILED;
}
p_ini_file->p_sections = p_temp_sections;
memset(&p_ini_file->p_sections[p_ini_file->section_count], 0, sizeof(Section_t)); // Initialize the new section
p_current_section = &p_ini_file->p_sections[p_ini_file->section_count++];
p_current_section->p_name = strdup(p_section_name);
if (!p_current_section->p_name)
{
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock);
return MEMORY_ALLOCATION_FAILED;
}
continue;
}
char *p_equal = strchr(p_trimmed_line, '=');
if (p_equal == NULL)
{
continue;
}
char *p_key = p_trimmed_line;
char *p_value = p_equal + 1;
*p_equal = '\0';
if (p_current_section == NULL)
{
continue;
}
Entry_t entry = {0};
entry.type = ENTRY_TYPE_KEY_VALUE;
entry.p_key = strdup(p_key);
if (!entry.p_key)
{
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock);
return MEMORY_ALLOCATION_FAILED;
}
entry.p_value = strdup(p_value);
if (!entry.p_value)
{
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock);
return MEMORY_ALLOCATION_FAILED;
}
Entry_t *p_temp_entries = realloc(p_current_section->p_entries, (p_current_section->entry_count + 1) * sizeof(Entry_t));
if (!p_temp_entries)
{
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock);
return MEMORY_ALLOCATION_FAILED;
}
p_current_section->p_entries = p_temp_entries;
memset(&p_current_section->p_entries[p_current_section->entry_count], 0, sizeof(Entry_t)); // Initialize the new entry
p_current_section->p_entries[p_current_section->entry_count] = entry;
p_current_section->entry_count++;
}
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock);
*p_ini_filename = p_ini_file;
return INI_SUCCESS;
}
四、增、删、改、查、各个函数接口
1、增加
// 添加
int add_entry(INIFile_t *p_ini_file, const char *p_section, const char *p_key, const char *p_value)
{
if (p_ini_file == NULL || p_section == NULL || p_key == NULL || p_value == NULL)
{
return PARAMETER_ERROR; // 参数错误
}
if (strchr(p_key, ' ') != NULL)
{
printf("No spaces allowed for key \n");
return NO_SPACES_ALLOWED_FOR_KEY;
}
if (strchr(p_key, '#') != NULL || strchr(p_key, ';') != NULL)
{
printf("No (# or ;) allowed for key \n");
return NO_COMMENT_FOR_KRY;
}
if (strlen(p_section) > LEN || strlen(p_key) + strlen(p_value) > LEN - 1)
{
printf("Input exceeds length\n");
return INPUT_EXCEED_LENGTH;
}
pthread_rwlock_wrlock(&p_ini_file->lock);
// 查找或创建节
Section_t *p_sec = NULL;
int i;
for (i = 0; i < p_ini_file->section_count; i++)
{
if (p_ini_file->p_sections[i].p_name && strcmp(p_ini_file->p_sections[i].p_name, p_section) == 0)
{
p_sec = &p_ini_file->p_sections[i];
break;
}
}
if (p_sec == NULL)
{
// 创建新节
p_ini_file->p_sections = realloc(p_ini_file->p_sections, (p_ini_file->section_count + 1) * sizeof(Section_t));
if (p_ini_file->p_sections == NULL)
{
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return MEMORY_ALLOCATION_FAILED;
}
p_sec = &p_ini_file->p_sections[p_ini_file->section_count++];
p_sec->p_name = strdup(p_section);
if (p_sec->p_name == NULL)
{
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return MEMORY_ALLOCATION_FAILED;
}
p_sec->p_entries = NULL;
p_sec->entry_count = 0;
}
for (i = 0; i < p_sec->entry_count; i++)
{
if (p_sec->p_entries[i].p_key && strcmp(p_sec->p_entries[i].p_key, p_key) == 0)
{
char *new_value = strdup(p_value);
if (!new_value)
{
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return MEMORY_ALLOCATION_FAILED;
}
free(p_sec->p_entries[i].p_value); // 释放旧的value的内存
p_sec->p_entries[i].p_value = new_value;
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return INI_SUCCESS; // key已存在并已被更新,直接返回
}
}
// 如果key在这个section中不存在,就添加新的key-value对
p_sec->p_entries = realloc(p_sec->p_entries, (p_sec->entry_count + 1) * sizeof(Entry_t));
if (p_sec->p_entries == NULL)
{
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return MEMORY_ALLOCATION_FAILED;
}
Entry_t *p_entry = &p_sec->p_entries[p_sec->entry_count++];
p_entry->type = ENTRY_TYPE_KEY_VALUE;
p_entry->p_key = strdup(p_key);
p_entry->p_value = strdup(p_value);
p_entry->content = NULL;
if (p_entry->p_key == NULL)
{
if (p_entry->p_key)
{
free(p_entry->p_key);
p_entry->p_key = NULL;
}
if (p_entry->p_value)
{
free(p_entry->p_value);
p_entry->p_value = NULL;
}
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return KEY_CANNOT_BE_EMPTY;
}
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return INI_SUCCESS;
}
2、删除
// 删除
int delete_entry(INIFile_t *p_ini_file, const char *p_section, const char *p_key)
{
if (p_ini_file == NULL || p_section == NULL || p_key == NULL)
{
return PARAMETER_ERROR; // 参数错误
}
if (strchr(p_key, ' ') != NULL)
{
return NO_SPACES_ALLOWED_FOR_KEY;
}
pthread_rwlock_wrlock(&p_ini_file->lock);
// 查找节
Section_t *p_sec = NULL;
int i, j;
for (i = 0; i < p_ini_file->section_count; i++)
{
if (p_ini_file->p_sections[i].p_name && strcmp(p_ini_file->p_sections[i].p_name, p_section) == 0)
{
p_sec = &p_ini_file->p_sections[i];
break;
}
}
if (p_sec == NULL)
{
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return SECTION_NOT_FOUND; // 没有找到节
}
// 在找到的节中查找条目
for (i = 0; i < p_sec->entry_count; i++)
{
if (p_sec->p_entries[i].p_key && strcmp(p_sec->p_entries[i].p_key, p_key) == 0)
{
// 找到条目,释放条目的key和value
free(p_sec->p_entries[i].p_key);
free(p_sec->p_entries[i].p_value);
// 将后面的条目向前移动,覆盖当前条目
for (j = i; j < p_sec->entry_count - 1; j++)
{
p_sec->p_entries[j] = p_sec->p_entries[j + 1];
}
// 缩小条目数组的大小
p_sec->p_entries = realloc(p_sec->p_entries, (p_sec->entry_count - 1) * sizeof(Entry_t));
p_sec->entry_count--;
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return INI_SUCCESS; // 成功
}
}
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return ENTRIES_NOT_FOUND; // 没有找到条目
}
3、修改
// 修改
int modify_entry(INIFile_t *p_ini_file, const char *p_section, const char *p_key, const char *p_value)
{
if (p_ini_file == NULL || p_section == NULL || p_key == NULL || p_value == NULL)
{
return PARAMETER_ERROR; // 参数错误
}
if (strchr(p_key, ' ') != NULL)
{
return NO_SPACES_ALLOWED_FOR_KEY;
}
pthread_rwlock_wrlock(&p_ini_file->lock);
// 查找节
Section_t *p_sec = NULL;
int i;
for (i = 0; i < p_ini_file->section_count; i++)
{
if (p_ini_file->p_sections[i].p_name && strcmp(p_ini_file->p_sections[i].p_name, p_section) == 0)
{
p_sec = &p_ini_file->p_sections[i];
break;
}
}
if (p_sec == NULL)
{
pthread_rwlock_unlock(&p_ini_file->lock);
return SECTION_NOT_FOUND; // 没有找到节
}
// 在找到的节中查找条目
for (i = 0; i < p_sec->entry_count; i++)
{
if (p_sec->p_entries[i].p_key && strcmp(p_sec->p_entries[i].p_key, p_key) == 0)
{
// 找到条目,释放旧的value
free(p_sec->p_entries[i].p_value);
// 设置新的value
p_sec->p_entries[i].p_value = strdup(p_value);
if (p_sec->p_entries[i].p_value == NULL)
{
pthread_rwlock_unlock(&p_ini_file->lock);
return MEMORY_ALLOCATION_FAILED;
}
pthread_rwlock_unlock(&p_ini_file->lock);
return INI_SUCCESS; // 成功
}
}
pthread_rwlock_unlock(&p_ini_file->lock);
return ENTRIES_NOT_FOUND; // 没有找到条目
}
4、查找
// 查找
const char *query_entry(INIFile_t *p_ini_file, const char *p_section, const char *p_key, int *return_code)
{
if (p_ini_file == NULL || p_section == NULL || p_key == NULL)
{
*return_code = PARAMETER_ERROR;
return NULL;
}
if (strchr(p_key, ' ') != NULL)
{
*return_code = NO_SPACES_ALLOWED_FOR_KEY;
return NULL;
}
pthread_rwlock_rdlock(&p_ini_file->lock);
// 查找节
Section_t *p_sec = NULL;
int i;
for (i = 0; i < p_ini_file->section_count; i++)
{
if (p_ini_file->p_sections[i].p_name && strcmp(p_ini_file->p_sections[i].p_name, p_section) == 0)
{
p_sec = &p_ini_file->p_sections[i];
break;
}
}
if (p_sec == NULL)
{
pthread_rwlock_unlock(&p_ini_file->lock);
*return_code = SECTION_NOT_FOUND;
return NULL;
}
// 在找到的节中查找条目
for (i = 0; i < p_sec->entry_count; i++)
{
if (p_sec->p_entries[i].p_key && strcmp(p_sec->p_entries[i].p_key, p_key) == 0)
{
// 找到条目,返回value
pthread_rwlock_unlock(&p_ini_file->lock);
*return_code = INI_SUCCESS;
return p_sec->p_entries[i].p_value; // 成功
}
}
pthread_rwlock_unlock(&p_ini_file->lock);
*return_code = ENTRIES_NOT_FOUND;
return NULL;
}
五、写文件
// 写文件
int write_ini_file(char *p_filename, INIFile_t *p_ini_file)
{
pthread_rwlock_wrlock(&p_ini_file->lock);
FILE *p_file = fopen(p_filename, "w");
if (p_file == NULL)
{
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return FAIL_TO_OPEN_FILE;
}
int i, j;
for (i = 0; i < p_ini_file->section_count; i++)
{
Section_t *p_section = &p_ini_file->p_sections[i];
if (strcmp(p_section->p_name, "DEFAULT_SECTION") != 0)
{
fprintf(p_file, "[%s]\n", p_section->p_name);
}
for (j = 0; j < p_section->entry_count; j++)
{
Entry_t *p_entry = &p_section->p_entries[j];
switch (p_entry->type)
{
case ENTRY_TYPE_KEY_VALUE:
if (p_entry->p_key == NULL)
{
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return KEY_CANNOT_BE_EMPTY;
}
char *format_str = "%s=%s\n";
if (fprintf(p_file, format_str, p_entry->p_key, p_entry->p_value) < 0)
{
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return FAIL_TO_WRITE_TO_FILE;
}
break;
case ENTRY_TYPE_COMMENT:
if (fprintf(p_file, "%s\n", p_entry->content) < 0)
{
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return FAIL_TO_WRITE_TO_FILE;
}
break;
case ENTRY_TYPE_EMPTY_LINE:
if (fprintf(p_file, "\n") < 0)
{
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return FAIL_TO_WRITE_TO_FILE;
}
break;
}
}
}
fclose(p_file);
pthread_rwlock_unlock(&p_ini_file->lock); // 解锁
return INI_SUCCESS;
}
别的程序可以直接引用这个静态库,用这个库的各个函数进行操作