sscanf读取格式相同的行时行为不一致,部分行丢失最后字符
咱们一步步拆解你的问题,你怀疑是未定义行为导致sscanf读取部分行失败,先看你提供的代码,首先有个明显的小问题——漏了<stdio.h>头文件!printf和sscanf都是标准IO函数,必须包含这个头文件,否则编译器会做隐式声明,这本身就是未定义行为的潜在来源,先补上这个:
#include <stdlib.h> #include <stdio.h> // 必须添加这个头文件 void printLine(char* line) { char lat, lon; unsigned int year, month, day; float temp, uncertainty, latitude, longitude; char country[100], city[100]; int result; // 格式字符串:匹配日期、温度、不确定度、城市、国家、纬度+方向、经度+方向 result = sscanf(line, "%u-%u-%u,%f,%f,%[^,],%[^,],%f%c,%f%c", &year, &month,&day, &temp, &uncertainty, city, country, &latitude, &lat, &longitude, &lon); printf("%u-%u-%u,%f,%f,%s,%s,%f%c,%f%c\n", year, month, day, temp, uncertainty, city, country, latitude, lat, longitude, lon); printf("sscanf read %i variables\n", result); } int main() { char line[] = "2013-08-01,19.005,3.621,Addis Abeba,Ethiopia,8.84N,38.11E"; char line2[] = "1816-03-01,27.426,1.793,Bangkok,Thailand,13.66N,99.91E"; char line3[] = "1743-11-01,3.264,1.665,New York,United States,40.99N,74.56W"; printLine(line); printLine(line2); printLine(line3); }
接下来针对你遇到的部分行读取失败问题,给你分步排查思路:
1. 先靠sscanf返回值定位问题
你已经打印了sscanf的返回值,这是最关键的排查线索:
- 如果返回值是
11:说明所有变量都读取成功,那打印时最后一个字符异常,大概率是变量被后续操作篡改了(比如缓冲区溢出)。 - 如果返回值小于
11:比如返回9,说明读到经度前就失败了;返回10说明最后一个方向字符(W/E)没读到。根据这个数字,直接对应格式字符串的位置,就能快速锁定哪一段输入匹配出了问题。
2. 排查未定义行为的常见诱因
(1)缓冲区溢出(最容易忽略的点)
你的city和country是char[100],但%[^,]会读取到逗号前的所有字符,包括空格。如果输入的城市/国家名称长度超过99(要留1个字节存字符串终止符\0),sscanf会直接超出缓冲区写入,覆盖后面的lat、longitude等变量,这会直接触发未定义行为——可能导致后续读取失败,或者打印出乱码。
解决方法:要么把数组改大(比如char country[200], city[200];),要么在格式符里限制读取长度,比如%99[^,](最多读99个字符,留位置给\0),修改后的格式字符串部分:
result = sscanf(line, "%u-%u-%u,%f,%f,%99[^,],%99[^,],%f%c,%f%c", ...);
(2)头文件缺失导致的隐式声明
前面提到的<stdio.h>缺失,C标准里调用未声明的函数会触发隐式声明(默认返回int),虽然sscanf和printf的返回值确实是int,但编译器可能会因为隐式声明做错误优化,或者在某些平台上导致行为异常,必须补上。
(3)格式符与变量类型不匹配
逐个核对格式符和变量:
%u对应unsigned int*:你的year、month、day都是unsigned int,没问题。%f对应float*:temp、uncertainty、latitude、longitude都是float,正确。%[^,]对应char*:city、country是字符数组,正确(记得限制长度)。%c对应char*:lat、lon是char,没问题。
这部分你的代码是对的,但如果不小心写错(比如把%u写成%d),会直接导致未定义行为,所以要仔细核对。
3. 检查输入与格式字符串的精确匹配
仔细看读取失败的输入行,确认是否和格式字符串完全匹配:
- 日期部分是不是严格的
数字-数字-数字?有没有多余的空格? - 纬度/经度部分是不是
数字+方向(比如8.84N),中间没有空格或其他字符? - 所有字段之间的逗号是不是都存在,没有缺失或多余?
比如,如果某行的经度是74.56 W(数字和方向之间有空格),那%f会读取74.56,%c会读取空格而不是W,此时格式字符串里的逗号会和输入的W不匹配,导致最后一个%f%c读取失败,返回值变成9。
4. 调试时打印中间变量
如果返回值还是不对,可以在sscanf之后逐个打印变量,看哪个值异常:
printf("日期:%u-%u-%u\n", year, month, day); printf("温度:%f,不确定度:%f\n", temp, uncertainty); printf("城市:%s,国家:%s\n", city, country); printf("纬度:%f,方向:%c\n", latitude, lat); printf("经度:%f,方向:%c\n", longitude, lon);
这样能直接看到哪一步的变量不符合预期,快速定位问题。
内容的提问来源于stack exchange,提问作者chilliefiber

