如何正确获取一行并用C解析它

问题描述 投票:0回答:3

我正在编写一个C程序,它将打开一个文件,写入它,然后读取所写的内容。我可以打开,写入和关闭文件,但我无法读取行并正确解析它们。

我已经阅读了许多其他博客和网站,但没有一个完全解决我正在尝试做的事情。我试过调整他们的一般解决方案,但我从来没有得到我想要的行为。我用fgets(),gets(),strtok(),scanf()和fscanf()运行了这段代码。我使用了strtok_r(),因为它被推荐为最佳实践。我使用gets()和scanf()作为实验,看看它们的输出是什么,而不是fgets()和fscanf()。

我想做的事:

  1. 得到第一行//第一行是一串空格分隔的整数“1 2 3 4 5”
  2. 解析此行,将每个char数转换为整数
  3. 将其存储到数组中。
  4. 得到下一行并重复直到EOF

有人可以告诉我我缺少什么,哪些功能被认为是最佳做法?

谢谢

我的代码:

#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 程序结束

c parsing io fgets strtok
3个回答
1
投票

主要问题是:

  • 你永远不会将s重置为0,所以列总是增加而不是从0到4(如果每行5个数字),所以你不要从第二行写入数组中的预期条目,你有风险写出具有未定义行为的数组(如分段错误)
  • 检查你没有读过太多的列和行(你的代码中有30个),否则你可以用不确定的行为写出数组(比如分段错误)
  • 你使用错误的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)

补充说明:

  • 检查fopen成功
  • 初始化婴儿车或记住第一行有多少列并检查下一行总是相同的列数,当然还要记住多少行,否则你不知道以后读取的数字在哪里排列
  • atoi不表示你是否读过一个数字

考虑这些评论的建议是(我将数组初始化为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

1
投票

直接的主要问题是你不断告诉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(),所有都在错误检查代码而没有循环控件。你真的不需要经常使用它根本。)


0
投票

如果你想解析以空格分隔的文本,那么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; }

注意还要检查边界以确保不超过固定大小的数组边界。

© www.soinside.com 2019 - 2024. All rights reserved.