Zahlen aus Datei parsen mit purem ANSI-C


  • Gesperrt

    Vielleicht habt ihr eine Idee, ich habe folgende Textdatei "hallo.txt":

    45 hallo du daa 67
    89 moin 1010
    -15 lang weil ig +16
    
    

    (die Zeile 4 ist leer)
    Und es soll Folgendes ausgegeben werden:

    |||45|67| hallo du daa |||
    |||89|1010| moin |||
    |||-15|16| lang weil ig |||
    

    Code:

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <string.h>
    
    const char *const FILEN = "hallo.txt";
    typedef struct dataf
    {
        int32_t first;
        int32_t second;
        char data[1000];
    } sDataf;
    sDataf array[10];
    int32_t size = 0;
    
    int lineLen(char *ca)
    {
        int len = -1;
        while (ca[++len] != '\n' && ca[len] != '\0')
            ;
        return len;
    }
    
    int firstIdxOf(char *ca, int len, char c)
    {
        int idx = -1;
        while (++idx < len && ca[idx] != c)
            ;
        if (idx == len)
            return -1;
        return idx;
    }
    
    int lastIdxOf(char *ca, int len, char c)
    {
        int idx = len;
        while (--idx >= 0 && ca[idx] != c)
            ;
        return idx;
    }
    
    void readFILEN()
    {
        FILE *f = fopen(FILEN, "r");
        char line[1000];
        char buf[1000];
        int len, space1, space2;
        sDataf *df;
        while (fgets(line, sizeof line, f) != NULL)
        {
            len = lineLen(line);
            space1 = firstIdxOf(line, len, ' ');
            space2 = lastIdxOf(line, len, ' ');
            printf("%d %d %d\n", len, space1, space2);
            if (len == 0 || space1 == -1 || space2 == -1 || space2 <= space1)
                break;
    
            df = &array[size++];
    
            memcpy(buf, &line[0], space1);
            buf[space1] = '\0';
            sscanf(buf, "%d", &df->first);
    
            memcpy(df->data, &line[space1], space2 - space1 + 1);
            df->data[space2 - space1 + 1] = '\0';
    
            memcpy(buf, &line[space2 + 1], len - space2);
            buf[len - space2] = '\0';
            sscanf(buf, "%d", &df->second);
        }
        fclose(f);
    }
    
    int main(int argc, char **args)
    {
        int i;
        sDataf *df;
        readFILEN();
        for (i = 0; i < size; i++)
        {
            df = &array[i];
            printf("|||%d|%d|%s|||\n", df->first, df->second, df->data);
        }
        return EXIT_SUCCESS;
    }
    

    Wie könnte man das jetzt weniger fehleranfällig/also robuster schreiben, wenn eine Zeile keine Zahlen enthält, also ein ungültiges Format besitzt? Vielen Dank für Verbesserungsvorschläge



  • mit purem ANSI-C

    Dann muss man die Frage natürlich unter C++ stellen ...



  • Lösung:

    #include <cstdio>
    #include <cstdlib>
    #include <cctype>
    #include <cstring>
    
    int main(){
        FILE* fp = fopen("test.txt", "r");
    
        if(fp){
            char line[1024];
            char string[1024];
    
            while(fgets(line, sizeof(line) - 1, fp)){
                char* p = line, * sp = string;
    
                if(*p == '\n')
                    continue;
    
                std::memset(string, '\0', 1024);
                printf("%s", "|||");
    
                while(*p){
                    if(isdigit(*p) || ((*p == '-' || *p == '+') && isdigit(*(p + 1))))
                        printf("%ld|", strtol(p, &p, 10));
    
                   else if(*p == '\n')
                        ++p;
    
                    else
                        *sp++ = *p++;
                }
    
                *sp = 0;
                printf("%s|||\n", string);
            }
        }
    }
    

  • Gesperrt

    Das gefällt mir sehr gut @eigenartig :

    #include <stdio.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <string.h>
    
    const char *const FILEN = "hallo.txt";
    typedef struct dataf
    {
        int first;
        int second;
        char data[1000];
    } sDataf;
    sDataf array[10];
    int size = 0;
    
    int main(int argc, char **args)
    {
        FILE *fp = fopen(FILEN, "r");
    
        if (fp)
        {
            char line[1024];
            char string[1024];
    
            while (fgets(line, sizeof(line) - 1, fp))
            {
                char *p = line, *sp = string;
    
                if (*p == '\n')
                    continue;
    
                memset(string, '\0', 1024);
                printf("%s", "|||");
    
                for (; *p; ++p)
                {
                    if (isdigit(*p) || *p == '-' || *p == '+')
                        printf("%ld|", strtol(p, &p, 10));
    
                    else
                        *sp++ = *p;
                }
    
                *sp = 0;
                printf(" %s |||\n", string);
            }
        }
    
        return EXIT_SUCCESS;
    }
    

    isdigit() kannte ich noch nicht.
    Sorry, dass das Thema unter C++ gelandet ist. 😞



  • @EinNutzer0 sagte in Zahlen aus Datei parsen mit purem ANSI-C:

    |||45|67| hallo du daa |||
    |||89|1010| moin |||
    |||-15|16| lang weil ig |||

    Sind es immer zwei integer in einer Zeile? Sollen die Zahlen in der Ausgabe aufsteigend sortiert sein oder ist das nur Zufall?



  • Dieser Beitrag wurde gelöscht!

  • Gesperrt

    @Swordfish sagte in Zahlen aus Datei parsen mit purem ANSI-C:

    Sollen die Zahlen in der Ausgabe aufsteigend sortiert sein oder ist das nur Zufall?

    Das ist nur Zufall.

    @Swordfish sagte in Zahlen aus Datei parsen mit purem ANSI-C:

    Sind es immer zwei integer in einer Zeile?

    Wenn eine Zeile gültig ist, dann besteht sie aus "%d %s %d\n", wobei s aber auch Leerzeichen enthalten kann...
    Und wenn eine Zeile nicht gültig ist, so sollte diese ignoriert werden...

    Und das alles in dem "Ur-C"!



  • @eigenartig
    Das ist komplett laienhaft und hat nichts mit ANSI C zu tun.



  • @EinNutzer0 sagte in Zahlen aus Datei parsen mit purem ANSI-C:

    FILE *f=...
    char *p,b[100];
    while(fgets(b,100,f))
        {
            int n,i;
            if( 1==sscanf(b,"%d%n",&i,&n) )
            if( p=strpbrk(b+n,"0123456789") )
            {
                if(p[-1]=='+'||p[-1]=='-') --p;
                p[-1]=0;
                printf("|||%d|%d|%s |||\n",i,atoi(p),b+n);
            }
        }
    

    Wenn es ANSI C sein soll, lass es den Compiler absegnen:

    gcc -Wall -ansi
    

    Wenn es strikt konformes ANSI C sein soll, lass es den Compiler absegnen:

    gcc -Wall -ansi -pedantic
    

    Wenn du eine Programmiersprache kennst, bei der es mit Standardmitteln kürzer geht, sag Bescheid.


  • Gesperrt

    @Wutz sagte in Zahlen aus Datei parsen mit purem ANSI-C:

    sag Bescheid

    Naja, in Python wären es 5 Zeilen:

    import re
    
    with open("hallo.txt") as f:
        for line in f:
            x = re.search(r"^([-+]?\d+) (.+) ([-+]?\d+)$", line)
            if x:
                print(f"|||{int(x.group(1))}|{int(x.group(3))}|{x.group(2)}|||")
    

    (wobei es sogar sein könnte, dass es noch kürzer ginge...)

    @Wutz sagte in Zahlen aus Datei parsen mit purem ANSI-C:

    gcc -Wall -ansi -pedantic

    Damit kompiliere ich schon. 😉



  • @EinNutzer0 sagte in Zahlen aus Datei parsen mit purem ANSI-C:

    import re

    Witzbold.
    Wer lesen kann ist klar im Vorteil.



  • @EinNutzer0 sagte in Zahlen aus Datei parsen mit purem ANSI-C:

    Und das alles in dem "Ur-C"!

    Das nennt sich K&R C.

    Ansi-C kam erst 1989



  • @Wutz

    Es geht in Python auch mit ohne Standardbibliothek kürzer, auch wenn ich jetzt nur auf eine nicht elegante Lösung gekommen bin (7 Zeilen als Funktion, keine Prüfung ob Integers am Ende,Anfang und Mitte auftauchen).

    def parse_file(strFile):
        with open(strFile,"r") as fIn:
            for strLine in fIn:
                try:
                    print(f"|||{int(strLine.split(' ',1)[0])}|{int(strLine.rsplit(' ',1)[-1])}|{strLine[strLine.find(' '):strLine.rfind(' ')+1]}|||")
                except:
                    continue
    

    Ausgabe:

    |||45|67| hallo du daa |||
    |||89|1010| moin |||
    |||-15|16| lang weil ig |||
    


  • @fairiestoy Das nimmt sich Zeichenmäßig garnichts. Und darüber was lesbarer ist brauchen wir wohl nicht reden. Ich hasse es zwar @Wutz mal recht zu geben aber ...



  • @Swordfish
    Guter Punkt, habe jetzt rein auf die Anzahl an Zeilen geachtet. Die paar Bytes machen den Braten wohl nicht fett.



  • Gerade auch mit obfuscation hat niemand eine Chance gegen C, was wohl allgemein bekannt sein dürfte.
    Aber darauf habe ich es ja gar nicht angelegt - sondern ein paar Zeilen mehr spendiert der Leserlichkeit wegen.


Anmelden zum Antworten