leggendo grandi liste attraverso lo stdin in C

Se il mio programma avrà grandi liste di numeri passati attraverso stdin , quale sarebbe il modo più efficace per leggerlo?

L’input che sto per passare nel programma sarà del seguente formato:

 3,5;6,7;8,9;11,4;; 

Ho bisogno di elaborare l’input in modo da poter utilizzare i numeri tra i due punti (cioè voglio essere in grado di utilizzare 3 e 5, 6 e 7, ecc. Ecc.). The ;; indica che è la fine della linea.

Stavo pensando di usare un lettore bufferato per leggere intere righe e quindi usare parseInt.

Questo sarebbe il modo più efficace per farlo?

Un altro modo abbastanza elegante per gestire questo è consentire a strtol di analizzare l’input facendo avanzare la stringa da leggere a endptr come restituita da strtol . Combinato con un array allocato / riallocato secondo le necessità, dovresti essere in grado di gestire linee di qualsiasi lunghezza (fino all’esaurimento della memoria). L’esempio seguente utilizza un singolo array per i dati. Se si desidera memorizzare più righe, ciascuna come una matrice separata, è ansible utilizzare lo stesso approccio, ma iniziare con un puntatore all’array di puntatori a int. (cioè int **numbers e allocare i puntatori e quindi ogni matrice). Fatemi sapere se avete domande:

 #include  #include  #include  #include  #define NMAX 256 int main () { char *ln = NULL; /* NULL forces getline to allocate */ size_t n = 0; /* max chars to read (0 - no limit) */ ssize_t nchr = 0; /* number of chars actually read */ int *numbers = NULL; /* array to hold numbers */ size_t nmax = NMAX; /* check for reallocation */ size_t idx = 0; /* numbers array index */ if (!(numbers = calloc (NMAX, sizeof *numbers))) { fprintf (stderr, "error: memory allocation failed."); return 1; } /* read each line from stdin - dynamicallly allocated */ while ((nchr = getline (&ln, &n, stdin)) != -1) { char *p = ln; /* pointer for use with strtol */ char *ep = NULL; errno = 0; while (errno == 0) { /* parse/convert each number on stdin */ numbers[idx] = strtol (p, &ep, 10); /* note: overflow/underflow checks omitted */ /* if valid conversion to number */ if (errno == 0 && p != ep) { idx++; /* increment index */ if (!ep) break; /* check for end of str */ } /* skip delimiters/move pointer to next digit */ while (*ep && (*ep <= '0' || *ep >= '9')) ep++; if (*ep) p = ep; else break; /* reallocate numbers if idx = nmax */ if (idx == nmax) { int *tmp = realloc (numbers, 2 * nmax * sizeof *numbers); if (!tmp) { fprintf (stderr, "Error: struct reallocation failure.\n"); exit (EXIT_FAILURE); } numbers = tmp; memset (numbers + nmax, 0, nmax * sizeof *numbers); nmax *= 2; } } } /* free mem allocated by getline */ if (ln) free (ln); /* show values stored in array */ size_t i = 0; for (i = 0; i < idx; i++) printf (" numbers[%2zu] %d\n", i, numbers[i]); /* free mem allocate to numbers */ if (numbers) free (numbers); return 0; } 

Produzione

 $ echo "3,5;6,7;8,9;11,4;;" | ./bin/prsistdin numbers[ 0] 3 numbers[ 1] 5 numbers[ 2] 6 numbers[ 3] 7 numbers[ 4] 8 numbers[ 5] 11 numbers[ 6] 4 

Funziona anche dove la stringa è memorizzata in un file come:

 $ cat dat/numsemic.csv | ./bin/prsistdin or $ ./bin/prsistdin < dat/numsemic.csv 

Usando fgets e senza size_t

Ci sono voluti un po 'di rielaborazione per arrivare a una revisione che mi ha fatto felice con la linea getline eliminata e il getline sostituito. getline è molto più flessibile, gestendo l'allocazione di spazio per te, con fgets dipende da te. (per non parlare di getline restituisce il numero effettivo di caratteri letti senza dover chiamare strlen ).

