2009-11-29 10 views
5

Ich weiß, in C Sie eine Zeichenfolge und die Anzahl der Zeichen wie unten, erklären kannBestimmung Länge von Char String in C -, wenn der Benutzer den Inhalt der Zeichenfolge eingibt

char mystring[50]; 

mit ‚50‘ die Anzahl der Zeichen zu sein .

Was ist jedoch richtige Prozedur, wenn der Benutzer den Inhalt der Zeichenfolge eingeben wird (via scanf ("% s", mystring);)? Verlasse ich es als,

char mystring[0]; 

Verlassen es als '0', da ich keine Ahnung habe, wie viele Zeichen der Benutzer eingeben?

Oder muss ich tun,

char mystring[400]; 

geben für den Benutzer zur Eingabe von 400 Zeichen auf?

Antwort

6

Sie haben das genaue Problem mit scanf() und% s gefunden - was passiert, wenn Sie nicht wissen, wie viel Input es gibt?

Wenn Sie versuchen, char mystring[0]; ausführen, wird Ihr Programm problemlos kompilieren. Aber du wirst immer segfault. Sie erstellen ein Array der Größe 0, wenn Sie also versuchen, etwas in das Array zu platzieren, werden Sie sofort die Grenzen für Ihre Zeichenfolge verlassen (da kein Speicher zugewiesen wurde) - was ein segfault ist.

Also, Punkt 1: Sie sollten immer eine Größe für Ihre Zeichenfolge zuordnen. Ich kann an sehr wenige Umstände denken (okay, keine), wo Sie lieber char mystring[0] statt char *mystring sagen würden.

Als nächstes, wenn Sie scanf verwenden, möchten Sie nie den "% s" -Spezifikator verwenden - weil dies keine Grenzen - Überprüfung der Größe der Zeichenfolge. so dass selbst wenn Sie haben:

char mystring[512]; 
scanf("%s", mystring); 

wenn der Benutzer mehr als 511 Zeichen eingibt (da die 512. \ 0 ist), werden Sie außerhalb der Grenzen Ihres Arrays gehen.Die Art und Weise, dies zu beheben ist:

scanf("%511s", mystring); 

Das alles ist zu sagen, dass C nicht über eine Einrichtung, um automatisch eine Zeichenfolge der Größe, wenn es mehr eingegeben wird, als Sie erwarten. Das müssen Sie manuell tun.

Eine Möglichkeit, damit umzugehen, ist die Verwendung von fgets().

Sie sagen konnte:

while (fgets(mystring, 512, stdin)) 
{ 
    /* process input */ 
} 

Sie dann sscanf() verwenden, können mystring

Versuchen Sie den obigen Code, mit einem String der Länge 5. Nach 4 Zeichen zu analysieren gelesen wurden, dass Code Schleifen erneut, um den Rest der Eingabe abzurufen. "Verarbeitung" könnte Code enthalten, um einen String einer größeren Größe zuzuordnen und dann die neueste Eingabe von fgets() anzufügen.

Der obige Code ist nicht perfekt - er würde Ihr Programm eine Schleife machen und eine unendliche Stringlänge verarbeiten, also möchten Sie vielleicht eine interne harte Grenze dafür haben (zB loop maximal 10 mal).

+0

t sollte hinzugefügt werden, dass% s Wörter, nicht ganze Strings liest. Da die scanf-Formatzeichenfolge Leerzeichen und Zeilenumbrüche als Trennzeichen verwendet.In diesem Fall verwenden Sie stattdessen% c (mit einer Feldbreite) oder fgets wie bereits erwähnt. Denken Sie bei% c mit einer Feldbreite daran, die gesamte Pufferzeichenfolge auf Null zu initialisieren. –

+0

Das Programm wird nicht immer segfault. In der Tat, wahrscheinlich nicht die meiste Zeit. Ihr Programm wird wahrscheinlich nur lautlos unterbrochen. Ist nicht C schön? :-) –

2

Der Benutzer kann immer mehr Zeichen eingeben, wodurch der Puffer überläuft (eine häufige Quelle von Sicherheitslücken). Sie können, geben jedoch eine „Feldbreite“ scanf, etwa so:

scanf("%50s", mystring); 

In diesem Fall Ihre Puffer 51 Zeichen lang sein sollten, für das 50 Zeichenfeld zu berücksichtigen plus dem Nullabschluss. Oder machen Sie Ihren Puffer 50 Zeichen und sagen scanf 49 ist die Breite.

+0

aber wenn ich die Zeichenfolge deklariere, sollte ich '0' oder eine große Zahl angeben? – HollerTrain

+1

Sie sollten in diesem Beispiel mindestens 51 angeben. (Die Länge + 1 für den Nullabschluss.) – Thanatos

+0

ok. also listet es nur als "0" auf, wenn die Zeichenkette nicht korrekt codiert wird? Mein Problem ist, dass ich keine Ahnung habe, wie viele Benutzer die richtige Methode eingeben und gleichzeitig die richtige Methode lernen möchten ... – HollerTrain

2

