2011-01-27 49 views
27

Hola a todos, estoy escribiendo un intérprete BF en C y me he encontrado con un problema al leer archivos. Solía ​​usar scanf para leer la primera cadena, pero luego no podía tener espacios ni comentarios en su código BF.Lectura de un archivo carácter por carácter en C

Ahora mismo esto es lo que tengo.

char *readFile(char *fileName) 
{ 
    FILE *file; 
    char *code = malloc(1000 * sizeof(char)); 
    file = fopen(fileName, "r"); 
    do 
    { 
    *code++ = (char)fgetc(file); 

    } while(*code != EOF); 
    return code; 
} 

Sé que el problema surge en la forma en que estoy asignando el siguiente caracter en el archivo para el puntero del código, pero que no estoy seguro de lo que es.
Me falta conocimiento de mi puntero, que es el objetivo de este ejercicio. El intérprete funciona bien, todos utilizan punteros, solo estoy teniendo problemas para leer archivos.

(Voy a implementar solo lectura "+ -> < [].," En el archivo más tarde, aunque si alguien tiene una buena manera de hacerlo, sería genial si me lo hiciera saber !)

Gracias de antemano

Respuesta

32

Hay una serie de cosas mal con su código:

char *readFile(char *fileName) 
{ 
    FILE *file; 
    char *code = malloc(1000 * sizeof(char)); 
    file = fopen(fileName, "r"); 
    do 
    { 
     *code++ = (char)fgetc(file); 

    } while(*code != EOF); 
    return code; 
} 
  1. ¿Qué pasa si el archivo es mayor que 1.000 bytes?
  2. Está aumentando code cada vez que lee un carácter, y devuelve code de vuelta a la persona que llama (aunque ya no apunta al primer byte del bloque de memoria como lo devolvió malloc).
  3. Estás emitiendo el resultado de fgetc(file) en char. Debe verificar EOF antes de enviar el resultado al char.

Es importante mantener el puntero original devuelto por malloc para que pueda liberarlo más tarde. Si hacemos caso omiso del tamaño del archivo, podemos lograr esto todavía con lo siguiente:

char *readFile(char *fileName) 
{ 
    FILE *file = fopen(fileName, "r"); 
    char *code; 
    size_t n = 0; 
    int c; 

    if (file == NULL) 
     return NULL; //could not open file 

    code = malloc(1000); 

    while ((c = fgetc(file)) != EOF) 
    { 
     code[n++] = (char) c; 
    } 

    // don't forget to terminate with the null character 
    code[n] = '\0';   

    return code; 
} 

Hay varias llamadas al sistema que le dará el tamaño de un archivo; uno común es stat.

+0

¿Cuál es la manera más fácil de contar el número de caracteres en un archivo para que pueda establecer el "1000" a eso? Además, no estoy exactamente seguro de lo que quiere decir con el número 2, entiendo que lo estoy haciendo mal de esa manera, pero ¿cómo lo enmendaría? –

+0

@pwnmonkey: Lo que quiero decir es que está devolviendo 'código' mientras apunta al * final * del archivo, no mientras apunta al comienzo. – dreamlax

+0

¡Oh OK, lo has editado, gracias! –

2

creo que el problema más importante es que se está incrementando a medida que lee code cosas en, y luego devolver el valor final de code, es decir, se le devuelve un puntero al final de la cadena. Probablemente desee hacer una copia de code antes del ciclo, y luego devolverlo.

Además, las cadenas C deben terminarse en nulo. Es necesario asegurarse de que se coloca un '\0' directamente después del carácter final que se lee en

Nota:. Sólo podía usar fgets() para obtener toda la línea en un hit.

+0

O use fread para leer todo el archivo. –

+0

Correcto, pero voy a excluir todo lo que no sea código BF, por lo que es más fácil procesarlo de una vez. –

1

Cualquiera de los dos debe hacer el truco -

