7 Stimmen

Wie man einen String in ein Array von Integer in C tokenisiert?

Hat jemand etwas über das Lesen einer fortlaufenden Nummer aus einer Textdatei pro Zeile und das Parsen in ein Array in C?

Was ich in einer Datei habe:

12 3 45 6 7 8
3 5 6 7
7 0 -1 4 5

Was ich in meinem Programm haben möchte:

array1[] = {12, 3, 45, 6, 7, 8};
array2[] = {3, 5, 6, 7};
array3[] = {7, 0, -1, 4, 5};

Ich habe bereits verschiedene Möglichkeiten ausprobiert, um es zu lesen, aber das eigentliche Problem tritt nur dann auf, wenn ich es pro Zeile tokenisieren möchte. Danke.

0 Stimmen

Danke für alles, aber jetzt erkenne ich, dass das Problem darin besteht, zu tokenisieren.

14voto

ChrisF Punkte 130622

Der folgende Code liest eine Datei zeilenweise ein

char line[80]
FILE* fp = fopen("data.txt","r");
while(fgets(line,1,fp) != null)
{
   // do something
}
fclose(fp);

Sie können dann die Eingabe mit strtok() und sscanf() tokenisieren, um den Text in Zahlen umzuwandeln.

Von der MSDN-Seite für sscanf:

Jede dieser Funktionen [sscanf und swscanf] gibt die Anzahl der erfolgreich konvertierten und zugewiesenen Felder zurück; der Rückgabewert enthält nicht die Felder, die gelesen, jedoch nicht zugewiesen wurden. Ein Rückgabewert von 0 gibt an, dass keine Felder zugewiesen wurden. Der Rückgabewert ist EOF bei einem Fehler oder wenn das Ende des Strings vor der ersten Konvertierung erreicht wird.

Der folgende Code wandelt den String in ein Array von Ganzzahlen um. Natürlich benötigen Sie für ein Array variabler Länge eine Liste oder müssen die Eingabe zweimal scannen, um die Länge des Arrays vor dem Parsen tatsächlich zu bestimmen.

char tokenstring[] = "12 23 3 4 5";
char seps[] = " ";
char* token;
int var;
int input[5];
int i = 0;

token = strtok (tokenstring, seps);
while (token != NULL)
{
    sscanf (token, "%d", &var);
    input[i++] = var;

    token = strtok (NULL, seps);
}

Durch das Hinzufügen von:

char seps[]   = " ,\t\n";

wird die Eingabe flexibler.

Ich musste eine Suche durchführen, um mich an die Syntax zu erinnern - ich habe es hier in der MSDN gefunden

0 Stimmen

Könnten Sie mir zeigen, wie man sscanf() für eine unbekannte Anzahl von Zahlen pro Zeile verwendet, um sie in ein Array zu analysieren?

0 Stimmen

@ChrisF die Idee, die ich hier gefunden habe, ist, einen int-Variable für den vorübergehenden Gebrauch zu verwenden. Übrigens vielen Dank.

0 Stimmen

@ChrisF Gibt es eine Möglichkeit, damit umzugehen 1,2,,4?

2voto

Chris Lutz Punkte 69879

Was ich tun würde, ist eine Funktion wie diese zu erstellen:

size_t read_em(FILE *f, int **a);

Weise in der Funktion etwas Speicher dem Zeiger *a zu, dann beginne Zahlen von der f zu lesen und speichere sie in *a. Wenn du ein Zeichen für einen Zeilenumbruch siehst, gib einfach die Anzahl der Elemente zurück, die du in *a gespeichert hast. Dann rufe sie so auf:

int *a = NULL;
FILE *f = fopen("Somefile.txt", "r");
size_t len = read_em(f, &a);
// jetzt ist a ein Array, und len ist die Anzahl der Elemente in diesem Array

Nützliche Funktionen:

  • malloc() um ein Array zuzuweisen.
  • realloc() um ein mit malloc() zugewiesenes Array zu erweitern
  • fgets() um eine Zeile Text zu lesen (oder so viel wie gespeichert werden kann).
  • sscanf() um Daten aus einem String zu lesen (wie z.B. ein String, der von fgets() zurückgegeben wird) in andere Variablen (wie z.B. ein int Array, das von malloc() erstellt wurde - Hinweis Hinweis)

