Поиск строки в текстовом файле C - PullRequest
5 голосов
/ 27 октября 2009

Следующий код читает текстовый файл по одному символу за раз и выводит его на стандартный вывод:

#include <stdio.h>

int main()
{
    char file_to_open[] = "text_file.txt", ch;
    FILE *file_ptr;

    if((file_ptr = fopen(file_to_open, "r")) != NULL)
    {
        while((ch = fgetc(file_ptr)) != EOF)
        {
            putchar(ch);
        }
    }
    else
    {
        printf("Could not open %s\n", file_to_open);
        return 1;
    }
    return(0);
}

Но вместо печати в stdout [putchar (ch)] я хочу найти в файле конкретные строки, предоставленные в другом текстовом файле, т.е. strings.txt и вывести строку с соответствием в out.txt

text_file.txt

1993 - 1999 Pentium
1997 - 1999 Pentium II
1999 - 2003 Pentium III
1998 - 2009 Xeon
2006 - 2009 Intel Core 2

strings.txt:

Nehalem
AMD Athlon
Pentium

В этом случае три первые строки text_file.txt будут совпадать. Я провел некоторое исследование файловых операций в C, и кажется, что я могу прочитать один символ за один раз с fgetc [как я делаю в своем коде], одну строку с fgets и один блок с fread, но нет слов, как я думаю, было бы идеально в моей ситуации?

Ответы [ 4 ]

9 голосов
/ 27 октября 2009

Я предполагаю, что это учебное упражнение, а вы просто ищете место для начала. В противном случае вам не следует изобретать велосипед.

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

Ключевая функция, которую вы ищете: strstr.

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

#ifdef DEBUG
#define INITIAL_ALLOC 2
#else
#define INITIAL_ALLOC 512
#endif

char *
read_line(FILE *fin) {
    char *buffer;
    char *tmp;
    int read_chars = 0;
    int bufsize = INITIAL_ALLOC;
    char *line = malloc(bufsize);

    if ( !line ) {
        return NULL;
    }

    buffer = line;

    while ( fgets(buffer, bufsize - read_chars, fin) ) {
        read_chars = strlen(line);

        if ( line[read_chars - 1] == '\n' ) {
            line[read_chars - 1] = '\0';
            return line;
        }

        else {
            bufsize = 2 * bufsize;
            tmp = realloc(line, bufsize);
            if ( tmp ) {
                line = tmp;
                buffer = line + read_chars;
            }
            else {
                free(line);
                return NULL;
            }
        }
    }
    return NULL;
}

int
main(int argc, char *argv[]) {
    FILE *fin;
    char *line;

    if ( argc != 3 ) {
        return EXIT_FAILURE;
    }

    fin = fopen(argv[1], "r");

    if ( fin ) {
        while ( line = read_line(fin) ) {
            if ( strstr(line, argv[2]) ){
                fprintf(stdout, "%s\n", line);
            }
            free(line);
        }
    }

    fclose(fin);
    return 0;
}

Пример вывода:

E:\Temp> searcher.exe searcher.c char
char *
    char *buffer;
    char *tmp;
    int read_chars = 0;
    char *line = malloc(bufsize);
    while ( fgets(buffer, bufsize - read_chars, fin) ) {
        read_chars = strlen(line);
        if ( line[read_chars - 1] == '\n' ) {
            line[read_chars - 1] = '\0';
                buffer = line + read_chars;
main(int argc, char *argv[]) {
    char *line;
4 голосов
/ 27 октября 2009

Помните: fgetc (), getc (), getchar () все возвращают целое число, а не символ. Целое число может быть EOF или допустимым символом, но оно возвращает еще одно значение, чем диапазон, поддерживаемый типом символа.

Вы пишете суррогат для команды 'fgrep':

fgrep -f strings.txt text_file.txt > out.txt

Вместо чтения символов вам нужно будет читать строки - используя fgets (). (Забудьте, что функция gets () существует!)

Я отступил в вашем коде и вставил возврат 0; в конце для вас (хотя C99 делает неявное 'return 0;', если вы опускаетесь до конца main ()). Однако C99 также требует явного возвращаемого типа для каждой функции - и я добавил для вас int к int main () (но вы не можете использовать оправдание, совместимое с C99, чтобы не возвращать 0 в конце). Сообщения об ошибках должны записываться в стандартную ошибку, а не в стандартный вывод.

Возможно, вам потребуется использовать динамическое размещение для списка строк. При простом поиске будет просто применяться strstr () для поиска каждой из требуемых строк в каждой строке ввода (убедитесь, что цикл разорван, как только вы нашли совпадение, чтобы строка не повторялась при наличии нескольких совпадений на одной строке).

Более сложный поиск позволил бы заранее рассчитать, какие символы можно игнорировать, чтобы вы могли искать все строки параллельно, пропуская текст быстрее, чем цикл в цикле. Это может быть модификация алгоритма поиска, такого как Бойер-Мур или Кнут-Моррис-Пратт ( добавлено : или Рабин-Карп, который предназначен для параллельного поиска нескольких строк).

2 голосов
/ 27 октября 2009
cat strings.txt |while read x; do grep "$x" text_file.txt; done
2 голосов
/ 27 октября 2009

Чтение по блокам всегда лучше, потому что именно так работает базовая файловая система.

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

Если этого тривиального алгоритма недостаточно (в вашем случае, вероятно, так и есть), существует гораздо более сложный алгоритм для поиска одновременно нескольких подстрок в одном буфере. Cf Rabin-Karp .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...