2013-06-26 12 views
6

ich habe:Max Stringlänge mit scanf -> ANSI C

#define MAX_STR_LEN 100 

und ich möchte in scanf Muster setzen, damit ich die Stringlänge steuern:

scanf("%100[^\n]s",sometext) 

Ich habe versucht:

scanf("%MAX_STR_LEN[^\n]s",sometext) 
scanf("%"MAX_STR_LEN"[^\n]s",sometext) 
scanf("%",MAX_STR_LEN,"[^\n]s",sometext) 

Und es hat nicht funktioniert. Ich möchte nur einen Pufferüberlauf vermeiden, da "sometext" mit malloc(MAX_STR_LEN) ...

zugeteilt wird Irgendwelche Ideen?

+0

Diskutiert [hier] (http://stackoverflow.com/questions/9457325/how-to-use-sscanf-correctly-and-safely). –

+0

Wie können Sie 'MAX_STR_LEN' innerhalb einer Formatzeichenfolge verwenden? – amulous

+0

@amulous, da es strenge ANSI C ist, habe ich nicht wirklich Zugriff auf echte String-Variable. Ich muss tun: char * somestring und dann malloc ... – tomdavies

Antwort

10

war ich mit irgendwelchen Lösungen nicht glücklich, so habe ich recherchiert weiter und entdeckte GNU GCC Makro stringification

, die als verwendet werden können: ähnlich

#define XSTR(A) STR(A) 
#define STR(A) #A 
#define MAX_STR_LEN 100 
scanf("%"XSTR(MAX_STR_LEN)"[^\n]s", sometext) 

Vielleicht bietet VS2010 etwas?

+5

Dies ist in der ANSI-C-Norm - es * sollte wirklich * mit VS 2010 arbeiten. (Angesichts der Idiosynkrasien von VS, bin ich mir nicht sicher.) – michaelb958

+2

Dies ist die richtige Antwort. An alle, die sagen: "scanf() nicht verwenden": Dies sollte ausreichen, um Pufferüberläufe wie von OP beschrieben zu vermeiden. – jimis

+0

Leider bricht dies die meisten IDE- und Kompilierzeit-Formatzeichenfolgeprüfung. –

7

Ich möchte nur Pufferüberlauf vermeiden

Dann nicht scanf() verwenden. Überhaupt.

Wenn Sie Textzeilen scannen, nicht #define MAX_STR entweder. Sie können LINE_MAX in <limits.h> haz (falls Sie POSIX-kompatible Systeme sind Targeting):

char buf[LINE_MAX]; 
fgets(buf, sizeof(buf), stdin); 

sollte es tun.

-3

Wie wäre es

scanf("%.*[^\n]s", MAX_STR_LEN, sometext) 
+0

Bitte, schön bitte, *** Nein! *** –

+3

Empfehlen Sie '*' Verwendung in 'scanf()' erneut zu lesen. Ich bin mir sicher, dass du deinen Beitrag ändern möchtest. Bei der Verwendung von printf() liest '*' eine Ganzzahl für die Größe, aber nicht für 'scanf()'. '*' bedeutet "Ein optionaler Start-Stern zeigt an, dass die Daten aus dem Stream gelesen, aber ignoriert werden sollen". Eine ganz andere Funktion. – chux

0

empfehlen fgets(buffer, sizeof(buffer), stdin) Ansatz.

Wenn Sie noch wollen scanf() verwenden, können Sie sein Format zur Laufzeit erstellen.

#define MAX_STR_LEN 100 
char format[2 + sizeof(size_t)*3 + 4 + 1]; // Ugly magiC# 
sprintf(format, " %%%zu[^\n]", (size_t) MAX_STR_LEN); 
scanf(format, sometext); 

oder MAX_STR_LEN Wieder definieren eine Zeichenfolge

#define MAX_STR_LEN "100" 
scanf(" %" MAX_STR_LEN "[^\n]", sometext); 

Noch die fgets() empfehlen zu sein.
Hinweis fgets() wird führende Leerzeichen und die nachfolgende \n in Ihrem Puffer, während " %[^\n]" wird nicht.
BTW: die nachlaufende s in Ihren Formaten ist nicht wahrscheinlich, was Sie denken.

4

Wie fast jeder sagt, ist es besser, fgets(..., stdin) zu verwenden, um dieses Problem zu behandeln.

In den folgenden Link, ich habe eine sichere und richtige Technik vorgeschlagen, die Sie lassen durch eine sicherere Methode scanf() zu ersetzen, durch eine solide Makro:

A macro that safely replaces scanf()

Das Makro Ich habe Text (die Arbeit mit kompatibel C99 Compilern) ist safe_scanf(), wie in dem folgende Programm gezeigt:

#include <stdio.h> 
#define safe_scanf(fmt, maxb, ...) { \ 
    char buffer[maxb+1] = { [maxb - 1] = '\0' }; \ 
    fgets(buffer, maxb+1, stdin); \ 
    if ((buffer[maxb - 1] != '\0') && (buffer[maxb - 1] != '\n')) \ 
     while(getchar() != '\n') \ 
      ; \ 
    sscanf(buffer, fmt, __VA_ARGS__); \ 
    } 