char *readFile(char *fileName) 
{ 
    FILE *file; 
    char *code = malloc(1000 * sizeof(char)); 
    char *p = code; 
    file = fopen(fileName, "r"); 
    do 
    { 
    *p++ = (char)fgetc(file); 
    } while(*p != EOF); 
    *p = '\0'; 
    return code; 
} 

char *readFile(char *fileName) 
{ 
    FILE *file; 
    int i = 0; 
    char *code = malloc(1000 * sizeof(char)); 
    file = fopen(fileName, "r"); 
    do 
    { 
    code[i++] = (char)fgetc(file); 
    } while(code[i-1] != EOF); 
    code[i] = '\0' 
    return code; 
} 

Al igual que los otros críticos han señalado, es necesario asegurarse de que el tamaño del archivo no supera los 1000 caracteres.Además, recuerde liberar la memoria cuando haya terminado de usarla.

1

El problema aquí es doble: a) incrementa el puntero antes de comprobar el valor leído, yb) ignora el hecho de que fgetc() devuelve un int en lugar de un char.

El primero se puede arreglar fácilmente:

char *orig = code; // the beginning of the array 
// ... 
do { 
    *code = fgetc(file); 
} while(*code++ != EOF); 
*code = '\0'; // nul-terminate the string 
return orig; // don't return a pointer to the end 

El segundo problema es más sutil - fgetc devuelve un int sonthat el valor EOF puede ser distinguida de cualquier posible valor CHSR. Al solucionar esto, se usa una int temporal para la comprobación de EOF y, probablemente, un bucle while normal en lugar de do/while.

+1

Si 'EOF' es algo entre -1 y -128, entonces * es * representable como un valor' char' (suponiendo que el tipo 'char' simple de la implementación está firmado). En este caso, la conversión de un 'int' a' char' está completamente definida cuando el valor es representable por un 'char', que puede ser el caso con' EOF'. – dreamlax

+0

Estoy usando unsigned. –

2

Aquí hay una forma sencilla de hacer caso omiso de todo menos caracteres brainfuck válidos:

#define BF_VALID "+-><[].," 

if (strchr(BF_VALID, c)) 
    code[n++] = c; 
+0

Sí, ya estaba usando esto antes, pero el problema es que si el archivo tiene líneas nuevas en él, coloca esas líneas nuevas dentro de la cadena. Lo cual es malo si quería escribir el código BF puro en un archivo en una línea, o incluso imprimirlo en la consola. –

+1

@pwnmonkey: No, esto no almacenará ningún carácter de nueva línea en la cadena de destino (si agregaste un '\ n' a' BF_VALID', lo haría). – caf

2

el archivo está siendo abierta y no cerrada por cada llamada a la función también

+0

Sí, lo sé, ya había cambiado eso, pero gracias por informarme. –

7

Ampliando el código anterior de @dreamlax

char *readFile(char *fileName) { 
    FILE *file = fopen(fileName, "r"); 
    char *code; 
    size_t n = 0; 
    int c; 

    if (file == NULL) return NULL; //could not open file 
    fseek(file, 0, SEEK_END); 
    long f_size = ftell(file); 
    fseek(file, 0, SEEK_SET); 
    code = malloc(f_size); 

    while ((c = fgetc(file)) != EOF) { 
     code[n++] = (char)c; 
    } 

    code[n] = '\0';   

    return code; 
} 

Esto le da la longitud del archivo, luego procede a leerlo carácter por carácter.

+0

¿Puedes explicar cómo funcionan fseek y ftell? Creo que necesitarías algún tipo de ciclo para contar los caracteres del archivo. – cokedude

+1

Piense en 'fseek' como una forma de reposicionar un cursor. fseek (archivo, 0, SEEK_END); coloca el cursor al final del archivo, luego 'ftell' te dice dónde está el cursor. Eso te da el tamaño del archivo. 'fseek (archivo, 0, SEEK_SET);' pone el cursor al principio del archivo para que pueda leerse. Si no lo hace, comenzará a leer el archivo desde el final, lo que causará errores y arruinará toda la operación. – Justin

Cuestiones relacionadas