Il mio objective qui era quello di preservare la capacità di leggere qualsiasi linea di lunghezza per soddisfare le tue esigenze. Questo significava inizialmente allocare qualche enorme buffer di linea (dispendioso) o inventare uno schema che avrebbe riallocato il buffer della riga di input come necessario nel caso in cui fosse più lungo dello spazio inizialmente assegnato a ln . (questo è ciò che la linea getline fa così bene). Sono ragionevolmente soddisfatto dei risultati. Nota: ho inserito il codice di riallocazione nelle funzioni per mantenere la riga main ragionevolmente pulita. nota 2

Dai un'occhiata al seguente codice. Nota, ho lasciato le direttive del preprocessore DEBUG nel codice che ti permettono di compilare con il flag -DDEBUG se vuoi farlo sputare ogni volta che assegna. [nota 1] È ansible compilare il codice con:

 gcc -Wall -Wextra -o yourexename yourfilename.c 

o se si desidera l'output di debug (ad es. impostare LMAX su 2 o qualcosa di meno della lunghezza della linea), utilizzare quanto segue:

 gcc -Wall -Wextra -o yourexename yourfilename.c -DDEBUG 

Fatemi sapere se avete domande:

 #include  #include  #include  #include  #define NMAX 256 #define LMAX 1024 char *realloc_char (char *sp, unsigned int *n); /* reallocate char array */ int *realloc_int (int *sp, unsigned int *n); /* reallocate int array */ char *fixshortread (FILE *fp, char **s, unsigned int *n); /* read all stdin */ int main () { char *ln = NULL; /* dynamically allocated for fgets */ int *numbers = NULL; /* array to hold numbers */ unsigned int nmax = NMAX; /* numbers check for reallocation */ unsigned int lmax = LMAX; /* ln check for reallocation */ unsigned int idx = 0; /* numbers array index */ unsigned int i = 0; /* simple counter variable */ char *nl = NULL; /* initial allocation for numbers */ if (!(numbers = calloc (NMAX, sizeof *numbers))) { fprintf (stderr, "error: memory allocation failed (numbers)."); return 1; } /* initial allocation for ln */ if (!(ln = calloc (LMAX, sizeof *ln))) { fprintf (stderr, "error: memory allocation failed (ln)."); return 1; } /* read each line from stdin - dynamicallly allocated */ while (fgets (ln, lmax, stdin) != NULL) { /* provide a fallback to read remainder of line if the line length exceeds lmax */ if (!(nl = strchr (ln, '\n'))) fixshortread (stdin, &ln, &lmax); else *nl = 0; char *p = ln; /* pointer for use with strtol */ char *ep = NULL; errno = 0; while (errno == 0) { /* parse/convert each number on stdin */ numbers[idx] = strtol (p, &ep, 10); /* note: overflow/underflow checks omitted */ /* if valid conversion to number */ if (errno == 0 && p != ep) { idx++; /* increment index */ if (!ep) break; /* check for end of str */ } /* skip delimiters/move pointer to next digit */ while (*ep && (*ep <= '0' || *ep >= '9')) ep++; if (*ep) p = ep; else break; /* reallocate numbers if idx = nmax */ if (idx == nmax) realloc_int (numbers, &nmax); } } /* free mem allocated by getline */ if (ln) free (ln); /* show values stored in array */ for (i = 0; i < idx; i++) printf (" numbers[%2u] %d\n", (unsigned int)i, numbers[i]); /* free mem allocate to numbers */ if (numbers) free (numbers); return 0; } /* reallocate character pointer memory */ char *realloc_char (char *sp, unsigned int *n) { char *tmp = realloc (sp, 2 * *n * sizeof *sp); #ifdef DEBUG printf ("\n reallocating %u to %u\n", *n, *n * 2); #endif if (!tmp) { fprintf (stderr, "Error: char pointer reallocation failure.\n"); exit (EXIT_FAILURE); } sp = tmp; memset (sp + *n, 0, *n * sizeof *sp); /* memset new ptrs 0 */ *n *= 2; return sp; } /* reallocate integer pointer memory */ int *realloc_int (int *sp, unsigned int *n) { int *tmp = realloc (sp, 2 * *n * sizeof *sp); #ifdef DEBUG printf ("\n reallocating %u to %u\n", *n, *n * 2); #endif if (!tmp) { fprintf (stderr, "Error: int pointer reallocation failure.\n"); exit (EXIT_FAILURE); } sp = tmp; memset (sp + *n, 0, *n * sizeof *sp); /* memset new ptrs 0 */ *n *= 2; return sp; } /* if fgets fails to read entire line, fix short read */ char *fixshortread (FILE *fp, char **s, unsigned int *n) { unsigned int i = 0; int c = 0; i = *n - 1; realloc_char (*s, n); do { c = fgetc (fp); (*s)[i] = c; i++; if (i == *n) realloc_char (*s, n); } while (c != '\n' && c != EOF); (*s)[i-1] = 0; return *s; } 

