2010-03-23 16 views
8

Estoy obteniendo "Error de bus" tratando de leer stdin en una variable char*. Solo quiero leer todo lo que viene sobre stdin y ponerlo primero en una variable, luego continuar trabajando en la variable.¿Cómo se lee la entrada estándar en la variable de cadena hasta EOF en C?

Mi código es el siguiente:

char* content; 
char* c; 
while(scanf("%c", c)) { 
strcat(content, c); 
} 

fprintf(stdout, "Size: %d", strlen(content)); 

Pero de alguna manera siempre me dan "Error de bus" devuelta al llamar cat test.txt | myapp, donde myapp es el código compilado anteriormente.

Mi pregunta es ¿cómo leo stdin hasta EOF en una variable? Como puede ver en el código, solo quiero imprimir el tamaño de la entrada viniendo por stdin, en este caso debe ser igual al tamaño del archivo test.txt.

Pensé que el solo uso de scanf sería suficiente, tal vez una forma de leer temporalmente stdin?

+0

Por qué no utilizar stat() para obtener el tamaño del archivo, luego use el tamaño de archivo + 1 para (intente) malloc el búfer y luego lea() en el búfer? – technosaurus

Respuesta

16

en primer lugar, estás pasando punteros no inicializados, lo que significa scanf y strcat escribirá la memoria no es propietario. En segundo lugar, strcat espera dos cadenas terminadas en nulo, mientras que c es solo un caracter. Esto nuevamente hará que lea la memoria que no es de su propiedad. No necesita scanf, porque no está procesando realmente. Finalmente, leer un personaje a la vez es innecesariamente lento. Aquí está el comienzo de una solución, utilizando un tampón de tamaño variable para la cadena final, y un tampón fijo para los fgets llamar

#define BUF_SIZE 1024 
char buffer[BUF_SIZE]; 
size_t contentSize = 1; // includes NULL 
/* Preallocate space. We could just allocate one char here, 
but that wouldn't be efficient. */ 
char *content = malloc(sizeof(char) * BUF_SIZE); 
if(content == NULL) 
{ 
    perror("Failed to allocate content"); 
    exit(1); 
} 
content[0] = '\0'; // make null-terminated 
while(fgets(buffer, BUF_SIZE, stdin)) 
{ 
    char *old = content; 
    contentSize += strlen(buffer); 
    content = realloc(content, contentSize); 
    if(content == NULL) 
    { 
     perror("Failed to reallocate content"); 
     free(old); 
     exit(2); 
    } 
    strcat(content, buffer); 
} 

if(ferror(stdin)) 
{ 
    free(content); 
    perror("Error reading from stdin."); 
    exit(3); 
} 

EDIT: Como Wolfer aludido, un nulo en su entrada provocará que la cadena se termina prematuramente cuando se usa fgets. getline es una mejor opción si está disponible, ya que maneja la asignación de memoria y no tiene problemas con la entrada NUL.

+0

Da conversión inválida de 'void *' a 'char *' [- fpermissive] 'en' char * content = malloc (sizeof (char) * BUF_SIZE); 'y' content = realloc (content, contentSize); ' con gcc 4.8.1 ... – Wolfer

+0

También falla cuando se presenta con '0x00' en stdin. – Wolfer

+1

@Wolfer, compilador * C * de gcc [no tiene] (http://linux.die.net/man/1/gcc) -fpermissive.¿Quizás lo está compilando como C++? eso explicaría el error? Las etiquetas dicen C. Tienes razón de que los fgets no funcionan bien con los personajes NUL. No causa ningún comportamiento indefinido, pero no lo sabrá y la cadena terminará prematuramente. Agregaré una nota sobre getline a la pregunta. –

7

Su problema es que nunca ha asignado c y content, por lo que no apuntan a ninguna parte definida, es probable que estén apuntando a alguna memoria no asignada, o algo que no existe en absoluto. Y luego estás poniendo datos en ellos. Debe asignarlos primero. (Eso es lo que normalmente significa un error de bus;. Que ha intentado hacer un acceso a memoria que no es válida)

(Como alternativa, ya que c siempre es la celebración de un solo carácter, se puede declarar como char c y pasar &c a scanf. No hay necesidad de declarar una cadena de caracteres cuando uno lo hará.)

Una vez que lo haga, se encontrará con el problema de asegurarse de que content sea suficiente para contener toda la entrada. O necesita tener una idea de cuánto ingreso espera y asignarlo al menos por ese tiempo (y luego error si lo excede), o necesita una estrategia para reasignarlo en un tamaño mayor si no es lo suficientemente largo.

Ah, y también se encontrará con el problema de que strcat espera una cadena, ni un solo carácter. Incluso si deja c como char*, la llamada scanf no la convierte en una cadena. Una cadena de un solo carácter es (en la memoria) un carácter seguido de un carácter nulo para indicar el final de la cadena. scanf, al escanear un solo carácter, no va a poner el carácter nulo después de él. Como resultado, strcpy no va a saber dónde está el final de la cadena, y va a vagar por la memoria buscando el carácter nulo.

+5

No son NULOS, tienen valores indefinidos. –

+0

+1 @Matthew, a menos que sean globales. –

+1

@Brooks, simplemente hacer 'c' a' char' y usar '& c' no es suficiente, ya que' strcat() 'necesita una cadena terminada en nulo. –

6

Dado que no le importa el contenido real, ¿por qué molestarse en construir una cadena? También me gustaría usar getchar():

int c; 
size_t s = 0; 

while ((c = getchar()) != EOF) 
{ 
    s++; 
} 

printf("Size: %z\n", s); 

Este código manejar correctamente los casos en que el archivo tiene '\0' caracteres en ella.

+0

El OP dijo: "Solo quiero leer todo lo que viene sobre stdin y ponerlo primero en una variable, luego continuar trabajando en la variable". Creo que el llamado de longitud() es solo un ejemplo. Pero su comentario acerca de que el archivo tiene caracteres '\ 0' es muy importante; su idea de ponerlo en una sola variable puede ser un poco defectuoso. –

+3

'c' debe declararse como' int' para incluir el rango de 'char' más' EOF'. –

+0

@Jon yup, buena llamada. Escribiendo sin pensar –

1

El problema aquí es que se hace referencia a una variable puntero que no hay memoria asignada a través de malloc, por lo tanto, los resultados serían definidos, y no solo eso, mediante el uso de strcat en un puntero indefinido que podría estar apuntando a algo, que terminó hasta con un error de autobús!

Este sería el código fijo requerido ....

 
char* content = malloc (100 * sizeof(char)); 
char c; 
if (content != NULL){ 
    content[0] = '\0'; // Thanks David! 
    while ((c = getchar()) != EOF) 
    { 
     if (strlen(content) < 100){ 
      strcat(content, c); 
      content[strlen(content)-1] = '\0'; 
     } 
    } 
} 
/* When done with the variable */ 
free(content); 

El código pone de relieve la responsabilidad del programador para gestionar la memoria - para cada malloc hay un free si no, usted tiene una pérdida de memoria!

Editar: Gracias a David Gelhar por su punto de salida en mi fallo! He arreglado el código anterior para reflejar las correcciones ... por supuesto en una situación de la vida real, quizás el valor fijo de 100 podría cambiarse a quizás #define para facilitar la expansión del búfer al duplicar la cantidad de memoria a través de realloc y recortar al tamaño ...

+1

Este código tiene un error: la primera vez a través del ciclo está llamando "strlen" en datos no inicializados. (y, por supuesto, debe comprobar si hay desbordamientos de búfer si hay más de 100 caracteres de entrada) –

+0

Como notó Jon Purdy en una de las otras publicaciones, necesita declarar c como int, no char, para que el posible resultado de EOF estará en rango. De lo contrario, entrarás en un ciclo infinito. Además, su llamada strcat espera dos cadenas terminadas en nulo, no una cadena y un solo carácter. Como está escrito, no compilará. –

1

Suponiendo que desea obtener (más corto que MAXL-1 caracteres) cuerdas y no a procesar su Char archivo de carbón, lo hice de la siguiente manera:

#include <stdio.h> 
#include <string.h> 
#define MAXL 256 

main(){ 
    char s[MAXL]; 
    s[0]=0; 
    scanf("%s",s); 
    while(strlen(s)>0){ 
    printf("Size of %s : %d\n",s,strlen(s)); 
    s[0]=0; 
    scanf("%s",s); 
    }; 
} 
Cuestiones relacionadas