2009-06-30 12 views
1

Ich möchte eine Variablen, die Vektoren aus weißem Raum abgegrenzten Textdatei und der Stolperstein scannen (alles zu oft für mich) ist Mangel an Eleganz.Scannen Variable Anzahl von 'Feldern' aus einer Textdatei

Derzeit mein Scancode die Größe des Vektors als erstes Element in der Datei erfordert abgrenzt:

7 : 1 3 6 8 -9 .123 1.1 

Was ich stört, weil die ‚7‘ durch Überprüfen des Leerraumes ermittelt werden kann.

Ich habe verschiedene Formen von fscanf(), strtok() usw. ausprobiert, aber alle scheinen rohe Gewalt zu sein. Ohne auf Lex/Yacc (nicht verfügbar) zurückgreifen zu können, könnte jemand etwas eleganteres vorschlagen als das Folgende?

typedef struct vector_tag 
{ 
    int Length; 
    double * value; 
} vector; 

vector v; 

char buf[BIG_ENOUGH], key[BIG_ENOUGH], val[BIG_ENOUGH]; 

void scan_vector(FILE * fh) 
{ 
    int i, length; 
    double * data; 
    char * tok; 

    do { 
     if (feof(fh)) return; 
     fgets(buf, sizeof buf, fh);  
    } while (2 != sscanf(buf,"%[^:]:%[^\n\r]",key,val)); 

    length  = 
    v.Length = strtol(key,NULL,10); 
    data  = 
    v.value  = malloc(length * sizeof(double)); 

    tok = strtok(val, " "); /* I'd prefer tokenizing on whitespace */ 
    for (i = 0; i++ < v.Length;) { 
     * data++ = strtod(tok,NULL);; 
     tok = strtok(NULL, " "); /* Again, tokenize on whitespace */ 
    } 
} 

Lösung: Dank der überprüften Antwort, ich implementiert:

static int scan_vector(FILE * fh, vector * v) 
{ 
    if (1 == fscanf(fh,"%d:",& v->length)) 
    { 
     int   i; 

     v->value = malloc(v->Length * sizeof(double)); 

     assert (NULL != v->value); 

     for (i = 0; i < v->Length; i++) 
     { 
      if (fscanf(fh,"%lf",v->value + i) != 1) return(0); 
     } 
     return(1); 
    } 
    return(0); 
} /* scan_vector() */ 
+0

Wenn Sie steuern können, wie die Eingabedaten formatiert werden, können Sie versuchen, auf die Vektorlänge zu verzichten. Dadurch wird die spezielle Hülle für die Vektorlänge entfernt und Sie gelangen direkt in das Tokenizing. Wenn Sie Ihre längste Vektorzeichenfolge zuverlässig kennen, können Sie sie in ein Array zerlegen und dann Ihren Vektorwert basierend auf der Anzahl der in Token angegebenen Werte zuweisen. Der Sscanf lässt mich zusammenzucken, aber jeder für sich. – Erik

Antwort

1

was mit etwas nicht in Ordnung ist wie:

int scan_vector(FILE *fh) 
{ 
    char pad[2]; 
    int i; 
    if (fscanf(fh,"%d %1[:]", &v.Length, &pad) != 2) 
     return -1; 
    v.value = malloc(v.Length * sizeof(double)); 
    for (i = 0; i < v.Length; i++) { 
     if (fscanf(fh, "%lf", &v.value[i]) != 1) 
      return -1; 
    } 
    return 0; 
} 

Dieser versucht, den Vektor mit scanf zu lesen, und gibt einen -1 Fehlercode, wenn es ein Problem gab.

Wenn Sie etwas viel komplexer als das tun möchten, sind Sie wahrscheinlich besser dran mit Flex mindestens (wenn nicht auch Bison).

+0

Einfache Markierungen. Es war direkt vor mir, danke für das Durchdringen meiner Frage. – Jamie

+0

(Abgesehen von der 'scanf' -> 'fscanf' Tippfehler) +1 – Jamie

0

Wenn Sie realloc() Sie können immer mehr Speicher fragen, ob Sie mit dem anfänglichen malloc() nicht genug zuordnen. Eine gemeinsame Strategie besteht darin, eine beliebige n Elemente zu starten. Wann immer Sie ausgehen, verdoppeln Sie dann den Platz n und ändern die Größe des Puffers.

Alternativ können Sie eine verknüpfte Liste anstelle eines Arrays verwenden. Verknüpfte Listen verarbeiten Einfügungen und hängen besser an als Arrays, Sie geben jedoch die Möglichkeit auf, auf Elemente nach Index zuzugreifen.

+0

Es ist mehr in das Scannen der Datei als Speicherverwaltung, die ich suche; obwohl ich natürlich einen temporären Platz zum Scannen brauchen würde, wenn ich nicht a priori wüsste, wie viele Variablen ich scannen müsste. – Jamie

+0

OK, ich werde aufhören, verschämt zu sein. Ich habe angedeutet (oops), dass Sie die Zahlen nacheinander lesen und nach Bedarf realloc(). Damit können Sie jede Zeile in einem Durchgang lesen, ohne die Längenmarkierung zu benötigen. –

0

Wie groß können Ihre Vektoren sein?
Ein Weg zu gehen,

  • eine Linie in der lokalen Puffer scannen (dies ist ein Vektordatum nehme ich an)
  • Scan über diesen lokalen Puffer den weißen Raum delimiters (ganz einfach zu Code) zu zählen
  • dann machen die korrekte Zuordnung
  • und initialisieren den Vektor

Wie Sie die Dimension beobachten, '7' muss nicht Teil der Eingabe sein.
Sie benötigen nur einen lokalen Puffer, der groß genug für die längste mögliche Leitung ist.
Und einige Fehlerbehandlung für sie :-)

0

Hier ist eine Version, die nicht den Vektor der Größe als erster Eintrag in der Datei benötigt:

aus Leistungsgründen und Faulheit
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <ctype.h> 

#define LINE_MAX 256 
#define VECTOR_SIZE_MAX 32 

struct vector 
{ 
    size_t size; 
    double *values; 
}; 

// returns 1 on error 
_Bool scan_vector(FILE *file, struct vector *v) 
{ 
    char buffer[LINE_MAX]; 
    if(!fgets(buffer, sizeof(buffer), file)) 
     return 1; 

    double values[VECTOR_SIZE_MAX]; 

    size_t size = 0; 
    errno = 0; 

    for(char *head = buffer, *tail = NULL;; ++size, head = tail) 
    { 
     while(isspace(*head)) ++head; 
     if(!*head) break; 

     if(size >= VECTOR_SIZE_MAX) 
      return 1; 

     values[size] = strtod(head, &tail); 
     if(errno || head == tail) 
      return 1; 
    } 

    v->size = size; 
    v->values = malloc(sizeof(double) * size); 
    if(!v->values) return 1; 

    memcpy(v->values, values, sizeof(double) * size); 

    return 0; 
} 

int main(void) 
{ 
    struct vector v; 
    while(!scan_vector(stdin, &v)) 
    { 
     printf("value count: %u\n", (unsigned)v.size); 
     free(v.values); 
    } 

    return 0; 
} 

Die maximale Zeilengröße und Anzahl der Einträge festgelegt sind.

Verwandte Themen