nota 1

niente di speciale sulla scelta della parola DEBUG (potrebbe essere stato DOG , ecc.), il punto da prendere è se si vuole includere / escludere condizionalmente il codice, si può semplicemente usare i flag del preprocessore per farlo. Devi solo aggiungere -Dflagname per passare il flagname al compilatore.

nota 2

puoi combinare le funzioni di riallocazione in una singola funzione void* che accetta un puntatore void come suo argomento insieme alla size del tipo da riallocare e restituisce un puntatore vuoto allo spazio riallocato - ma lasceremo che per una data successiva .

Questa è una soluzione funzionante
Un modo per farlo è usare strtok () e memorizzare i valori in un array. Idealmente, assegnato in modo dinamico.

  int main(int argc, char *argv[]) { int lst_size=100; int line_size=255; int lst[lst_size]; int count=0; char buff[line_size]; char * token=NULL; fgets (buff, line_size, stdin); //Get input 

Usare strtok passando ‘,’ e ‘;’ come deleminatore.

  token=strtok(buff, ";,"); lst[count++]=atoi(token); while(token=strtok(NULL, ";,")){ lst[count++]=atoi(token); } 

Infine devi rendere conto del doppio “;;” riducendo il conteggio di 1, perché atoi (token) restituirà 0 per quel caso e lo memorizzerà nell’ennesimo indice. Quale non vuoi

  count--; } 

Quello che potresti fare è leggere da stdin usando fgets o fgetc . Puoi anche usare getline () visto che stai leggendo da stdin.

Una volta letto nella riga puoi usare strtok () con il delimitatore per “;” per dividere la stringa in pezzi al punto e virgola. È ansible eseguire il ciclo fino a quando strok () è nullo o, in questo caso, ‘;’. Anche in C dovresti usare atoi () per convertire stringhe in numeri interi.

Per esempio:

 int length = 256; char* str = (char*)malloc(length); int err = getline(&str, &length, stdin); 

Vorrei leggere nel comando args, quindi analizzare usando il metodo della libreria strtok()

http://man7.org/linux/man-pages/man3/strtok.3.html

(La pagina Web a cui fa riferimento l’URL sopra riportato ha anche un esempio di codice su come usarlo).

Sono un po ‘arrugginito in C, ma potrebbe funzionare per te?

 char[1000] remainder; int first, second; fp = fopen("C:\\file.txt", "r"); // Error check this, probably. while (fgets(&remainder, 1000, fp) != null) { // Get a line. while (sscanf(remainder, "%d,%d;%s", first, second, remainder) != null) { // place first and second into a struct or something } } 

getchar_unlocked() è quello che stai cercando.

Ecco il codice:

 #include  inline int fastRead_int(int * x) { register int c = getchar_unlocked(); *x = 0; // clean stuff in front of + look for EOF for(; ((c<48 || c>57) && c != EOF); c = getchar_unlocked()); if(c == EOF) return 0; // build int for(; c>47 && c<58 ; c = getchar_unlocked()) { *x = (*x<<1) + (*x<<3) + c - 48; } return 1; } int main() { int x; while(fastRead_int(&x)) printf("%d ",x); return 0; } 

Per input 1;2;2;;3;;4;;;;;54;;;; il codice sopra produce 1 2 2 3 4 54 .

Garantisco, questa soluzione è molto più veloce di altre presentate in questo argomento. Non si usa solo getchar_unlocked() , ma usa anche register , inline e moltiplicando per 10 in modo complicato: (*x<<1) + (*x<<3) .

Vi auguro buona fortuna nel trovare una soluzione migliore.