繁体   English   中英

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

[英]How to properly get a line and parse it with C

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

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

我想做的事:

  1. 获取第一行//第一行是由空格分隔的字符串int“ 1 2 3 4 5”
  2. 解析此行,将每个字符数转换为整数
  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
程序结束

直接的主要问题是,您一直告诉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() 他们方面有一些正义,但可能还不够重要。

另请参阅如何在循环中使用sscanf() 有关如何使用sscanf()解析行。

采用:

while (fgets(lines, 30, file))

用于外循环控制; 不要使用feof()除非(可能)在循环终止后区分EOF和I / O错误。 (几年前,我检查了我的数百个C源文件,发现不到一半的eof()用途,全部用于错误检查代码,而没有用于循环控件。您真的不需要使用它非常经常。)

主要问题是:

  • 您永远不会将s重置为0,因此该列始终会增加而不是从0增加到4(如果每行5个数字),那么您就不会从第二行开始写数组中的预期条目,并且有可能以未定义的行为(例如分段错误)写出数组
  • 检查您没有读太多的行和列(在代码中为30),否则您可以用未定义的行为(例如分段错误)写出数组
  • 您错误地使用strtok_r ,仅当您第一次解析行时(编辑之前),第一个参数不能为null
  • 执行number = strtok_r(from, " ", &next) nextstrtok_r修改,同时用于下一行的from初始化,因此第二行将无法正确读取,您的执行仅是:

这是婴儿车[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的权限

附加说明:

  • 检查打开成功
  • 初始化pram或记住第一行中有多少列,并检查下一行中的列数始终相同,当然也要记住有多少行,否则以后您将不知道读取的数字在哪里数组
  • 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来轻松检测数字是否只有数字,请注意,我添加的\\nstrtok的分隔符

编译与执行:

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM