2009-09-05 14 views
6

Tengo un archivo de la siguiente manera:análisis de texto en C

... 
words 13 
more words 21 
even more words 4 
... 

(formato general es una cadena de no-dígitos, seguido de un espacio, entonces cualquier número de dígitos y una nueva línea)

y Me gustaría analizar cada línea, poner las palabras en un campo de la estructura y el número en la otra. En este momento estoy usando un feo truco de leer la línea mientras los caracteres no son números, y luego leo el resto. Creo que hay una manera más clara.

+1

Sea más específico sobre el formato. ¿El separador entre palabras y el número es siempre un único espacio (es decir, no dos espacios, o espacio y una pestaña)? ¿Puede haber espacios en blanco siguiendo el número (antes de la línea nueva)? ¿Pueden las palabras contener dígitos? –

Respuesta

0

Puede intentar usar strtok() para tokenizar cada línea, y luego comprobar si cada token es un número o una palabra (una comprobación bastante trivial una vez que tiene la cadena de tokens - basta con ver el primer carácter del token).

+0

Solo mirando el primer caracter del token no es un cheque muy robusto. No confiaría demasiado en los datos de un archivo. –

+0

Depende del origen del archivo. Si se trata de archivos internos generados por la aplicación (o archivos preexistentes cuyo formato es estricto y ya se conoce), entonces es muy posible que no se necesite una comprobación sólida. – Amber

+1

En general, strtok() no es una forma particularmente buena de hacer las cosas. Doblemente no en un programa enhebrado. Además, si el almacenamiento requerido es 'cadena que posiblemente contiene espacios' más el número, strtok probablemente dividirá las cosas en demasiadas partes. –

6

Editar: Puede utilizar pNum-buf para obtener la longitud de la parte alfabética de la cadena, y utilizar strncpy() para copiar que en otro búfer. Asegúrese de agregar un '\ 0' al final del búfer de destino. Insertaría este código antes del pNum ++.

int len = pNum-buf; 
strncpy(newBuf, buf, len-1); 
newBuf[len] = '\0'; 

Se podía leer la línea entera en un búfer y luego usar:

char *pNum; 
if (pNum = strrchr(buf, ' ')) { 
    pNum++; 
} 

para obtener un puntero al campo de número.

+1

Eso es lo que estaba escribiendo, gracias a la alerta naranja ajaxy de Stack Overflow :-) – p4bl0

+1

Je, usualmente estoy del otro lado de la alerta también. –

+0

Eso funciona, pero ¿qué pasa con la parte alfabética? ¿Cómo lo copio hasta el último espacio? –

0

Suponiendo que el número está seguido inmediatamente por '\ n'. se puede leer cada línea para carboniza a búfer, utilice sscanf ("% d") en toda la línea para obtener el número, y luego calcular el número de caracteres que este número lleva al final de la cadena de texto.

1
fscanf(file, "%s %d", word, &value); 

Esto obtiene los valores directamente en una cadena y un entero, y hace frente a las variaciones en los espacios en blanco y formatos numéricos, etc.

Editar

Lamentablemente, se me olvidó que tenía espacios entre las palabras. En ese caso, haría lo siguiente. (Tenga en cuenta que trunca el texto original en 'línea')

// Scan to find the last space in the line 
char *p = line; 
char *lastSpace = null; 
while(*p != '\0') 
{ 
    if (*p == ' ') 
     lastSpace = p; 
    p++; 
} 


if (lastSpace == null) 
    return("parse error"); 

// Replace the last space in the line with a NUL 
*lastSpace = '\0'; 

// Advance past the NUL to the first character of the number field 
lastSpace++; 

char *word = text; 
int number = atoi(lastSpace); 

Puede resolver esto utilizando funciones stdlib, pero lo anterior es probable que sea más eficiente, ya que sólo está en busca de los personajes que le interesan .

+0

El% s solo coincidirá con el siguiente carácter de espacio en blanco. –

+0

Duh, leí el ejemplo, luego leí la descripción del formato a continuación y olvidé que el formato podría tener múltiples espacios. (¡sonrojo!) –

