String Array mit qsort sortieren



  • SeppJ schrieb:

    Das fgets sieht nur ein \n, wo in der Datei \r\n stand.

    Du hast meinen Punkt nicht verstanden. 255 Zeichen für den Namen + \n + \0 macht de facto 257. 😉



  • Wirklich klarer wird mir der erste Teil der Schleife leider auch nicht, der 2te is jetzt logisch, aber der Anfang:

    Was macht das Rufzeichen vor der Anweisung? Normalerweise würde ich sagen, wenn der Ausdruck falsch ist, aber wenn ich versuche das so zu kompilieren, kommt ein Error. Hätte es nicht den gleichen Effekt, wenn ich das Rufzeichen weglasse, und nicht != sondern == abfrage? Dann funktioniert es nämlich.

    Dann: Kannst du mir noch erklären, was genau du in der Schleife (Zeile 3) berechnest? Oder eher, warum du das so berechnest?

    Und bezüglich qsort: was meinst du mit casten? Und was mir noch aufgefallen ist, warum sortiert es die Namen verkehrt?

    MFG



  • Ach komm, was solls. Hier:

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    
    int main(void)
    {
    	char strings[256][257]; // 256 Arrays mit je 257 chars
    	char (*p)[257] = strings; // Ein Pointer auf ein Array mit 257 chars. Zeigt jetzt auf das erste Array von strings.
            // Hier wird eingelesen
    	for (; p != strings + sizeof(strings) / sizeof(*strings) && fgets(*p, sizeof(*p), stdin) != NULL; ++p);
            // sizeof(strings) / sizeof(*strings) berechnet wie viele Elemente strings hat, das sind hier 256
            // p darf also nicht strings + arraygröße(strings) sein.
            // Diese Bedingung wird zu erst getestet, wenn sie wahr ist, wird fgets() aufgerufen.
            // *p ist ein Array, das zerfällt bei der Übergabe in einen Zeiger auf das erste Element.
            // (Wie du es auch schon kennst von eindimensionalen Puffern.)
            // sizeof(*p) gibt hier 257 zurück.
            // ++p lässt p sizeof(*p) byte weiter nach vorne zeigen.
            // (Dort liegt dann das jeweils nächste Array[257] aus strings)
    
            // Hier wird sortiert. Nach "casten c++" kannst du selbst googlen.
    	qsort(strings, p - strings, sizeof(*p), (int (*) (const void*, const void*))strcmp);
    
            // Das hier ist die Ausgabe, wie du siehst wird rückwärts ausgegeben.
            // Das letzte Element wird als erstes ausgegeben und so weiter.
    	while (p-- != strings)
    		printf("%s", *p);
    
    	return 0;
    }
    

    Edit: Zu SeppJs !: Da hat er Klammern vergessen.



  • Danke schonmal vielmals, langsam wird mir alles klar!

    Ich hab das mit qsort jetzt noch ein bisschen anders gelöst, und zwar so:

    meine Aufruffunktion ist

    qsort (namen, zahler, sizeof(char*), compare);
    

    die Funktion compare schaut so aus:

    int compare (const void * a, const void * b)
    {
        return strcmp(a,b);
    }
    

    is ja eig. nur die längere Version von dir.

    Soweit funktioniert das auch, allerdings hab ich ein kleines Problem: ich will die Strings ein wenig anders sortieren, und zwar so, dass aA vor Aa is, was hier leider nicht der Fall is.

    Könnt ihr mir nochmal helfen? Wäre wirklich sehr dankbar!

    MFG Zoltamor



  • Dann musst du dir deine eigene strcmp() bauen.

    Wenn du den Quellcode von strcmp() hast, wirst du sehen, dass das eine ziemlich einfache Funktion ist.

    Die strcmp-Versionen, die zumindest Groß/kleinschreibung ignorieren heissen meist
    strcasecmp oder strcmpi .
    Auch dazu solltest du Quellen finden, damit du das anpassen kannst.

    Bedenke noch das qsort nur darauf achtet, ob die Werte <0 , ==0 oder >0 sind.
    Es muss nicht -1 oder +1 sein



  • Ok, hab das ganze jetzt ein wenig anders gelöst, und zwar so:

    int compare (const void * a, const void * b)
    {
    
    int result = 0;
        char* string1 = (char*) a;
        char* string2 = (char*) b;
        //run through all chars in the string
    
     for(int i = 0;i<255;i++)
        {
            // compare each char case insensitive
                result = strncasecmp(string1+i,string2+i,1);
                result*=2;
               // if a char is the uppercase variant of the other strings char and the words have the same length add 1 to the result do differentiate between upper and lower
                if(isupper((int)string1[i])!= 0 && result == 0 && strlen(string1)==strlen(string2)) result+=1;
                else if( isupper((int)string2[i])!= 0 && result == 0 && strlen(string1)==strlen(string2)) result=-1;
                if(result != 0) break;
        }
      return result;
    }
    

    Allerdings hab ich einen Error, und keine Ahnung was dieser bedeutet:

    error: 'for' loop initial declaration used outside C99 mode

    der Aufruf mit qsort ist dieser:

    qsort (namen, zahler, sizeof(char*), compare);
    

    Was is da falsch gelaufen?

    MFG Zoltar


  • Mod

    Zoltamor schrieb:

    Allerdings hab ich einen Error, und keine Ahnung was dieser bedeutet:

    error: 'for' loop initial declaration used outside C99 mode

    der Aufruf mit qsort ist dieser:

    qsort (namen, zahler, sizeof(char*), compare);
    

    Guck doch mal genau auf den Fehler. Der ist ganz woanders.

    Schleifen der Form for(Deklaration;...) darfst du erst ab C99-Standard. Das musst du bei deinem Compiler anscheinend aktivieren (-std=c99 bei GCC), sofern er es überhaupt kann (ja, der MS Compiler kann C99 (das 99 steht für 1999) überhaupt nicht und wird es wohl auch nie können).

    Deine Vergleichsfunktion ist übrigens ziemlich sicher total falsch, außer du möchtest ganz was anderes als ich vermute. Was soll die machen? Groß-/Kleinschreibung beim Vergleich ignorieren?



  • Mein Programm sortiert jetzt zwar Klein- vor Großbuchstaben, aber nur den ersten char! Obwohl ich natürlich jeden String nach klein- und großschreibung sortieren will.. Könnt ihr mir sagen, wo der Fehler ist?

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <stdbool.h>
    
    int compare (const void * a, const void * b)
    {
    
    int result = 0;
        char* string1 = (char*) a;
        char* string2 = (char*) b;
        int i=0;
    
     for(i = 0;i<255;i++)
        {
            // compare each char case insensitive
                result = strncasecmp(string1+i,string2+i,1);
                result*=2;
               // if a char is the uppercase variant of the other strings char and the words have the same length add 1 to the result do differentiate between upper and lower
                if(isupper((int)string1[i])!= 0 && result == 0 && strlen(string1)==strlen(string2)) result+=1;
                else if( isupper((int)string2[i])!= 0 && result == 0 && strlen(string1)==strlen(string2)) result=-1;
                if(result != 0) break;
        }
      return result;
    }
    
     int main(int argc, char *argv[])
     {
    
     FILE *datei;
     char arr[257]; //da Namen maximal 255 Zeichen lang
    
     int zahler=0;
     int i;
     int vergleich_r;
     int c=0;
    
        if(argc==4)
                datei = fopen(argv[2], "r");
        else
                datei = fopen(argv[1], "r");
    
    while ((c = fgetc(datei)) != EOF)       //geht dur Datei bis zum Ende
      {
    
       if( (c >='a' && c <='z')   ||  (c >='A' && c <='Z') || c=='-' || c==' ' || c=='\n' ) //wenn es nur aus diesen Zeichen besteht
           {
            }
            else
           {
                fprintf(stderr,"sortnames: wrong input format\n");
                return 1;
                break;
           }
    
      }
    
     fclose(datei);
    
     if(argc==4)
                datei = fopen(argv[2], "r");
        else
                datei = fopen(argv[1], "r");
    
        if(datei == NULL)
             {
    
                    if(argc==4)     //wenn 4 Parameter übergeben sind und Input Datei nicht vorhanden ist
                 {
                    fprintf(stderr,"sortnames: cannot open input file: %s\n",argv[2]);
                    return 1;
                 }
    
                 else       //wenn x Parameter übergeben sind und Input Datei nicht vorhanden ist
                 {
                    fprintf(stderr,"sortnames: cannot open input file: %s\n",argv[1]);
                    return 1;
                 }
    
             }
    
        while(fgets(arr, 257,datei) != NULL)    //bei jeder eingelesenen Zeile
             {
             zahler++;
             }
    
     fclose(datei);
    
    if(argc==4)
                datei = fopen(argv[2], "r");
        else
                datei = fopen(argv[1], "r");
    
      char namen[zahler][255];
    
     for(i=0;i<zahler;i++)
             {
    
                     fgets(namen[i], 255, datei);
    
             //  printf("Namen Stelle %d: %s",i,namen[i]);
    
             }
     fclose(datei);
    
        if(argc<3)  //zu wenig Parameter
        {
           fprintf(stderr,"sortnames: wrong number of input or output files\n");
           return 1;
    
        }
    
        if (argc==4)
            {
                 vergleich_r = strcmp(argv[1],"-r");    //wenn 4 Parameter, muss der an Stelle 1 die Option sein, also -r
            if (vergleich_r == 0)           //wenn 4 Parameter sind und -r
                    {
    
                    qsort (namen, zahler, sizeof(char*), compare);
    
                        if(argc==4)
                            datei = fopen(argv[3], "w");
                        else
                            datei = fopen(argv[2], "w");
    
                         if(datei == NULL)
             {
    
                    if(argc==4)     //wenn 4 Parameter übergeben sind und Output Datei nicht gelesen werden ann
                 {
                    fprintf(stderr,"sortnames: cannot open output file: %s\n",argv[3]);
                    return 1;
                 }
    
                 else       //wenn x Parameter übergeben sind und Output Date nicht gelesen werden kann
                 {
                    fprintf(stderr,"sortnames: cannot open output file: %s\n",argv[2]);
                    return 1;
                 }
    
             }
    
                    for(i=zahler;i>=0;i--)
                         {
                         fputs (namen[i],datei);
                         }
                    fclose(datei);
                    }
    
            else                    //4 Parameter, aber kein -r
                {
                    fprintf(stderr,"sortnames: wrong option %c\n",argv[1][1]);
                    return 1;
                }
    
            }
    
                else        //3 Parameter
                    {
    
                    qsort (namen, zahler, sizeof(char*), compare);
    
                    if(argc==4)
                            datei = fopen(argv[3], "w");
                        else
                            datei = fopen(argv[2], "w");
    
                    if(datei == NULL)
                                 {
    
                                        if(argc==4)     //wenn 4 Parameter übergeben sind und Output Datei nicht gelesen werden ann
                                     {
                                        fprintf(stderr,"sortnames: cannot open output file: %s\n",argv[3]);
                                        return 1;
                                     }
    
                                     else       //wenn x Parameter übergeben sind und Output Date nicht gelesen werden kann
                                     {
                                        fprintf(stderr,"sortnames: cannot open output file: %s\n",argv[2]);
                                        return 1;
                                     }
    
                                 }
    
                    for(i=0;i<zahler;i++)
                         {
                         fputs (namen[i],datei);
                         }
                    fclose(datei);
                    }
    
     return 0;
     }
    

    MFG Zoltamor



  • Das for(int i = 0;i<255;i++) ist für Strings schon mal großer Mist, da ein String ja auch mal weniger Zeichen enthalten kann.

    Du musst doch nur wenn strcasecmp 0 zurückliefert und strcmp != 0 vom Ergebnis von strcmp das Vorzeichen ändern.

    if ( !(result = strcasecmp(string1,string2)))  /* Es sind die gleichen Woerter) */
      result = -strcmp(string1,stringg2);   /* Bei 0 sind sie gleich, sonst willst du das umgekehrte Verhalten */
    


  • Ja aber es ist halt die maximale Länge, also sehe ich da kein Problem oder?

    Grundsätzlich funktioniert das sortieren ja, nur dass er mir nur den ersten char nach Groß- und Kleinschreibung sortiert (also klein vor groß), aber warum ist die Frage? Ich vergleiche ja eh die kompletten Strings...

    MFG



  • Zoltamor schrieb:

    Ja aber es ist halt die maximale Länge, also sehe ich da kein Problem oder?

    Sehr schlechte Einstellung.

    Strings enden beim Stringendezeichen '\0' und nicht nach 254 Zeichen wie bei dir.

    Einmal richtig programmiert und du kannst es immer wieder ohne Änderung verwenden.

    Zudem vergleichst du ja jedes Zeichen einzeln mit einer Stringfunktion.

    Das macht man eigentlich direkt mit den Zeichen:

    result = (int)*(string1+i)-(int)*(string2+i); // Der * ist kein mal sondern das Dereferenzierungszeichen
    

Anmelden zum Antworten