Java's split Funktion



  • ... lässt man mal die fixe Spaltenbreite und die Leerzeichen im Delimiter weg, dann müsste der Code also so aussehen:

    void split(char *s) {
    	if (*s++ != '"') printf("Fehler");
    	do {
    		if (*s == '"' && *(s+1) == ';' && *(s+2) == '"') {
    			s+=2;
    			printf(":");
    		} else {
    			if (*s != '"' && *(s+1) != '\0')
    				printf("%c",*s);
    		}
    	} while (*++s != '\0');
    }
    

    1. Wie kann ich den Code verschönern? 2 if's ineinander verschachtelt gefällt mir gar nicht ...
    2. wie kann ich realisieren, dass Leerzeichen/Tabs zwischen meiner Zeichenkette erlaubt sind?
    3. Wie kann ich in einer Funktion Exception-Throwing realisieren? (siehe Zeile 2)
    4. Wie kann ich das letzte Anführungszeichen in meinem String schon zu Beginn der Funktion entfernen / eine erneute Exception erzeugen, wenn kein Anführungszeichen am Ende der Zeile steht? (Leerzeichen nehmen wir mal wieder aus; ich müsste also irgendein "Trim" darüberlegen ...
    4. Wie könnte ich das Auffüllen mit Leerzeichen umsetzen? Brauch ich da zwingend eine Schleife oder gibt es da eine elegantere Möglichkeit? (in Perl könnte ich glaub ich einfach $str+" " x spaltenlaengelength(spaltenlaenge - length(str) schreiben; c wird mir das warscheinlich nicht so leicht machen, oder?!)

    soweit mal.
    lg, guni



  • was in perl zwei zeilen sind, kann in c leicht zu 20-30 zeilen ausarten.
    ohne schleifen läuft in c kaum was. für das auffüllen kannst du ein char array der fixen spaltenbreite mit leerzeichen initialisieren. die elemente, die nicht überschrieben werden sind die automatischen auffüller. vor jedem neubeschreiben mit leerzeichen memsetten ( funktion memset ).
    oder andere möglichkeit: direkt kopieren und zeichen zählen bis ".
    differenz mit leerzeichen auffüllen. aber das ist ja nix neues, ne.

    das letzte " entfernen? gar nicht erst einlesen 😉

    whitespaces (oder andere zeichen) ignorieren kannst du natürlich auch in eine funktion auslagern:

    int skip_spaces(FILE* fp, int c)
    {
    	while (isspace(c))
    		c = fgetc(fp);
    	return c;
    }
    

    exceptions gibt es in c nicht. du kannst aus void split ein int split machen und ggf. fehlernummern zurückgeben, über die wiederum eine nachricht abrufbar ist.
    vllt. noch besser: gar keine split funktion verwenden.
    hier ein minimalkonverter, wichtige fehlerprüfungen der kürze wegen weggelassen, guckst du:

    int main() 
    { 
    	FILE* a, *b;
    	char* fa = "semicolon.csv";
    	char* fb = "colon.csv";
    	int c = 0;
    	a = fopen(fa, "rb");
    	b = fopen(fb, "wb");
    
    	while((c = fgetc(a)) != EOF )
    	{
    		switch(c)
    		{
    			case '"':
    			continue;
    
    			case ';':
    				c = ':';
    			break;
    		}
    		fputc(c, b);
    	}
    
    	fcloseall();
        return 0; 
    }
    

    gruß,
    p.w.p



  • hallo,

    hab versucht, die ideen umzusetzen / mich damit abzufinden, dass mein c-Programm nicht so schön und kurz werden wird wie ein Perl-Programm. Hier mein aktueller Code:

    #include <stdio.h>
    
    #define TRUE  (1==1)
    #define FALSE (!TRUE)
    
    enum bool {false,true};	
    /*
    struct column {
    	bool notNull,
    	int len
    };
    */
    
    // length of the current column
    // gets counted when a character is printed
    // gets a reset each time a delimiter is found
    int currlen = 0;
    
    // the current column
    // gets incremented each time a delimiter is found
    int currcol = 0;
    
    // 'Constructor' for column 
    /*
    struct column newCol(bool notNull, int len) {
    	struct column ret;
    	ret.notNull = notNull;
    	ret.len = len;
    	return ret;
    }
    */
    
    void fillup() {
    	// fills up column with whitespaces
    	/*
    	if (col.len < currlen) {
    		exit programm returning an error
    	} else {
    		for (int i = currlen; i<col.len;i++){
    			printf(" ");
    		} 
    	}
    	*/
    }
    
    void replaceDelim(char *s) {
    	if (*s == '"' && *(s+1) == ';' && *(s+2) == '"') {
    		s+=2;
    		currlen = 0;
    		currcol++;
    		fillup();
    		printf(":");
    	}
    }
    
    void printNormal(char *s) {
    	if (!(*s=='"' && *(s+1)=='\0')) {
    		currlen++;
    		printf("%c",*s);
    	}
    }
    
    void split(char *s) {
    	if (*s++ != '"') 
    		printf("Fehler");
    	do {
    		replaceDelim(s);
    		printNormal(s);
    	} while (*++s != '\0');
    }
    
    int main(void) {
    	/*
    	struct column cols[4];
    	cols[0] = newCol(TRUE,10);
    	cols[1] = newCol(TRUE,2);
    	cols[2] = newCol(FALSE,8);
    	cols[3] = newCol(TRUE,13);
    	*/
    	split("\"Das\";\"ist\";\"e\"i\"n\";\"Test\"");
        return 0;
    }
    

    ein paar Dinge sind mir noch unklar:

    1. wie definiere ich die struct? Jedes Mal, wenn ich die Kommentare wegmache bekomme ich beim Komplieren einen Fehler

    2. wie ihr seht, habe ich das Erkennen des Delimiters / das Ausgeben von Zeichen die in einer Spalte vorkommen ausgelagert. Jetzt hab ich aber das Problem, dass ich in meiner replaceDelim den Pointer um 2 Zeichen weiterspringen lassen möchte. nur wird in der eigentlichen split-Funktion dann ignoriert, was in replaceDelim mit dem Pointer gemacht wurde. Ich muss den Pointer vermutlich byRef und nicht byVal übergeben (um es einmal in VisualBasic zu formulieren ;))
    Aber ein Pointer ist ja schon eine Referenz! Muss ich da einen Pointer auf einen Pointer machen?!
    Wie muss ich das schreiben?!
    Hab es einfach mal so versucht:

    void replaceDelim(char **s) {
    	if (**s == '"' && **(s+1) == ';' && **(s+2) == '"') {
    		*s+=2;
    		currlen = 0;
    		currcol++;
    		fillup();
    		printf(":");
    	}
    }
    

    und mache dann in der spilt meinen Aufruf so:

    replaceDelim(*s);
    

    allerdings gibt mein Compiler einen "passing arg1 of 'replaceDelim' makes pointer from integer without a cast"-Fehler.
    Kann mir da irgendwer weiterhelfen?!

    danke. guni



  • weiß da keiner mehr weiter?!



  • probier mal das:

    #include <stdio.h>
    
    // 1. zeigt auf eingabestring
    // 2. zeigt auf ausgabestring
    // 3. mindestlaenge der woerter 
    void replaceDelim (char *in, char *out, int length)
    {
      int chars = 0;
    
      while (*in)     // mach fuer alle zeichen...
      {
        switch (*in)  // welches zeichen haben wir gerade?
        {
          case '"':   // " <-- auslassen
          break;
    
          case ';':    // ; <-- spezialbehandlung
          while (chars++ < length)  // letztes wort auffuellen mit ' '
            *out++ = ' ';
          chars = 0;       // wortzaehler reset
          break;
    
          default:         // alles andere speichern
          *out++ = *in;     
          chars++;
          break;           // mitzaehlen fuer's auffuellen
        }
        in++;     // naechstes zeichen pruefen
      }
      while (chars++ < length)  // letztes wort auffuellen
        *out++ = ' ';
      *out = 0;            // string-ende anhaengen
    }
    
    int main()
    {
      char out[256];   // <-- muss gross genug sein
      replaceDelim ("\"Das\";\"ist\";\"e\"i\"n\";\"Test\"", out, 10);  // test
      puts (out); // und? gut oder bloed?
    }
    


  • 🙂 <-- vergessen
    🙂



  • ach so, da sollen noch doppelpunkte an's ende, kein problem -> füge nach zeile 19 folgendes ein:

    out++ = ':';
    

    🙂



  • ^^edit: sternchen vergessen, so natürlich:

    *out++ = ':';
    

    🙂



  • hi +fricky.

    dein ansatz ist ganz gut, deckt aber nicht ganz meine anforderungen ab, da die spalten ja unterschiedlich lang sein können.

    deswegen hab ich mir überlegt, folgende struct zu bauen:

    struct column {
    	bool notNull,
    	int len
    };
    

    (in weiterer Folge muss ich noch entscheiden ob eine Spalte pflichtig ist oder nicht)

    in meinem main-Programm will ich dann die Spalten deklarieren:
    column col[] = ...
    dann übergeb ich an die Funktion split mein col[] und die Funktion kann sich dann raussuchen, bei welchem Feld sie wieviel auffüllen muss:

    while (chars++ < col[i++])  // letztes wort auffuellen
        *out++ = ' ';
    

    wie auch immer:
    mein eigentliches Problem ist ja zur Zeit
    1. dass ich die Struct irgendwie falsch zu deklarieren scheine (-> Code)
    2. dass ich mein replaceDelim / printNormal / fillup aus der eigentlichen Split-Funktion auslagern will (aus Übersichtlichkeitsgründen); den pointer an die Unterfunktionen aber zur Zeit so übergebe dass ein s++ in replaceDelim nur lokal durchgeführt wird und die Zählung in der split-Funktion dann nicht übernommen wird!

    lg, guni



  • Hi!
    Guckst du hier, machst du so:

    typedef int bool;
    enum {false, true};
    

    Den Aufruf für

    void replaceDelim(char **s)

    in der Funktion split machst du so:
    replaceDelim(**&**s);

    Ich würde Modulglobal

    static char* s;
    

    deklarieren.
    Die Parameter müssen nicht ständig durch die Funktionen geschleppt werden.
    Aus

    void replaceDelim(char** s);
    

    wird

    void replaceDelim();
    

    usw., der Dereferenzierungssternchensalat in den Funktionen verschwindet vollständig.
    Ich nehmen an, du bist objektorientierte Programmierung gewöhnt?
    Deine Klasse ist jetzt deine *.c-Datei, die Membervariable in diesem Fall der statische char-Zeiger.



  • hi BigBrother,

    dein Ansatz gefällt mir ganz gut (abgesehen davon, dass ich lieber Parameter übergebe), aber was soll's ...

    jedenfalls bekomm ich jetzt beim Ausführen meines Programmes so einen Fehler:

    $ ./converter
    8 [main] converter 512 _cygtls::handle_exceptions: Error while dumping sta
    te (probably corrupted stack)
    Segmentation fault (core dumped)

    ...

    ausserdem wird eine .stacktrace-Datei angelegt, aus der ich aber nicht ganz schlau werde:

    Stack trace:
    Frame     Function  Args
    0022CCB8  0040108E  (61004690, 0022CE64, 0022CCE8, 00401169)
    0022CCC8  00401117  (6116B772, 0000002F, 00401130, 00401155)
    0022CCE8  00401169  (00000001, 6116B748, 00660090, 0022CC70)
    0022CD98  610060D8  (00000000, 0022CDD0, 61005450, 0022CDD0)
    61005450  61004416  (0000009C, A02404C7, E8611021, FFFFFF48)
    

    ... was kann ich da machen?!
    danke.
    guni



  • guni schrieb:

    dein Ansatz gefällt mir ganz gut (abgesehen davon, dass ich lieber Parameter übergebe), aber was soll's ...

    Kannst du ja auch ruhig so weiter machen, du machst dir die Sache allerdings unnötig schwer.
    Ich bin auch kein Fan der totalen Variablenglobalisierung aber in manchen Fällen, wie diesem, bietet es sich zweckmäßigerweise an.
    Die statische Variable bleibt ja im Modul gekapselt. Für einen OOPisten ist das eine große Umstellung, verständlich, ging mir nämlich nicht anders.

    Was deine Stacktracegeschichte betrifft, keine Ahnung. Hab noch nie etwas mit einer Stacktrace-Datei zu tun gehabt.
    Wäre evtl. hilfreich, das betreffende Codefragment zu posten.



  • hmm ... keine Ahnung, welches das entsprechende Code-Fragment ist.
    Zur Zeit sieht mein Code so aus:

    #include <stdio.h>
    
    typedef int bool;
    enum bool {false,true};	
    
    struct column {
    	bool notNull;
    	int len;
    };
    
    // length of the current column
    // gets counted when a character is printed
    // gets a reset each time a delimiter is found
    int currlen = 0;
    
    // the current column
    // gets incremented each time a delimiter is found
    int currcol = 0;
    
    static char *s;
    
    // 'Constructor' for column 
    /*
    struct column newCol(bool notNull, int len) {
    	struct column ret;
    	ret.notNull = notNull;
    	ret.len = len;
    	return ret;
    }
    */
    
    void fillup() {
    	// fills up column with whitespaces
    	/*
    	if (col.len < currlen) {
    		exit programm returning an error
    	} else {
    		for (int i = currlen; i<col.len;i++){
    			printf(" ");
    		} 
    	}
    	*/
    }
    
    void replaceDelim() {
    	if (*s == '"' && *(s+1) == ';' && *(s+2) == '"') {
    		*s+=2;
    		currlen = 0;
    		currcol++;
    		fillup();
    		printf(":");
    	}
    }
    
    void printNormal() {
    	if (!(*s=='"' && *(s+1)=='\0')) {
    		currlen++;
    		printf("%c",*s);
    	}
    }
    
    void split() {
    	if (*s++ != '"') 
    		printf("Fehler");
    	do {
    		replaceDelim();
    		printNormal();
    	} while (*++s != '\0');
    }
    
    int main(void) {
    	/*
    	struct column cols[4];
    	cols[0] = newCol(TRUE,10);
    	cols[1] = newCol(TRUE,2);
    	cols[2] = newCol(FALSE,8);
    	cols[3] = newCol(TRUE,13);
    	*/
    	s = "\"Das\";\"ist\";\"e\"i\"n\";\"Test\"";
    	split();
        return 0;
    }
    

    man sieht an den Kommentaren, dass ich noch einiges zu basteln plane ...

    lg, guni



  • *(s+2)

    Das wird wohl den Stackfehler verursachen, beim Abarbeiten des letzten Zeichens.
    Es wird auf Speicher hinter der Zeichenkette zugegriffen.


Anmelden zum Antworten