0

Dependiendo de la complejidad de sus cadenas se convierten es posible que desee utilizar la biblioteca PCRE. Al menos de esa manera, puedes compilar una expresión regular perl'ish para dividir tus líneas. Sin embargo, puede ser exagerado.

0

Dada la descripción, esto es lo que haría: leer cada línea como una sola cadena usando fgets() (asegurándose de que el buffer de destino sea lo suficientemente grande), luego divida la línea usando strtok(). Para determinar si cada token es una palabra o un número, usaría strtol() para intentar la conversión y verificar la condición de error. Ejemplo:

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

/** 
* Read the next line from the file, splitting the tokens into 
* multiple strings and a single integer. Assumes input lines 
* never exceed MAX_LINE_LENGTH and each individual string never 
* exceeds MAX_STR_SIZE. Otherwise things get a little more 
* interesting. Also assumes that the integer is the last 
* thing on each line. 
*/ 
int getNextLine(FILE *in, char (*strs)[MAX_STR_SIZE], int *numStrings, int *value) 
{ 
    char buffer[MAX_LINE_LENGTH]; 
    int rval = 1; 
    if (fgets(buffer, buffer, sizeof buffer)) 
    { 
    char *token = strtok(buffer, " "); 
    *numStrings = 0; 
    while (token) 
    { 
     char *chk; 
     *value = (int) strtol(token, &chk, 10); 
     if (*chk != 0 && *chk != '\n') 
     { 
     strcpy(strs[(*numStrings)++], token); 
     } 
     token = strtok(NULL, " "); 
    } 
    } 
    else 
    { 
    /** 
    * fgets() hit either EOF or error; either way return 0 
    */ 
    rval = 0; 
    } 
    return rval; 
} 
/** 
* sample main 
*/ 
int main(void) 
{ 
    FILE *input; 
    char strings[MAX_NUM_STRINGS][MAX_STRING_LENGTH]; 
    int numStrings; 
    int value; 

    input = fopen("datafile.txt", "r"); 
    if (input) 
    { 
    while (getNextLine(input, &strings, &numStrings, &value)) 
    { 
     /** 
     * Do something with strings and value here 
     */ 
    } 
    fclose(input); 
    } 
    return 0; 
} 
0

Dada la descripción, creo que haría uso de una variante de este código C99 (Probado):

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

struct word_number 
{ 
    char word[128]; 
    long number; 
}; 

int read_word_number(FILE *fp, struct word_number *wnp) 
{ 
    char buffer[140]; 
    if (fgets(buffer, sizeof(buffer), fp) == 0) 
     return EOF; 
    size_t len = strlen(buffer); 
    if (buffer[len-1] != '\n') // Error if line too long to fit 
     return EOF; 
    buffer[--len] = '\0'; 
    char *num = &buffer[len-1]; 
    while (num > buffer && !isspace(*num)) 
     num--; 
    if (num == buffer)   // No space in input data 
     return EOF; 
    char *end; 
    wnp->number = strtol(num+1, &end, 0); 
    if (*end != '\0') // Invalid number as last word on line 
     return EOF; 
    *num = '\0'; 
    if (num - buffer >= sizeof(wnp->word)) // Non-number part too long 
     return EOF; 
    memcpy(wnp->word, buffer, num - buffer); 
    return(0); 
} 

int main(void) 
{ 
    struct word_number wn; 
    while (read_word_number(stdin, &wn) != EOF) 
     printf("Word <<%s>> Number %ld\n", wn.word, wn.number); 
    return(0); 
} 

se podría mejorar el informe de errores mediante la devolución de valores diferentes para los diferentes problemas . Puede hacer que funcione con memoria asignada dinámicamente para la parte de palabra de las líneas. Podrías hacerlo funcionar con líneas más largas de lo que permití. Puede escanear hacia atrás sobre dígitos en lugar de no espacios, pero esto le permite al usuario escribir "abc 0x123" y el valor hexadecimal se maneja correctamente. Es posible que prefiera asegurarse de que no haya dígitos en la parte de la palabra; este código no le importa