2009-10-26 32 views
5

El siguiente código lee un archivo de texto, un carácter a la vez y lo imprime en la salida estándar:Buscar cadena en archivo de texto C

#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); 
} 

Pero en lugar de imprimir a la salida estándar [putchar (CH)] Quiero buscar en el archivo cadenas específicas proporcionadas en otro archivo de texto, es decir. strings.txt y la salida de la línea con el partido a 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 

En este caso las tres primeras líneas de text_file.txt se correspondería. He realizado algunas investigaciones sobre las operaciones de archivos en C, y parece que puedo leer un carácter a la vez con fgetc [como lo hago en mi código], una línea con fgets y un bloque con fread, pero no hay palabras como supongo sería perfecto en mi situación?

+3

por qué se desea escribir este programa ?! Usa grep/awk/sed para hacer esto. –

+0

No, Tim. Las etiquetas son para buscar Nadie va a buscar eso. – GManNickG

+1

Sí, sé que con las herramientas estándar de Unix puedo resolver esto en cuestión de segundos, pero esto es para obtener una comprensión más profunda de C archivo IO. –

Respuesta

7

Supongo que se trata de un ejercicio de aprendizaje y que simplemente está buscando un lugar para comenzar. De lo contrario, no deberías reinventar la rueda.

El siguiente código le dará una idea de lo que se trata. Es un programa que le permite especificar el nombre del archivo que se va a buscar y un único argumento para buscar en ese archivo. Debería poder modificar esto para poner las frases para buscar en una matriz de cadenas y verificar si alguna de las palabras en esa matriz aparece en cualquiera de las líneas leídas.

La función clave que está buscando es 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; 
} 

Salida de ejemplo:

 
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; 
+0

Esto se ve muy interesante. Está asumiendo correctamente, este es un ejercicio de aprendizaje para mí, y puedo ver que la fuente consta de elementos con los que he trabajado previamente, por lo que debería ser capaz de entender completamente este código. –

+0

Soy bastante nuevo en el código C pero acabo de reemplazar toda la llamada a la función read_line con la llamada a función fgets y asigné la línea char * en la función principal a un número arbitrariamente grande ya que fgets se detiene en el carácter '\ n'. ¿Puede explicar el propósito de la función read_line? Parece que hay un montón de código superfluo allí. – anon58192932

+1

@advocate [¿Qué tan grande es lo suficientemente grande?] (Http://en.wikipedia.org/wiki/Buffer_overflow) Empiezo con un buffer de tamaño razonable y sigo expandiéndolo según sea necesario. En realidad, debería haber otro control para que el búfer se vuelva demasiado grande para evitar que su computadora se quede sin memoria si alguien está alimentando un flujo sin terminaciones de línea, pero este fue un simple ejercicio de aprendizaje. –

4

Recuerde: fgetc(), getc(), getchar() devuelven un número entero, no un carácter. El entero puede ser EOF o un carácter válido, pero devuelve un valor más que el rango admitido por el tipo de carácter.

Usted está escribiendo un sustituto para el comando 'fgrep':

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

En lugar de caracteres de lectura, que van a necesitar para leer líneas - utilizando fgets(). (¡Olvídese de que la función get() existe!)

He sangrado su código e insertado un retorno 0; al final para ti (aunque C99 hace un 'retorno 0' implícito) si te caes del final de main()). Sin embargo, C99 también exige un tipo de devolución explícita para cada función, y agregué la 'int' a 'int main()' para usted (pero no puede usar la excusa conforme a C99 para no devolver 0 al final). Los mensajes de error se deben escribir en el error estándar en lugar de la salida estándar.

Probablemente necesite usar la asignación dinámica para la lista de cadenas. Una búsqueda simple simplemente aplicará 'strstr()' buscando cada una de las cadenas requeridas en cada línea de entrada (asegurándose de romper el bucle una vez que haya encontrado una coincidencia para que no se repita una línea si hay múltiples coincidencias en una sola línea).

Una búsqueda más sofisticada predeciría qué caracteres se pueden ignorar para que pueda buscar todas las cadenas en paralelo, salteando el texto más rápido que el loop-in-a-loop. Esto podría ser una modificación de un algoritmo de búsqueda como Boyer-Moore o Knuth-Morris-Pratt (añadido: o Rabin-Karp que está diseñado para la búsqueda paralela de múltiples cadenas).

+0

Personalmente, prefiero escribir una función para almacenar en el búfer los caracteres ... usar solo fgets te da límites arbitrarios en la longitud de la línea. – asveikau

+0

@asveikau: ¿No veo la diferencia? Cuando usemos fgets proporcionamos el búfer, podemos configurarlo de cualquier tamaño que queramos. Y si las líneas en strings.txt son más largas que el búfer, tenemos problemas de todos modos ... ¿Quiere decir que deberíamos gestionar el caso de desbordamiento del búfer incluso cuando usemos fgets? sí, de hecho, y es menos obvio que con un buffer sin tipo. – kriss

+0

fgets() lee hasta la longitud de búfer dada; si no ha encontrado una nueva línea en el momento en que se queda sin espacio, se detiene y regresa. Entonces, si el último carácter no es nueva y el búfer está lleno, entonces puede encontrar más espacio (reasignar?) Para poner los caracteres adicionales, llame de nuevo a fgets() (cuidadosamente - comenzando donde terminó, solo contándolo) el espacio extra) y obtener más de la línea. Entonces, sí, puede escribir su propio lector para obtener datos en un búfer dinámicamente asignado que crece, o use fgets() para hacer la lectura mientras maneja el búfer. –

2

La lectura por bloques es siempre mejor, porque así es como funciona el sistema de archivos subyacente.

Por lo tanto, solo lea por bloques, verifique si alguna de sus palabras aparece en el búfer, y luego lea otro búfer lleno. Solo tiene que tener cuidado para volver a copiar los últimos caracteres del buffer anterior en el nuevo para evitar la pérdida de detección si las palabras de búsqueda están en el límite del buffer.

Si este algoritmo trivial no es suficiente (en su caso, probablemente) existe un algoritmo mucho más sofisticado para buscar simultáneamente varias subcadenas en una memoria intermedia, cf Rabin-Karp.

+0

cuando usas fgetc(), estoy bastante seguro de que stdio leerá bloques y caracteres del buffer ... – asveikau

+0

cierto, pero llamar a fgetc tiene su costo en sí mismo y si quieres comparar la entrada con una cadena (o varias cadenas) tendrás que copiarlo en alguna parte. Eso tiene un costo mucho mayor que leer un búfer completo y trabajar con él. Leer una línea completa como Jonathan propone es también una buena alternativa para leer un búfer completo si no desea administrar los detalles sangrientos para leer directamente los búferes. – kriss

2
cat strings.txt |while read x; do grep "$x" text_file.txt; done 
+1

¿Quiso decir 'fgrep -f strings.txt text_file.txt> out.txt'? –

+0

Sí, sí, 'fgrep -f strings.txt text_file.txt'. Creo que más exposición significa más opciones. –

+0

gracias. escribir un programa C para hacer esto es una completa pérdida de tiempo. –

Cuestiones relacionadas