C语言解析ASCII文件键值对:sscanf格式串问题咨询
问题背景
需要编写C函数读取ASCII文件中的键值对,文件规则如下:
- 忽略以
[、!、$开头的行 =前后可能存在0个或多个空格- 值可以是字符串或数字
- 键值对后的
$及后续内容需忽略
现有代码使用sscanf无法匹配无空格的键值对(如param_1=123),输出缺失该部分内容。
文件内容
[SECTION 1 HEADER - ignore this line] VAR1 = 'xyz' VAR2 = 3.0 VARIAB3 = 'blabla' ! COMMENT: ignore this line VAR7 = 'ABC123' $---------another line to ignore----------------- [SECTION 2 HEADER - ignore me!] varname8 = 'abcd' $---------yet another line to ignore----------- [SECTION HEADER - ignore me] VARIABLE10 = 4.05101e+05 $ignore from the dollar sign to eol VARIABLE13 = 7e-06 $ignore from the dollar sign to eol param_1=123 param_2=321
原有代码
fh = fopen(filename, "r"); if(NULL == fh) { perror(filename); } else { printf("File opened succesfully.\n"); while(fgets(buffer, 100, fh) != NULL) { { int offset; int res = sscanf(buffer, "%s = %s%n", key, value, &offset); if(res==2) { printf("Found: %s = %s\n", key, value); } } } }
原有输出
Filename is: sample.tir File opened succesfully. Found: VAR1 = 'xyz' Found: VAR2 = 3.0 Found: VARIAB3 = 'blabla' Found: varname8 = 'abcd' Found: VARIABLE10 = 4.05101e+05 Found: VARIABLE13 = 7e-06 File closed.
问题原因
原sscanf格式串"%s = %s%n"强制要求=前后必须有空格:
%s会读取到第一个空格为止,对于param_1=123,%s会把整个param_1=123当作键读取,后续找不到匹配的=,导致返回值不等于2,无法触发输出逻辑。
修正方案
1. 优化sscanf格式串,支持=前后无空格
使用灵活的格式匹配规则,适配=前后有无空格的场景:
int res = sscanf(buffer, "%[^=]%*[ =]%[^$]", key, value);
%[^=]:匹配所有非=的字符,读取键(会包含键前的空格,后续需修剪)%*[ =]:跳过任意数量的空格和=符号,*表示只匹配不存储%[^$]:匹配所有非$的字符,读取值(会包含值后的空格,后续需修剪)
2. 先过滤无效行
在处理每行前直接跳过规则要求忽略的行,避免无效匹配:
// 跳过空白行和开头为[、!、$的行 if (buffer[0] == '\0' || buffer[0] == '[' || buffer[0] == '!' || buffer[0] == '$') { continue; }
3. 修剪键和值的前后空白
因为格式串会读取到键前、值后的空格,需要手动修剪:
// 实现空白修剪函数 char* trim_whitespace(char *str) { // 去掉开头空格 while(isspace((unsigned char)*str)) str++; if(*str == 0) return str; // 去掉结尾空格 char *end = str + strlen(str) - 1; while(end > str && isspace((unsigned char)*end)) end--; // 添加字符串结束符 *(end + 1) = '\0'; return str; }
完整修正代码
#include <stdio.h> #include <string.h> #include <ctype.h> char* trim_whitespace(char *str) { while(isspace((unsigned char)*str)) str++; if(*str == 0) return str; char *end = str + strlen(str) - 1; while(end > str && isspace((unsigned char)*end)) end--; *(end + 1) = '\0'; return str; } int main(int argc, char *argv[]) { if(argc < 2) { printf("Usage: %s <filename>\n", argv[0]); return 1; } const char *filename = argv[1]; FILE *fh = fopen(filename, "r"); if(NULL == fh) { perror(filename); return 1; } printf("File opened successfully.\n"); char buffer[100]; char key[100], value[100]; while(fgets(buffer, sizeof(buffer), fh) != NULL) { // 跳过无效行 if (buffer[0] == '\0' || buffer[0] == '[' || buffer[0] == '!' || buffer[0] == '$') { continue; } // 匹配键值对,支持=前后无空格,忽略$后的内容 int res = sscanf(buffer, "%[^=]%*[ =]%[^$]", key, value); if(res == 2) { trim_whitespace(key); trim_whitespace(value); printf("Found: %s = %s\n", key, value); } } fclose(fh); printf("File closed.\n"); return 0; }
测试结果
运行修正后的代码,会输出所有符合规则的键值对:
File opened successfully. Found: VAR1 = 'xyz' Found: VAR2 = 3.0 Found: VARIAB3 = 'blabla' Found: varname8 = 'abcd' Found: VARIABLE10 = 4.05101e+05 Found: VARIABLE13 = 7e-06 Found: param_1 = 123 Found: param_2 = 321 File closed.
内容的提问来源于stack exchange,提问作者picchiolu
相关产品推荐
相关产品推荐