2voto

fvu Punkte 31678

Ich würde dringend davon abraten, sscanf und friends zu verwenden, wenn die Anzahl der Felder variabel ist. Verwenden Sie strtok und atoi. Stellen Sie sicher, dass Sie die strtok-Manpage gut lesen, viele Programmierer finden die Syntax am Anfang etwas überraschend. Beachten Sie auch, dass strtok den Eingabestring ändern wird, daher möchten Sie vielleicht an einer Kopie arbeiten.

1 Stimmen

Noch besser: verwende strtol(), dann brauchst du strtok() nicht.

1voto

paxdiablo Punkte 809679

Der folgende Code könnte das sein, wonach Sie suchen. Hoffentlich benötigen Sie nicht zu viel Erklärung, da Kommentare vorhanden sind, aber wenn Sie Fragen haben, zögern Sie nicht, zu fragen.

Es verwendet im Grunde eine Schleife mit fgets, um jede Zeile einzulesen und strtok, um diese Zeile in Felder zu trennen. Es konstruiert eine verkettete Liste von Integer-Arrays, die die tatsächlichen Daten enthalten - Sie können die Verwendung dieser verketteten Liste im Code am Ende sehen, der die Tabelle ausgibt.

Es bietet auch eine Möglichkeit, beliebig große Zeilen in der Eingabedatei ohne Pufferüberlauf zu verarbeiten (abhängig von Speicherbeschränkungen). Beachten Sie, dass das strtok nur ein Leerzeichen zwischen jedem Feld auf der Zeile erwartet, obwohl dies so umprogrammiert werden könnte, dass es auch mehrere Leerzeichen oder sogar beliebige Mengen an Leerzeichen handhabt. Ich habe diesen Teil einfach gehalten, da der Code bereits ein wenig groß war :-)

Die Funktion atoi wird verwendet, um das einzelne Wort auf jeder Zeile in Integer umzuwandeln. Wenn Sie eine Fehlerüberprüfung für diese wünschen, würde ich Ihre eigene Variante aufrufen, die auch überprüft, ob alle Zeichen im Wort numerisch sind.

Bei Verwendung Ihrer Eingabedatei:

12 3 45 6 7 8
3 5 6 7
7 0 -1 4 5

erzeugt es eine Ausgabe in etwa wie folgt:

0x97b5170, Größe = 6:
   12 3 45 6 7 8
0x97b51d0, Größe = 4:
   3 5 6 7
0x97b51e0, Größe = 5:
   7 0 -1 4 5

Hier ist der Code, der diese Ausgabe produziert hat:

#include 
#include 
#include 
#include 

// Hierbei handelt es sich um die verkettete Liste von Integer-Arrays.

typedef struct _tIntArray {
    int size;
    int *array;
    struct _tIntArray *next;
} tIntArray;
static tIntArray *erste = NULL;
static tIntArray *letzte = NULL;

// Füge eine Zeile von Ganzzahlen als Knoten hinzu.

static int addNode (char *str) {
    tIntArray *curr;  // Zeiger für neues Integer-Array.
    char *word;       // Wort innerhalb des Strings.
    char *tmpStr;     // Temporäre Kopie des Puffers.
    int fldCnt;       // Feldanzahl für Zeile.
    int i;

    // Zähle die Anzahl der Felder.

    if ((tmpStr = strdup (str)) == NULL) {
        printf ("Kann keinen duplizierten String erstellen (%d).\n", errno);
        return 1;
    }
    fldCnt = 0;
    for (word = strtok (tmpStr, " "); word; word = strtok (NULL, " "))
        fldCnt++;
    free (tmpStr);

    // Erstelle neuen Knoten für die verkettete Liste.

    if ((curr = malloc (sizeof (tIntArray))) == NULL) {
        printf ("Kann keinen Integer-Array-Knoten zuweisen (%d).\n", errno);
        return 1;
    }

    curr->size = fldCnt;
    if ((curr->array = malloc (fldCnt * sizeof (int))) == NULL) {
        printf ("Kann keinen Integer-Array zuweisen (%d).\n", errno);
        free (curr);
        return 1;
    }
    curr->next = NULL;

    for (i = 0, word = strtok (str, " "); word; word = strtok (NULL, " "))
        curr->array[i++] = atoi (word);

    if (letzte == NULL)
        erste = letzte = curr;
    else {
        letzte->next = curr;
        letzte = curr;
    }

    return 0;
}

