我正在编写一个C程序,它将打开一个文件,写入它,然后读取所写的内容。我可以打开,写入和关闭文件,但我无法读取行并正确解析它们。
我已经阅读了许多其他博客和网站,但没有一个完全解决我正在尝试做的事情。我试过调整他们的一般解决方案,但我从来没有得到我想要的行为。我用fgets(),gets(),strtok(),scanf()和fscanf()运行了这段代码。我使用了strtok_r(),因为它被推荐为最佳实践。我使用gets()和scanf()作为实验,看看它们的输出是什么,而不是fgets()和fscanf()。
我想做的事:
有人可以告诉我我缺少什么,哪些功能被认为是最佳做法?
谢谢
我的代码:
#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int main(){
FILE * file;
// read data from customer.txt
char lines[30];
file = fopen("data.txt", "r");
// data.txt currently holds five lines
// 1 1 1 1 1
// 2 2 2 2 2
// 3 3 3 3 3
// 4 4 4 4 4
// 5 5 5 5 5
char *number;
char *next = lines;
int s = 0;
int t = 0;
int num;
int prams[30][30];
while(fgets(lines, 30, file)){
char *from = next;
while((number = strtok_r(from, " ", &next)) != NULL){
int i = atoi(number);
prams[t][s] = i;
printf("this is prams[%d][%d]: %d\n", t, s, prams[t][s]);
s++;
from = NULL;
}
t++;
}
fclose(file);
}// main
预期产量:
这是婴儿车[0] [0]:1 ... 这是婴儿车[4] [4]:5
实际产量:
这是婴儿车[0] [0]:1 这是婴儿车[0] [1]:1 这是婴儿车[0] [2]:1 这是婴儿车[0] [3]:1 这是婴儿车[0] [4]:1 程序结束
主要问题是:
strtok_r
,第一个参数必须不是空的只有你第一次解析一行(在编辑之前)number = strtok_r(from, " ", &next)
由strtok_r
修改,而它用于初始化下一行,所以第二行将无法正确读取并且您的执行仅为:这是婴儿车[0] [0]:11 这是婴儿车[0] [1]:12 这是婴儿车[0] [2]:13 这是婴儿车[0] [3]:14 这是婴儿车[0] [4]:15 这是婴儿车[3] [5]:0
data.txt包含:
11 12 13 14 15 21 22 23 24 25 31 32 33 34 35 41 42 43 44 45 51 52 53 54 55
(也看看索引[3][5]
,因为你错过了重置s)
补充说明:
考虑这些评论的建议是(我将数组初始化为0而不假设每行数字):
#include <stdio.h>
#include <string.h>
#define LINELENGTH 30
#define SIZE 30
int main(){
// read data from customer.txt
char lines[LINELENGTH];
FILE * file = fopen("data.txt", "r");
if (file == NULL) {
fprintf(stderr, "cannot read data.txt");
return -1;
}
// data.txt currently holds five lines
// 1 1 1 1 1
// 2 2 2 2 2
// 3 3 3 3 3
// 4 4 4 4 4
// 5 5 5 5 5
int t = 0;
int prams[SIZE][SIZE] = { 0 };
while (fgets(lines, LINELENGTH, file)) {
char * number;
char * str = lines;
int s = 0;
while ((number = strtok(str, " \n")) != NULL) {
char c;
int i;
if (sscanf(number, "%d%c", &i, &c) != 1) {
fprintf(stderr, "invalid number '%s'\n", number);
return -1;
}
prams[t][s] = i;
printf("this is prams[%d][%d]: %d\n", t, s, prams[t][s]);
str = NULL;
if (++s == SIZE)
break;
}
if (++t == SIZE)
break;
}
fclose(file);
}// main
我使用sscanf(number, "%d%c", &i, &c) != 1
轻松检测一个数字是否只读取一个数字,注意我添加了\n
是strtok的分隔符
编译和执行:
pi@raspberrypi:/tmp $ !g
gcc -pedantic -Wall -Wextra l.c
pi@raspberrypi:/tmp $ cat data.txt
11 12 13 14 15
21 22 23 24 25
31 32 33 34 35
41 42 43 44 45
51 52 53 54 55
pi@raspberrypi:/tmp $ ./a.out
this is prams[0][0]: 11
this is prams[0][1]: 12
this is prams[0][2]: 13
this is prams[0][3]: 14
this is prams[0][4]: 15
this is prams[1][0]: 21
this is prams[1][1]: 22
this is prams[1][2]: 23
this is prams[1][3]: 24
this is prams[1][4]: 25
this is prams[2][0]: 31
this is prams[2][1]: 32
this is prams[2][2]: 33
this is prams[2][3]: 34
this is prams[2][4]: 35
this is prams[3][0]: 41
this is prams[3][1]: 42
this is prams[3][2]: 43
this is prams[3][3]: 44
this is prams[3][4]: 45
this is prams[4][0]: 51
this is prams[4][1]: 52
this is prams[4][2]: 53
this is prams[4][3]: 54
this is prams[4][4]: 55
直接的主要问题是你不断告诉strtok_r()
从字符串的开头开始,所以它继续返回相同的值。您需要将strtok_r()
的第一个参数设置为NULL,以便它从中断处继续:
char *from = next;
while ((number = strtok_r(from, " ", &next)) != NULL)
{
int i = atoi(number);
prams[t][s] = i;
printf("this is prams[%d][%d]: %d\n", t, s, prams[t][s]);
s++;
from = NULL;
}
有些人赞成strtol()
而不是atoi()
;他们身边有一些正义,但可能不够重要。
另请参阅How to use sscanf()
in loops?了解如何使用sscanf()解析该行。
使用:
while (fgets(lines, 30, file))
用于外环控制; don't use feof()
除了(可能)循环终止后,以区分EOF和I / O错误。 (几年前,我检查了我的数百个C源文件,发现不到六次使用eof()
,所有都在错误检查代码而没有循环控件。你真的不需要经常使用它根本。)
如果你想解析以空格分隔的文本,那么scanf和朋友是你最好的选择。但是,如果您想要专门处理换行符而不是空格,那么您需要fgets + sscanf循环:
#define ROWS 30
#define COLS 30
#define MAXLINE 512
int prams[ROWS][COLS];
int row, col, len;
char buffer[MAXLINE], *p;
row = 0;
while (row < ROWS && fgets(buffer, MAXLINE, stdin)) {
col = 0;
p = buffer;
while (col < COLS && sscanf(p, "%d %n", &prams[row][col], &len) > 0) {
p += len;
++col; }
if (*p) {
/* extra stuff on the end of the line -- error? */ }
++row; }
注意还要检查边界以确保不超过固定大小的数组边界。