Чтение файла в C - PullRequest
       41

Чтение файла в C

4 голосов
/ 16 декабря 2011

У меня есть входной файл, из которого мне нужно извлечь слова.Слова могут содержать только буквы и цифры, поэтому все остальное будет рассматриваться как разделитель.Я пробовал fscanf, fgets + sscanf и strtok, но ничего не работает.

while(!feof(file))
{
    fscanf(file,"%s",string);
    printf("%s\n",string);
}

Выше явно не работает, потому что он не использует разделители, поэтому я заменил строку следующим образом:

 fscanf(file,"%[A-z]",string);

Он хорошо читает первое слово, но файлуказатель продолжает перематываться, поэтому он читает первое слово снова и снова.

Поэтому я использовал fgets для чтения первой строки и использования sscanf:

sscanf(line,"%[A-z]%n,word,len);
line+=len;

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

while(p != NULL) {
printf("%s\n", p);
p = strtok(NULL, " ");

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

Я что-то здесь упускаюИз-за того, что извлечение слов из файла поначалу казалось простой концепцией, но все, что я пытаюсь сделать, действительно работает?

Ответы [ 4 ]

3 голосов
/ 16 декабря 2011

Рассмотрим создание минимального лексера .В состоянии слово оно будет оставаться в нем до тех пор, пока оно видит буквы и цифры.Он будет переключаться в состояние delimiter при обнаружении чего-либо еще.Тогда он может сделать прямо противоположное в состоянии delimiter .

Вот пример простого конечного автомата, который может оказаться полезным.Для краткости это работает только с цифрами.echo "2341,452(42 555" | ./main напечатает каждое число в отдельной строке.Это не лексер, но идея переключения между состояниями очень похожа.

#include <stdio.h>
#include <string.h>

int main() {
  static const int WORD = 1, DELIM = 2, BUFLEN = 1024;
  int state = WORD, ptr = 0;
  char buffer[BUFLEN], *digits = "1234567890";
  while ((c = getchar()) != EOF) {
    if (strchr(digits, c)) {
      if (WORD == state) {
        buffer[ptr++] = c;
      } else {
        buffer[0] = c;
        ptr = 1;
      }
      state = WORD;
    } else {
      if (WORD == state) {
        buffer[ptr] = '\0';
        printf("%s\n", buffer);
      }
      state = DELIM;
    }
  }
  return 0;
}

Если число состояний увеличивается, вы можете рассмотреть возможность замены операторов if, проверяющих текущее состояние, на блоки switch.Производительность можно повысить, заменив getchar чтением целого блока ввода во временный буфер и итерацией по нему.

В случае необходимости иметь дело с более сложным форматом входного файла, вы можете использовать лексическийгенераторы анализаторов типа flex .Они могут помочь вам определить переходы между состояниями и другие элементы генерации лексера.

2 голосов
/ 16 декабря 2011

Несколько точек:

Прежде всего, не используйте feof(file) в качестве условия цикла;feof не вернет true до тех пор, пока после вы не попытаетесь прочитать за концом файла, поэтому ваш цикл будет выполняться слишком часто.

Во-вторых, вы упомянули это:

fscanf(file,"%[A-z]",string);

Он хорошо читает первое слово, но указатель файла продолжает перематываться, поэтому он читает первое слово снова и снова.

Это не совсем то, что происходит;если следующий символ в потоке не соответствует спецификатору формата, scanf возвращается, не прочитав ничего, и string не изменяется.

Вот простой, хотя и не элегантный, метод: он читает один символза раз из входного файла проверяет, является ли это буквой или цифрой, и, если это так, добавляет ее в строку.

#include <stdio.h>
#include <ctype.h>

int get_next_word(FILE *file, char *word, size_t wordSize)
{
  size_t i = 0;
  int c;

  /**
   * Skip over any non-alphanumeric characters
   */
  while ((c = fgetc(file)) != EOF && !isalnum(c))
    ; // empty loop

  if (c != EOF)
    word[i++] = c;

  /**
   * Read up to the next non-alphanumeric character and
   * store it to word
   */
  while ((c = fgetc(file)) != EOF && i < (wordSize - 1) && isalnum(c))
  {
      word[i++] = c;
  }
  word[i] = 0;
  return c != EOF;
}

int main(void)
{
   char word[SIZE]; // where SIZE is large enough to handle expected inputs
   FILE *file;
   ...
   while (get_next_word(file, word, sizeof word))
     // do something with word
   ...
}
1 голос
/ 16 декабря 2011

Я бы использовал:

FILE *file;
char string[200];

while(fscanf(file, "%*[^A-Za-z]"), fscanf(file, "%199[a-zA-Z]", string) > 0) {
    /* do something with string... */
}

Это пропускает не-буквы, а затем читает строку длиной до 199 букв.Единственная странность в том, что если у вас есть «слова» длиной более 199 букв, они будут разбиты на несколько слов, но вам нужно ограничение, чтобы избежать переполнения буфера ...

0 голосов
/ 16 декабря 2011

Каковы ваши разделители?Второй аргумент strtok должен быть строкой, содержащей ваши разделители, а первый должен быть указателем на вашу строку в первый раз, затем NULL впоследствии:

char * p = strtok(line, ","); // assuming a , delimiter
printf("%s\n", p);

while(p)
{
    p = strtok(NULL, ",");
    printf("%S\n", p);
} 
...