String Operationen - strtok



  • Hi @ all
    Ich spiele jetzt schon etwas länger mit xml-Datein und C herum.
    Soweit bin ich:
    XML einlesen = Ok
    Bestimmt Node finden = OK
    Inneren Text der Node auslesen = Teilweise ok und hier kommt mein Problem:

    Ich bekomme bei meiner Ausgabe immer vorher und nachher eine Leere Ausgabe.
    Diese will ich aber nicht und ich check es einfach nicht warum diese kommt und wie ich diese entfernen kann.

    Hier mal mein Code:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct _NodeName
    {
        char *name;
        char *inner_element;
        struct InnerText *innerelement;   
        struct NodeName *naechstes_element;
    
    } NodeName;
    
    typedef struct _InnerText
    {
       char *inner_name;
       char *wert;
       struct InnerText *naechstes_element;
    } InnerText;
    
    void Daten_trennen(char *textstring){
       while (textstring != NULL)  //Sonst wird einmal Text(null) ausgegeben
       {
          if(textstring != " "){
             printf("Textstring: %s Laenge: %u\n", textstring, strlen(textstring)); //Hier wird auch leerer String ausgegeben
             textstring = strtok(NULL, "<>"); 
          }
       }
    }
    
    int main() {
       FILE *f;
       char *searchstring = "<PERSON";
       char puffer[255];
       int counter=0; 
       char *wort;
       char nodename[255];
    
       f=fopen("Person.xml","r");
       if(f == NULL) {
          printf("Fehler bei fopen()...");
          return EXIT_FAILURE;
       }
       NodeName node;
       //<PERSON  Hausnr="4" LandesCd="AUT" Ort="Wien" PLZ="1210" PersArtCd="N" Personennr="2918072346@11435245" Strasse="Versicherungsstraße">
       while(fgets(puffer, 255, f) != NULL) {
          
          wort = strtok(puffer, " ");  //wort =<PERSON\0Hausnr="4" LandesCd="AUT" Ort="Wien" PLZ="1210" PersArtCd="N" Personennr="2918072346@11435245" Strasse="Versicherungsstraße">
    
          while(wort != NULL) {
             
              //Nodename
             if(strcmp(wort,searchstring) == 0){
                printf("Nodename: %s bei: %u\n",wort+1, counter);  // +1 da sonst < vor String steht
    
                wort = wort + 7;   //um erstes Element <PERSON zu überspringen
                while (wort != NULL)
                {   
                   Daten_trennen(wort);
                   wort = strtok(NULL, ">");  //wort =<PERSON\0Hausnr="4" LandesCd="AUT" Ort="Wien" PLZ="1210" PersArtCd="N" Personennr="2918072346@11435245" Strasse="Versicherungsstraße"\0             
                }
             }         
             wort = strtok(NULL," ");  //NULL da strtok bereits intern einen Zeiger gespeichert hat (bei folgeaufrufen) Gibt NULL bei ende vom String retour        
          }
          counter++;
       }   
       return 0;
    }
    

    Hier ne XML Datei die ich auslese:

    <?xml version="1.0" encoding="UTF-8"?>
    <OMDS xmlns="urn:omds20" xmlns:asx="http://www.sap.com/abapxml" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
      <PAKET DVRNrAbs="00099999" MaklerID="109999" OMDSVersion="213-00" PaketInhCd="VM" PaketUmfCd="G" PaketZpktErstell="2021-01-01T15:12:39" VUNr="099">
    
     <PERSON  Hausnr="99" LandesCd="AUT" Ort="Kaindorf bei Hartberg" PLZ="8224" PersArtCd="S" Personennr="2918745850@13866256" Strasse="Kaindorf">
      <SONSTIGE_PERSON Name="Ihr Versicherungsmakler GmbH" SonstPersArtCd="0"/>
    </PERSON>
    <PERSON  Hausnr="88" LandesCd="AUT" Ort="Kaindorf bei Hartberg" PLZ="8224" PersArtCd="N" Personennr="2918965473@15462572" Strasse="Strada">
      <NATUERLICHE_PERSON Familienname="Famnam" FamilienstandCd="0" Gebdat="1973-05-14" GeschlechtCd="2" LandesCd="AUT" Vorname="Vornam"/>
    </PERSON>
    <PERSON  Hausnr="1" LandesCd="AUT" Ort="Wien" PLZ="1210" PersArtCd="N" Personennr="2918072343@11435234" Strasse="Versicherungsstraße">
      <NATUERLICHE_PERSON Familienname="Mustermax" FamilienstandCd="0" Gebdat="1966-04-29" GeschlechtCd="1" LandesCd="AUT" Vorname="Nikolaus"/>
      <EL-Text TxtArtCd="ANR" TxtInhalt="Herr"/>
      <EL-Text TxtArtCd="TIT" TxtInhalt="Ing."/>
    </PERSON>
    <PERSON  Hausnr="2" LandesCd="AUT" Ort="Wien" PLZ="1210" PersArtCd="N" Personennr="2918072344@11435237" Strasse="Versicherungsstraße">
      <NATUERLICHE_PERSON Familienname="Mustermax" FamilienstandCd="0" Gebdat="1967-05-14" GeschlechtCd="2" LandesCd="AUT" Vorname="Silvia"/>
      <EL-Text TxtArtCd="ANR" TxtInhalt="Frau"/>
      <EL-Text TxtArtCd="TIT" TxtInhalt="Mag."/>
    </PERSON>
    <PERSON  Hausnr="3" LandesCd="AUT" Ort="Wien" PLZ="1210" PersArtCd="N" Personennr="2918072345@11435242" Strasse="Versicherungsstraße">
      <NATUERLICHE_PERSON Familienname="Mustermax" FamilienstandCd="0" Gebdat="1996-02-03" GeschlechtCd="2" LandesCd="AUT" Vorname="Caroline"/>
      <EL-Text TxtArtCd="ANR" TxtInhalt="Frau"/>
    </PERSON>
    <PERSON  Hausnr="4" LandesCd="AUT" Ort="Wien" PLZ="1210" PersArtCd="N" Personennr="2918072346@11435245" Strasse="Versicherungsstraße">
      <NATUERLICHE_PERSON Familienname="Mustermax" FamilienstandCd="0" Gebdat="1998-03-02" GeschlechtCd="2" LandesCd="AUT" Vorname="Hanna"/>
      <EL-Text TxtArtCd="ANR" TxtInhalt="Frau"/>
    </PERSON>
    </PAKET>
    </OMDS>
    

    So sieht meine Ausgabe aus:

    Nodename: PERSON bei: 10
    Textstring:  Laenge: 0
    Textstring:  Hausnr="1" LandesCd="AUT" Ort="Wien" PLZ="1210" PersArtCd="N" Personennr="2918072343@11435234" Strasse="Versicherungsstraße" Laenge: 126
    Textstring:
     Laenge: 1
    

    Mir ist auch Unklar warum bei der Ausgabe Zeile 4 + 5 getrennt sind.
    Ich weiß das es für C div. Parser gibt. Will mich aber so durch kämpen 👦



  • Ist das nur eine Übungsaufgabe oder warum benutzt du keine XML Parser Lib, z.B. Expat?

    Aber zu deinen Fragen:
    Du liest ja auch alle Zeilenumbrüche ein (mit strtok trennst du ja nur die Leerzeichen).
    Und daher auch der Zeilenumbruch bei der Ausgabe...

    Wenn du weißt, daß in der XML-Datei die Tags jeweils in eigenen Zeilen stehen, könntest du also zeilenweise einlesen (ist jedoch nicht zu empfehlen).

    PS: Außerdem sieht man an deiner Ausgabe auch, daß du die XML-Daten nicht als UTF-8 einliest (wie es im XML-Header steht).



  • Zeile 24 sieht irgendwie komisch aus, findest du nicht auch?
    strtok ist zu vermeiden, da es - wie du schon gemerkt hast - statisch arbeitet.
    Bezeichner mit beginnendem Unterstrich sind gemäß Standard reserviert.
    Ich empfehle dir, nicht zeilen/abschnittsweise zu arbeiten, sondern mit einem einzigen String, zumindest aber fscanf zu benutzen.
    Verkettete Listen sind ein Alptraum.



  • @Th69 Danke für die Infos.

    Wollte jetzt mal das UTF-8 Problem angehen.
    Laut Microsoft braucht man nur bei fload das ccs flag setzten

    (FILE *fp = fopen("newfile.txt", "rt+, ccs=encoding");
    

    hab das jetzt bei mir mal gemacht (Zeile 39 oben im Code):

    f=fopen("Person.xml","r, ccs=UTF-8");
    

    nur kommt dann gar keine Ausgabe bei mir raus.



  • @WienMax MS empfiehlt _wfopen nicht fopen.



  • @WienMax sagte in String Operationen - strtok:

    ccs=encoding");

    Ach spannend, dass das ccs-Flag von fopen gerade jetzt hier auftaucht. Mir war dieses Flag bislang unbekannt, aber ich hatte es gerade vor ein paar Tagen auf Twitter im Zusammenhang mit einer Sicherheitslücke kennengelernt.

    UTF-8 kann man in solchen Dateien häufig ignorieren, wenn man nur Daten weiterverarbeitet. Solange die Meta-Zeichen wie "<", ">" etc. alle aus ASCII kommen, kann man den Rest einfach weiterleiten.



  • @Th69 sagte in String Operationen - strtok:

    PS: Außerdem sieht man an deiner Ausgabe auch, daß du die XML-Daten nicht als UTF-8 einliest (wie es im XML-Header steht).

    Wobei hier das Problem nicht das einlesen ist. Sondern die Ausgabe. Die Windows command line (cmd.exe) unterstützt by default kein UTF-8 für text-ausgaben. Die bytes werden in einer ANSI codepage interpretiert.
    Für die Ausgabe von UTF-8 texten muss man erst, wenn ich mich recht erinnere, eine andere codepage setzen



  • @firefly sagte in String Operationen - strtok:

    Für die Ausgabe von UTF-8 texten muss man erst, wenn ich mich recht erinnere, eine andere codepage setzen

    ist auch buggy



  • mal abgesehen von dem UTF-Kram;
    bei bekannter Struktur lässt sich ein simpler XMLReader auch mal schnell selbst entwerfen, der die Daten dann auch gleich in die struct packt:

    enum { MAXATTR = 7 };
    typedef char string[100];
    typedef struct { string x; } sstring;
    typedef struct { string key; int off; } Map;
    typedef struct { string Hausnr, LandesCd, Ort, PLZ, PersArtCd, Personennr, Strasse; } xperson;
    typedef struct { string Familienname, FamilienstandCd, Gebdat, GeschlechtCd, LandesCd, Vorname; } nperson;
    typedef struct { string Name, SonstPersArtCd; } sperson;
    typedef struct { string TxtArtCd, TxtInhalt; } elText;
    typedef struct { xperson p; nperson n; sperson s; elText t[3]; } Person;
    
    struct { string tag;  int off; Map map[MAXATTR]; } map[] = {
    {"PERSON",offsetof(Person,p),{{ "Hausnr",0 }, { "LandesCd",offsetof(xperson,LandesCd) }, { "Ort",offsetof(xperson,Ort) }, { "PLZ",offsetof(xperson,PLZ) }, { "PersArtCd",offsetof(xperson,PersArtCd) }, { "Personennr",offsetof(xperson,Personennr) }, { "Strasse",offsetof(xperson,Strasse) }}},
    {"NATUERLICHE_PERSON",offsetof(Person,n),{{ "Familienname",0 }, { "FamilienstandCd",offsetof(nperson,FamilienstandCd) }, { "Gebdat",offsetof(nperson,Gebdat) }, { "GeschlechtCd",offsetof(nperson,GeschlechtCd) }, { "LandesCd",offsetof(nperson,LandesCd) }, { "Vorname",offsetof(nperson,Vorname) }}},
    {"SONSTIGE_PERSON",offsetof(Person,s),{{ "Name",0 }, {"SonstPersArtCd",offsetof(sperson,SonstPersArtCd) }}},
    {"EL-Text",offsetof(Person,t),{{ "TxtArtCd",0 }, {"TxtInhalt",offsetof(elText,TxtInhalt) }}} };
    
    int tagof(char* s)
    {
      for (int i = 0; i < sizeof(map) / sizeof * map; ++i)
        if (!strcmp(s, map[i].tag)) return i + 1;
      return 0;
    }
    int indexof(char* s, int off, Map* map)
    {
      for (int i = 0; i < MAXATTR; ++i)
        if (!strcmp(map[i].key, s)) return off + map[i].off;
    }
    int quotecheck(char* s)
    {
      int i = 0;
      while (*s)
        if (*s++ == '\"') ++i;
      return i == 0 || i == 2;
    }
    char* readtoken(FILE* f, char* x)
    {
      *x = 0;
      string tmp = "";
      while (1 == fscanf(f, "%99s", tmp) && (strcat(x, *x ? " " : ""), strcat(x, tmp), !quotecheck(x)));
      return *x ? x : 0;
    }
    void copy(string dest, string src)
    {
      *(sstring*)dest = *(sstring*)src;
    }
    
    Person person[100];
    
    int main()
    {
      FILE* f = fopen("Person.xml", "r");
      string w;
      int level = -1, p = -1;
    
      while (readtoken(f, w))
      {
        int i;
        if (i = tagof(w + 1))
        {
          level = i - 1;
          if (level == 0) p++;
        }
        else
          if (level > -1)
          {
            string key, value;
            if (2 == sscanf(w, "%[^=]=\"%[^\"]", key, value))
            {
              sstring* ptr = (char*)(person + p) + indexof(key, map[level].off, map[level].map);
              while (*(char*)ptr) ptr++;
              copy(ptr, value);
            }
          }
      }
      fclose(f);
    
      p = 0;
      while (*person[p].p.Hausnr)
      {
        printf("\n\n%s,%s,%s,%s,%s,%s,%s", person[p].p.Hausnr, person[p].p.LandesCd, person[p].p.Ort, person[p].p.PersArtCd, person[p].p.Personennr, person[p].p.PLZ, person[p].p.Strasse);
        printf("\n%s,%s,%s,%s,%s,%s", person[p].n.Familienname, person[p].n.FamilienstandCd, person[p].n.Gebdat, person[p].n.GeschlechtCd, person[p].n.LandesCd, person[p].n.Vorname);
        printf("\n%s,%s", person[p].s.Name, person[p].s.SonstPersArtCd);
        printf("\n%s,%s,%s,%s", person[p].t[0].TxtArtCd, person[p].t[0].TxtInhalt, person[p].t[1].TxtArtCd, person[p].t[1].TxtInhalt);
        p++;
      }
      return 0;
    }
    
    99,AUT,Kaindorf bei Hartberg,S,2918745850@13866256,8224,Kaindorf
    ,,,,,
    Ihr Versicherungsmakler GmbH,0
    ,,,
    
    88,AUT,Kaindorf bei Hartberg,N,2918965473@15462572,8224,Strada
    Famnam,0,1973-05-14,2,AUT,Vornam
    ,
    ,,,
    
    1,AUT,Wien,N,2918072343@11435234,1210,Versicherungsstraße
    Mustermax,0,1966-04-29,1,AUT,Nikolaus
    ,
    ANR,Herr,TIT,Ing.
    
    2,AUT,Wien,N,2918072344@11435237,1210,Versicherungsstraße
    Mustermax,0,1967-05-14,2,AUT,Silvia
    ,
    ANR,Frau,TIT,Mag.
    
    3,AUT,Wien,N,2918072345@11435242,1210,Versicherungsstraße
    Mustermax,0,1996-02-03,2,AUT,Caroline
    ,
    ANR,Frau,,
    
    4,AUT,Wien,N,2918072346@11435245,1210,Versicherungsstraße
    Mustermax,0,1998-03-02,2,AUT,Hanna
    ,
    ANR,Frau,,
    

Anmelden zum Antworten