2016-08-22 5 views
0

мне нужно прочитать следующий текст файла:как использовать как зсапЕ и fgets, чтобы прочитать файл

2 2 
Kauri tree 
Waterfall 
0 0 W S 
0 1 E N 

Я хотел использовать scanf, чтобы получить первую строку и использовать fgets для второго и третьего затем снова используйте scanf для остальных линий.

я написал такой код:

#include <stdio.h> 

#define NUM_OF_CHAR 2 

int main() 
{ 
    int node, edge; 
    scanf("%d %d", &node, &edge); 

    FILE* fp; 
    fp = stdin; 

    char* str[NUM_OF_CHAR]; //should be char str[NUM_OF_CHAR]; 

    for (int i = 0; i < node; i++) { 
     fgets(str[i], 2, fp);  //should be fgets(str, 2, fp); 
    } 
    printf("%s", str[0]);   //printf("%s", str); 
} 

вход я вошел был:

2 2 
hello 

я получил Segmentation fault

Я видел подобный вопрос здесь, человек отметил, что I может позвонить fgets один раз для получения первой строки, но проигнорировать его, а затем снова использовать fgets, чтобы получить вторую строку. Но я не знаю, как это сделать.

+0

Используйте fgets(), а затем strtok(), чтобы разбить строку вверх. –

+0

Что касается вашей * текущей * проблемы, то аварии вы получаете, теперь подумайте немного. Вы объявляете массив указателей на 'char', но вы никогда не делаете эти указатели на самом деле * точкой * в любом месте. –

+0

Привет, Мэтт. Спасибо за ответ. Я новичок в c. Я не знаю, как использовать strtok(). Я сейчас посмотрю. – chrisgjh

ответ

2

Локальные переменные, определенные внутри func если только они явно не инициализированы, имеют неопределенное значение . Для указателей это означает, что они указывают на случайное местоположение. Использование любой неинициализированной переменной, за исключением ее инициализации, приводит к undefined behavior.

Что происходит, так это то, что fgets будет использовать (неинициализированный и, казалось бы, случайный) указатель и использовать его для записи в память, на которую указывает. Эта память в большинстве случаев не принадлежит вам или вашей программе и может даже перезаписать некоторые другие важные данные. Это может привести к сбоям или другому странному поведению или результатам.

Самое простое решение сделать str массив из массивов символов, как

#define NUM_OF_STRINGS 2 
#define STRING_LENGTH 64 
... 
char str[NUM_OF_STRINGS][STRING_LENGTH]; 
... 
fgets(str[i], sizeof str[i], stdin); 

Вы должны убедиться, что выше STRING_LENGTH достаточно, чтобы соответствовать каждой строки включая символ новой строки и строки терминатор. В случае того, что я показываю выше, это 64, что означает, что вы можете иметь строки длиной не более 62 символов.


Теперь, как и для другой проблемы я указал, с первым вызовом fgets чтения пустой строки.

Если у вас есть вход

2 2 
hello 

вход хранится в буфере памяти, а затем scanf и fgets читает из этого буфера.Буфер, с выше входом, посмотреть что-то вроде этого

 
+----+----+----+----+----+----+----+----+----+ 
| 2 | 2 | \n | h | e | l | l | o | \n | 
+----+----+----+----+----+----+----+----+----+ 

После scanf вызова прочитать два номера входного буфера выглядит как

 
+----+----+----+----+----+----+----+ 
| \n | h | e | l | l | o | \n | 
+----+----+----+----+----+----+----+ 

Так что самый первый вызов fgets в цикл увидит новую строку. Таким образом, он считывает эту новую строку, а затем ее выполняет, оставляя строку "hello\n" в буфере для второй. Позвоните по номеру fgets.

Существует несколько способов решения этой проблемы. Я лично предпочитаю использовать fgets универсально для чтения строк, и если вам нужен простой синтаксический анализ строки, используйте sscanf (обратите внимание на ведущий s, также пожалуйста, see here for a good reference of all scanf variants).

Другой способ - просто прочитать символы с входа, по одному символу за раз и отбросить их. Когда вы читаете новую строку, остановите цикл и продолжите работу с остальной частью программы.

+0

спасибо. вы сделали это очень легко понять для меня. – chrisgjh

1

Рассмотрим следующий пример, где комментарии объяснить некоторые важные моменты:

#include <stdio.h> 

#define NUM_OF_CHAR 2 
#define LEN_OF_STR 20 

int main() 
{ 
    int node, edge; 
    FILE* fp; 
    fp = stdin; 
    char strbuf[LEN_OF_STR]; 
    // stream is available after that 
    // reading numbers 
    fscanf(fp, "%d %d", &node, &edge); 
    // reading strings 
    for (int i = 0; i < node; i++) { 
     // reading line from input stream 
     fgets(strbuf, LEN_OF_STR, fp); 
    } 
    // cleaning input buffer 
    while (getchar() != '\n'); 
    // reading lines with data 
    char str[NUM_OF_CHAR]; 
    int a, b; 
    for (int i = 0; i < node; i++) { 
     // reading two numbers and two characters 
     fscanf(fp, "%d %d %c %c", &a, &b, &str[0], &str[1]); 
     // do something with dada, e.g. output 
     printf("%d %d %c %c\n", a, b, str[0], str[1]); 
    } 
    return 0; 
} 

При чтении данных с scanf или fscanf вы можете проверить результаты, например:

if (4 == fscanf(fp, "%d %d %c %c", &a, &b, &str[0], &str[1])) 
    { 
     // actions for correct data 
    } 
    else 
    { 
     // actions for wrong input 
    } 

здесь формат линия имеет 4 спецификаторы - «% d% d% c% c», поэтому мы проверяем как «сравниваем возвращаемое значение с 4»

+2

Проверка возвращаемого значения 'scanf' является * обязательным * – 4386427

+0

это действительно работает для меня. спасибо – chrisgjh

1

Я решил свою проблему. Я не должен использовать указатель char* и указывать на массив. Первый параметр, который передается функции fgets, должен быть char*, поэтому я должен был использовать только массив.

также, поскольку scanf отскакивает от первой линии уже, если я использую fgets следующий, он автоматически получит следующую строку.

#include <stdio.h> 

#define NUM_OF_CHAR 100 

int main() 
{ 
    int node, edge; 
    scanf("%d %d", &node, &edge); 

    FILE* fp; 
    fp = stdin; 

    char str[NUM_OF_CHAR] = {'\0'}; 

    for (int i = 0; i < node; i++) { 
     fgets(str, NUM_OF_CHAR, fp); 
    } 
    printf("%s", str); 
}