int main(void) {
    int lineSz;       // Aktuelle Zeilengröße.
    char *buff;       // Puffer zum Halten der Zeile.
    FILE *fin;        // Datei-Handle für Eingabedatei.
    long offset;      // Offset zum Neuzuweisen des Zeilenpuffers.
    tIntArray *curr;  // Zeiger für neues Integer-Array.
    int i;

    // Öffne Datei.

    if ((fin = fopen ("qq.in", "r")) == NULL) {
        printf ("Kann qq.in nicht öffnen, errno = %d\n", errno);
        return 1;
    }

    // Weise Anfangspuffer zu.

    lineSz = 2;
    if ((buff = malloc (lineSz+1)) == NULL) {
        printf ("Kann keinen anfänglichen Speicher zuweisen, errno = %d.\n", errno);
        return 1;
    }

    // Schleife unbegrenzt.

    while (1) {
        // Speichere Offset, falls erforderlich.

        offset = ftell (fin);

        // Hole Zeile, beende bei Dateiende.

        if (fgets (buff, lineSz, fin) == NULL)
            break;

        // Wenn kein Zeilenumbruch vorhanden, gehe davon aus, dass der Puffer nicht groß genug war.

        if (buff[strlen(buff)-1] != '\n') {
            // Hole größeren Puffer, setze zurück zum Anfang der Zeile und versuche erneut.

            free (buff);
            lineSz += 3;
            if ((buff = malloc (lineSz+1)) == NULL) {
                printf ("Kann keinen zusätzlichen Speicher zuweisen, errno = %d.\n", errno);
                return 1;
            }
            if (fseek (fin, offset, SEEK_SET) != 0) {
                printf ("Kann nicht suchen, errno = %d.\n", errno);
                return 1;
            }
            continue;
        }

        // Entferne Zeilenumbruch und verarbeite.

        buff[strlen(buff)-1] = '\0';
        if (addNode (buff) != 0)
            return 1;
    }

    // Tabelle für Debugging ausgeben.

    for (curr = erste; curr != NULL; curr = curr->next) {
        printf ("%p, Größe = %d:\n  ", curr, curr->size);
        for (i = 0; i < curr->size; i++)
            printf (" %d", curr->array[i]);
        printf ("\n");
    }

    // Ressourcen freigeben und beenden.

    free (buff);
    fclose (fin);
    return 0;
}

0voto

phoebus Punkte 14081

Hat Ihre Datei eine bestimmte Anzahl von Zeilen oder müssen Sie in der Lage sein, eine beliebige Anzahl in zufällige Arrays zu lesen?

Hier ist der Code, um eine Datei zeilenweise einzulesen.

#include 

int main()
{
    char *inname = "test.txt";
    FILE *infile;
    char line_buffer[BUFSIZ];

    infile = fopen(inname, "r");
    if (!infile) {
        printf("Konnte die Datei %s nicht zum Lesen öffnen.\n", inname);
        return 0;
    }

    while (fgets(line_buffer, sizeof(line_buffer), infile)) {
        // Zeile verarbeiten
    }

    return 0;
}

Sie können sscanf oder eine beliebige Anzahl von Tokenisierungs-/Konvertierungsfunktionen verwenden, um die Zahlen zu extrahieren. BUFSIZ ist eine gute Konstante aus stdio.h, die darauf ausgelegt ist, die E/A auf einem Zielsystem effizient zu gestalten.

0 Stimmen

Warum speichern Sie eine line_number als Typ char? Möchten Sie von Natur aus auf 127 Zeilen begrenzt sein?

0 Stimmen

Könnten Sie mir zeigen, wie man die Zahlen mithilfe von sscanf extrahiert?

0 Stimmen

Die Zeilennummer sollte dort nicht einmal sein, sie ist ein Überbleibsel aus einem anderen Code...jetzt entfernt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X