Как правильно получить строку и разобрать ее с помощью C - PullRequest
0 голосов
/ 25 марта 2019

Я пишу программу на C, которая откроет файл, напишет в него, а затем прочитает, что было написано. Я могу открыть, написать и закрыть файл, но не могу прочитать строки и правильно их разобрать.

Я читал много других блогов и сайтов, но ни один из них не полностью посвящен тому, что я пытаюсь сделать. Я пытался адаптировать их общие решения, но никогда не получал желаемого поведения. Я запустил этот код с помощью fgets (), gets (), strtok () и scanf () и fscanf (). Я использовал strtok_r (), как это было рекомендовано в качестве лучшей практики. В качестве экспериментов я использовал get () и scanf (), чтобы посмотреть, каковы будут их результаты, в отличие от fgets () и fscanf ().

Что я хочу сделать:

  1. get first line // fist line - строка разделенных пробелами целых чисел "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
программа заканчивается

Ответы [ 3 ]

1 голос
/ 25 марта 2019

Основные проблемы:

  • вы никогда не сбросите 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 голос
/ 25 марта 2019

Непосредственная главная проблема заключается в том, что вы продолжаете указывать 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 и ошибку ввода / вывода. (Несколько лет назад я проверил несколько сотен моих исходных файлов на C и обнаружил менее полдюжины вариантов использования eof(), все в коде проверки ошибок и ни в одном из элементов управления циклом. Вам действительно не нужно его использовать очень часто вообще.)

0 голосов
/ 25 марта 2019

Если вы хотите проанализировать текст, разделенный пробелами, тогда лучшим выбором будет 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; }

Примечание. ТАКЖЕ проверяйте границы, чтобы убедиться, что границы массивов фиксированного размера не превышены.

...