2016-04-26 11 views
4

Ich möchte eine bestimmte Zeile analysieren. Also, ich habe das folgende Stück Code, um die Logik zu testen, aber ich wahrscheinlich verstehe etwas falsch:mangelndes Verständnis über sscanf Verwendung

typedef struct vers 
{ 
    char tu8UVersion[5]; 
    char tu8UCommit[32]; 
}tst_prg_versions; 

int main(int argc, char **argv) 
{ 
    tst_prg_versions lstVer; 
    char buf1[32]; 
    char buf2[32]; 

    char str[] = "BOARD-VERS-v1.0.0-git+9abc12345a"; 
    sscanf(str, "BOARD-VERS-v%5s-git+%s", lstVer.tu8UVersion, lstVer.tu8UCommit); 
    printf("vers='%s'\n", lstVer.tu8UVersion); 
    printf("commit='%s'\n", lstVer.tu8UCommit); 

    sscanf(str, "BOARD-VERS-v%5s-git+%s", buf1, buf2); 
    printf("vers='%s'\n", buf1); 
    printf("commit='%s'\n", buf2); 
    return 0; 
} 

Einmal ausgeführt kehrt:

vers='1.0.09abc12345a' 
commit='9abc12345a' 
vers='1.0.0' 
commit='9abc12345a 

Warum der erste vers gleich zu 1.0.09abc12345a ist und nicht 1.0.0?

+0

@ user3121023 Vielen Dank für Ihre Antwort! Um diese Art von Problem zu vermeiden, ist es besser, Strukturvariablen als char * zu deklarieren? sscanf ist verantwortlich für die Speicherzuweisung – ogs

+2

'scanf' reserviert keinen Speicher für Sie; Sie müssen ausreichend große Puffer bereitstellen. –

+0

'printf (" vers = '% s' \ n ", lstVer.tu8UVersion);' ist nicht nur fehlerhaft, es ist undefiniertes Verhalten. – sjsam

Antwort

2

Ihr Problem wurde bereits in den Kommentaren identifiziert: Sie lassen keinen Platz für das abschließende Nullzeichen und die beiden Strings laufen zusammen.

Wenn Sie eine Version scannen möchten, deren Größe Sie vorher nicht kennen, können Sie die Zeichen auf Dezimalstellen und Punkte mit %[.-9] oder auf alles außer einem Bindestrich mit %[^-] beschränken. (Das %[...] Format ist wie %s, außer dass Sie eine Liste gültiger Zeichen in den Klammern angeben müssen. Ein Caret als erster Buchstabe bedeutet, dass die Zeichenfolge aus Zeichen besteht, die nicht aufgeführt sind. Mit anderen Worten, %s ist die Abkürzung für %[^ \t\n]

Wenn Sie eine Zeichenfolge zu scannen, sollten Sie den Rückgabewert von sscanf testen um sicher zu sein, dass alle Elemente korrekt gescannt wurden und gültige Werte enthalten

Hier ist eine Variante, die Versionsnummern von bis zu 11 Buchstaben abtastet.:

#include <stdlib.h> 
#include <stdio.h> 

typedef struct vers 
{ 
    char tu8UVersion[12]; 
    char tu8UCommit[32]; 
} tst_prg_versions; 

int main(int argc, char **argv) 
{ 
    tst_prg_versions lstVer; 

    char str[] = "BOARD-VERS-v1.0.0-git+9abc12345a"; 
    int n; 

    n = sscanf(str, "BOARD-VERS-v%11[^-]-git+%s", 
     lstVer.tu8UVersion, lstVer.tu8UCommit); 

    if (n == 2) { 
     printf("vers='%s'\n", lstVer.tu8UVersion); 
     printf("commit='%s'\n", lstVer.tu8UCommit); 
    } else { 
     puts("Parse error."); 
    } 

    return 0; 
} 
+0

Vielen Dank für Ihre Antwort !! Ich werde diese Informationen sicherlich in Zukunft wieder verwenden – ogs

3

Die erste liest tatsächlich 1.0.0! Das Problem ist jedoch, dass tu8UVersion nicht null-terminiert ist, daher druckt printf (nicht sscanf) über das Feld (dies ist jedoch ein undefiniertes Verhalten, wie von sjsam bemerkt) - dem unmittelbar tu8UCommit folgt (muss nicht unbedingt) so, da könnte es aus Ausrichtungsgründen immer noch ein paar Füllbytes dazwischen geben!).

Sie müssen entweder maximal 5 Zeichen (%.5s im printf-Formatzeichenfolge) drucken oder einen Platz zum Beenden der tu8UVersion mit 0 lassen, wie bereits in einem Kommentar vorgeschlagen.

Ähnliches könnte mit Ihren Puffern auch passiert sein. Sie haben das Glück, dass sie anscheinend bereits auf 0 initialisiert wurden (wahrscheinlich wegen der kompilierten Debug-Version), was wiederum nicht unbedingt passieren muss. Mit Pech hätten Sie also den Rest von buf1 drucken können (nachdem Sie im Müll liegen gelassen wurden) und sogar darüber hinaus.

3

Warum ist der erste Vers gleich 1.0.09abc12345a und nicht 1.0.0?

Denken Sie daran, dass Sie

typedef struct vers 
{ 
    char tu8UVersion[5]; 
    char tu8UCommit[32]; 
}tst_prg_versions; 

habe ich denke, es gibt eine gute Chance, den Speicher für tu8UVersion und tu8UCommit angrenzt. Da Sie nicht nullterminiertentu8UVersion haben, wenn Sie tun:

printf("vers='%s'\n", lstVer.tu8UVersion); 

es auf tu8UCommit in Druck geht und es stoppt, weil tu8UCommit null beendet ist.

Während sscanf die vernünftigste Lösung hier scheinen könnte man auch einige Formatierungen vorstellen:

char tu8UVersion[32]; 
    /* version number can't get too big. 
    * So the first step is do allocated a 
    * reasonably - but not too - big size for it. 
    * So that you can be sure there are few empty bytes at the end. 
    */ 

und dann eine Funktion verwenden, um einen String zu sanieren:

char* sanitized(char* ptr) 
{ 
    if(ptr[strlen(ptr)]!='\0') // include string.h for strlen 
    ptr[strlen(ptr)]='\0'; 
    return ptr; 
} 

und drucken Sie es mögen:

printf("vers='%s'\n", sanitized(lstVer.tu8UVersion));