Es gibt eine Funktion namens ggets(), die nicht Teil der Standard-C-Bibliothek ist. Es ist eine ziemlich einfache Funktion. Es initialisiert ein char-Array mit malloc(). Es liest dann Zeichen von stdin ein Zeichen gleichzeitig. Es protokolliert, wie viele Zeichen gelesen wurden, und erweitert das char-Array mit realloc(), wenn der Speicherplatz knapp wird.

Es ist hier verfügbar: http://cbfalconer.home.att.net/download/index.htm

Ich würde vorschlagen, Sie den Code zu lesen und wieder selbst zu implementieren.

0

Die übliche Praxis in C ist so etwas wie GNU readline oder vielleicht verwenden NetBSD editline, aka libedit. (gleiche API, unterschiedliche Implementierung und Software-Lizenz.)

Für ein einfacheres oder Hausaufgaben-Programm, könnten Sie theoretisch eine Feldbreite geben scanf , aber eine normale Praxis ist fgets() zu einem Array fester Breite und dann sscanf() darauf laufen. Auf diese Weise haben Sie die Kontrolle über die Anzahl der gelesenen Zeilen.

0

Wenn der Benutzer beispielsweise seinen Vornamen eingibt, ist es nicht immer sicher, die Größe von 'mystring' als 35 Zeichen zu maximieren, da einige Leute wirklich lange Namen haben. Sie möchten nicht den Fall erreichen, in dem der Benutzer die angeforderten Informationen nicht vollständig eingeben kann. Der richtige Weg wäre, einen temporären Puffer mit einer sehr großen Größe zu haben, der alle möglichen Eingaben durch den Benutzer abdeckt. Sobald der Benutzer die Informationen eingibt und diese in den Puffer gespeichert werden, übertragen Sie die Zeichen aus dem Puffer an mystring, während Sie den gesamten zusätzlichen Speicherplatz am Ende des Puffers abschneiden. Sie können die Größe, die Sie für 'mystring' benötigen, genau angeben und Sie können nur so viel Speicherplatz für diesen Bereich reservieren und den Puffer verwerfen. Auf diese Weise verwenden Sie keine Zeichenfolge, die mehr Speicher für den Rest des Programms benötigt. Sie verwenden nur eine Zeichenfolge mit der benötigten Speichermenge.

+0

Sie müssten immer noch eine Art von Überprüfung durchführen, um sicherzustellen, dass das, was der Benutzer eingibt, nicht größer ist als der Puffer, der in den sehr seltenen Fällen zugewiesen wurde oder wenn jemand versucht, Ihr Programm auszunutzen. –

1

Dies ist cbfalconer Code (http://cbfalconer.home.att.net/download/index.htm) mit ein paar kleinere Modifikationen und in einer Datei zusammengestellt:

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

#define INITSIZE 112 /* power of 2 minus 16, helps malloc */ 
#define DELTASIZE (INITSIZE + 16) 

enum {OK = 0, NOMEM}; 

int fggets(char* *ln, FILE *f) 
{ 
    int  cursize, ch, ix; 
    char *buffer, *temp; 

    *ln = NULL; /* default */ 
    if (NULL == (buffer = malloc(INITSIZE))) return NOMEM; 
    cursize = INITSIZE; 

    ix = 0; 
    while ((EOF != (ch = getc(f))) && ('\n' != ch)) { 
     if (ix >= (cursize - 1)) { /* extend buffer */ 
     cursize += DELTASIZE; 
     if (NULL == (temp = realloc(buffer, (size_t)cursize))) { 
      /* ran out of memory, return partial line */ 
      buffer[ix] = '\0'; 
      *ln = buffer; 
      return NOMEM; 
     } 
     buffer = temp; 
     } 
     buffer[ix++] = ch; 
    } 
    if ((EOF == ch) && (0 == ix)) { 
     free(buffer); 
     return EOF; 
    } 

    buffer[ix] = '\0'; 
    if (NULL == (temp = realloc(buffer, (size_t)ix + 1))) { 
     *ln = buffer; /* without reducing it */ 
    } 
    else *ln = temp; 
    return OK; 
} /* fggets */ 
/* End of ggets.c */ 

int main(int argc, char **argv) 
{ 
    FILE *infile; 
    char *line; 
    int cnt; 

    //if (argc == 2) 
     //if ((infile = fopen(argv[1], "r"))) { 
     cnt = 0; 
     while (0 == fggets(&line, stdin)) { 
      fprintf(stderr, "%4d %4d\n", ++cnt, (int)strlen(line)); 
      (void)puts(line); 
      free(line); 
     } 
     return 0; 
     //} 
    //(void)puts("Usage: tggets filetodisplay"); 
    //return EXIT_FAILURE; 
} /* main */ 
/* END file tggets.c */ 

ich es getestet und es wird Sie immer, was Sie wollen.

+0

Im Grunde, um seinen ursprünglichen Code zu bekommen, kommentieren Sie die Kommentare und ersetzen stdin durch infile im fggets-Aufruf. –

Verwandte Themen