#define MAXBUFF 20  

int main(void) { 
    int x; float f;  
    safe_scanf("%d %g", MAXBUFF+1, &x, &f); 
    printf("Your input was: x == %d\t\t f == %g", x, f); 
    return 0; 
} 

möchten Sie Melodie haben der Wert MAXBUFF nach Ihren Bedürfnissen ...
Obwohl das Makro safe_scanf() ziemlich solide ist,
gibt es einige Schwächen in den Makro-Ansatz:
Fehlende für Parameter Typ-Überprüfung , fehlende "Return" -Werte (die sich kaum von der "echten" scanf()-Funktion unterscheiden, die einen int zurückgibt, mit wertvollen Informationen zur Fehlerüberprüfung) und so weiter.
Alle, die Probleme haben, Lösung, aber es Teil eines anderen Thema ...

Vielleicht ist die genaueste Lösung ist eine Funktion my_scanf() mit variabler Anzahl von Parametern zu definieren, durch die Berufung auf stdarg.h Bibliothek, Gelenk- einer Kombination von fgets() und vsscanf(). Hier haben Sie den Code:

#include <stdio.h> 
#include <stdarg.h> 

int my_scanf(const char* fmt, const unsigned int maxbuff, ...) { 
    va_list ptr; 
    int ret; 

    if (maxbuff <= 0) 
     return EOF; /* Bad size for buffer[] */ 

    char buffer[maxbuff+1]; 
    buffer[maxbuff-1] = '\0'; /* Quick buffer cleaning... */ 

    if (fgets(buffer, maxbuff+1, stdin) == NULL) 
     return EOF; /* Error detected */ 
    else { 
     if ((buffer[maxbuff-1] != '\n') && (buffer[maxbuff-1] != '\0')) 
      /* Condition logically equivalent to: 
        fgets() has not reached an '\n' 
      */ 
      while (getchar() != '\n') 
       ; /* "Flushing" stdin... */ 

     va_start(ptr, maxbuff); 
     ret = vsscanf(buffer, fmt, ptr); 
     va_end(ptr); 
     return ret; 
    }  
} 

#define MAXBUFF 20 
int main(void) { 
    int x; 
    float z; 
    int scanf_ret = my_scanf("%d %g", MAXBUFF, &x, &z); 
    printf("\nTest:\n x == %d\n z == %g\n scanfret == %d", x, z, scanf_ret); 
    getchar(); 
    return 0; 
} 

Die Funktion my_scanf() hat den Prototyp

int my_scanf(const char* fmt, const int maxbuff, ...); 

Es akzeptiert eine Format-String fmt, die auf die gleiche Art und Weise verhält sich wie jeder andere scanf() -ähnlichen tut.
Der zweite Parameter ist die maximale Anzahl von Zeichen, die effektiv von der Standardeingabe (Tastatur) akzeptiert werden.
Der Rückgabewert ist ein Int, die EOF ist, wenn maxbuff keinen Sinn hat, oder auch einige Eingabefehler passiert. Wenn ein nicht negativer Wert zurückgegeben wird, ist dies der gleiche Wert, der von den Standardfunktionen sscanf() oder zurückgegeben würde.

Innerhalb der Funktion wird maxbuff in 1 inkrementiert, weil fgets() Platz für ein zusätzliches '\ 0'-Zeichen bietet.
Nicht positive Werte von maxbuff werden sofort verworfen.
fgets() wird eine Zeichenfolge gelesen wird gelesen von stdin (Tastatur) mit Platz für höchstens maxbuff Zeichen, einschließlich '\ n'.
Wenn der Benutzer einen sehr langen String eingegeben hat, wird er gekürzt, und es ist eine Art "Flush" -Mechanismus erforderlich, um alle Zeichen zum nächsten '\ n' (ENTER) zu verwerfen. Wenn nicht, könnte das nächste Tastaturlesen ältere Zeichen haben, die überhaupt nicht erwünscht sind.
Die Bedingung für "Spülen" ist, dass fgets() '\ n' nach dem Lesen stdin nicht erreicht hat.
Dies ist der Fall, wenn und nur wenn buffer[maxbuff - 1] nicht gleich '\ 0' oder '\ n' ist.
(Check it!)
Schließlich ist eine sachgemäße Kombination aus stdarg.hMakros und der Funktion vsscanf() verwendet wird, um die variable Parameterliste zu verarbeiten.