C语言实现:如何将CSV文件数字按首数字奇偶分至两个集合?
问题分析与修正方案
你的代码存在几个关键问题,导致无法按首数字奇偶性完成分类:
- 首数字判断逻辑完全错误:你用
geo[0].latitud%2==0判断的是整个数值的奇偶,还错误引用了第一个元素geo[0]而非当前元素geo[i],完全没处理首数字的提取逻辑。 - 赋值方向颠倒:
geo[i].latitud=conjunto1[i];是把集合的值覆盖到结构体里,反过来才对——应该把结构体中的数值存入集合。 - CSV字段读取错误:读取经度时用了
sscanf(temp,"%f",&geo[i].longitud);,但strtok分割后应该用strtok(NULL,coma)的结果来读取,而非原字符串temp。 - 行数统计的
feof陷阱:while(!feof(pFich))会导致多统计一行(因为最后一次fgets失败后feof才触发)。 - 内存检查位置错误:
if(geo==NULL)的判断应该放在malloc之后,而不是循环内部。 - 打印格式错误:
printf ("f",conjunto1[i]);缺少百分号,格式完全不正确。
修正后的代码
核心思路是从字符串层面提取首数字(避免浮点数取整的精度误差):跳过正负号,找到第一个数字字符,转成整数后判断奇偶,再分别存入两个集合,同时用独立计数器跟踪两个集合的元素数量。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> // 假设你的tGeolocalizacion结构体定义如下 typedef struct { float latitud; float longitud; } tGeolocalizacion; void lectura() { float conjunto_par[1000], conjunto_impar[1000]; int cnt_par = 0, cnt_impar = 0; // 两个集合的元素计数器 char temp[1000]; int cont = 0, i; char *numero; tGeolocalizacion *geo; const char *coma = ";"; // 改成const char*更规范 char archivo[] = "fichnum.csv"; FILE *pFich = fopen(archivo, "r"); if (pFich == NULL) { printf("无法打开文件,错误!\n"); exit(1); } // 修正:统计有效行数,避免feof陷阱 while (fgets(temp, 1000, pFich) != NULL) { // 跳过空行(可选) if (strlen(temp) <= 1) continue; cont++; } rewind(pFich); // 分配内存并立即检查 geo = malloc(cont * sizeof(tGeolocalizacion)); if (geo == NULL) { printf("内存分配失败,错误!\n"); fclose(pFich); // 记得关闭文件再退出 exit(1); } for (i = 0; i < cont; i++) { fgets(temp, 1000, pFich); // 跳过空行(对应统计时的处理) if (strlen(temp) <= 1) { i--; continue; } // 正确读取CSV的两个字段 numero = strtok(temp, coma); if (numero == NULL) break; // 处理格式错误的行 sscanf(numero, "%f", &geo[i].latitud); numero = strtok(NULL, coma); if (numero == NULL) break; sscanf(numero, "%f", &geo[i].longitud); // 提取首数字并判断奇偶 char *ptr = numero; // 若要按纬度首数字分类,替换成读取纬度的numero即可 // 跳过正负号 while (*ptr == '+' || *ptr == '-') { ptr++; } // 找到第一个数字字符 while (*ptr != '\0' && !isdigit((unsigned char)*ptr)) { ptr++; } if (isdigit((unsigned char)*ptr)) { int primer_digito = *ptr - '0'; // 字符转数字 if (primer_digito % 2 == 0) { conjunto_par[cnt_par++] = geo[i].latitud; // 存入偶首数集合 } else { conjunto_impar[cnt_impar++] = geo[i].latitud; // 存入奇首数集合 } } } // 打印结果 printf("首数字为偶数的集合:\n"); for (i = 0; i < cnt_par; i++) { printf("%.2f ", conjunto_par[i]); } printf("\n首数字为奇数的集合:\n"); for (i = 0; i < cnt_impar; i++) { printf("%.2f ", conjunto_impar[i]); } printf("\n文件读取完成\n"); // 释放资源 free(geo); fclose(pFich); } int main() { lectura(); return 0; }
关键修改说明
- 首数字提取:通过字符串遍历跳过正负号和非数字字符,直接取第一个数字字符转成整数,避免了浮点数取整时的精度问题(比如
123.45取整是123,但首数字是1)。 - 集合计数器:用
cnt_par和cnt_impar分别跟踪两个集合的元素数量,避免原代码中用数组索引导致的空位问题。 - 错误处理:增加了对
strtok返回NULL的判断,处理格式错误的CSV行;内存分配后立即检查;退出前关闭文件避免资源泄漏。 - 行数统计修正:用
fgets != NULL代替!feof,避免多统计一行的问题。
内容的提问来源于stack exchange,提问作者Nachonacho Nachonacho
相关产品推荐
相关产品推荐

