Ersetzen von Tabulator durch Leerzeichen



  • Hi,

    war wohl doch zu spät gestern, mhm ...

    Irgendwie habe ich das ganze auch total falsch interpretiert und jetzt ist es glaube ich doch schon um einiges klarer.

    Das ganze soll ja eh nur in der Konsole ausgegeben werden, von daher.

    Das ganze sieht jetzt so bei mir aus:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void expand(FILE *filePointer);
    
    int main(int argc, char *argv[])
    {
    	FILE *fileStream;
    
    	if (argc < 2)
    	{
    		printf("Bitte geben sie einen Text ein: \n\n");
    		expand(stdin);
    	}
    	else 
    	{
    		fileStream = fopen(argv[1], "r");
    
    		if(fileStream.good())
    		{						
    			expand(fileStream);
    			fclose(fileStream);
    		}
    		else
    			exit(1);
    	}
    }
    
    void expand(FILE *filePointer)
    {
    	int character = 0;
    
    	if (filePointer != NULL) 
    	{
    		while((character = fgetc(filePointer)) != EOF)
            {
            	if (character == '\\') // oder c = 92
    			{ 
    				character = fgetc(filePointer);
    
    				if (character == 't') // oder c == 116
    				{ 
    					printf("\t");
    				}
    				else 
    				{
    					putchar(92); // entspricht \
    					putchar(character);
    				}
    			}
    			else 
    			{
    				putchar(character); // Die Zeichen ausgeben
    			}
    		}
    	}
    	else 
    		printf("Dateiname existiert nicht");
    }
    

    Das ganze funktioniert auch soweit aber wie kriege ich das jetzt mit der main argv[1] hin. Soweit ich weiß steht das ja für eine Datei aber wie gebe ich da eine Datei mit rein?
    Das verstehe ich leider noch nicht so ganz aber dass das 2 Argument für die Datei steht weiß ich.

    Warum ich versucht habe einen Nullpointer zu schließen kann ich dir auch nicht sagen aber ich würde sagne, dass das Programm dann absürzt oder dergleichen. 😃

    Ist das so sonst soweit okay?
    Leider komme ich nur nicht mit dieser argv[] Geschichte weiter.



  • Das Programm erfüllt nicht die Aufgabe.

    fileStream.good() ist kein C.

    Zu argv[1]:

    Öffne eine Konsole, wechsele in das Verzeichnis in dem dein Programm liegt.
    Da kannst du dann noch zusätzliche Angaben beim Programmstart machen.

    Du kannst aber auch in der IDE das Programm so starten, dass die Parameter abgefragt oder fest eingegeben werden.

    Entweder '\' oder 92 aber nicht gemischt. Darum '\'



  • Djangou schrieb:

    Das ganze funktioniert auch soweit

    Das bezweifle ich.

    Djangou schrieb:

    aber wie kriege ich das jetzt mit der main argv[1] hin

    Aha, also doch nicht.
    Also läuft dein Programm denn nun oder nicht?

    if(fileStream.good())
    

    Das ist C++ Schrott.

    2x fgetc in der Schleife ist ebenso Müll, du wertest die Zeichenkette "\\t" aus und nicht das Zeichen '\t'.
    Außerdem ist die Aufgabenstellung Müll, bei Ausgabe eines '\t' auf stdout wird (normalerweise) bereits eine Interpretation vorgenommen und eine (nicht im C-Standard definierte) Anzahl Leerzeichen ausgegeben, d.h. du brauchst selbst nichts mehr machen als einfach jedes einzeln gelesene Zeichen auf stdout gleich wieder auszugeben.


  • Mod

    Wutz schrieb:

    Außerdem ist die Aufgabenstellung Müll, bei Ausgabe eines '\t' auf stdout wird (normalerweise) bereits eine Interpretation vorgenommen und eine (nicht im C-Standard definierte) Anzahl Leerzeichen ausgegeben, d.h. du brauchst selbst nichts mehr machen als einfach jedes einzeln gelesene Zeichen auf stdout gleich wieder auszugeben.

    Nein.



  • Doch, es funktioniert soweit, nur halt mit dem argv[1] konnte ich das noch nicht testen. Ich habe das auch mit dem .good() entfernt und dann noch beim fopen geprüft, ob es ungleich NULL ist. Wie kann ich denn prüfen, ob die Datei gelesen wurde @SeppJ?

    Ich arbeite übirgens mit der devc++ ide. Wie kann ich denn dann über die cmd zusätzliche Angaben zum Programmstart machen?

    Kann ich denn direkt auch \\t überprüfen, weil ich ja nur einen char benutze?



  • Kennst du das Konsolenfenster bzw. die cmd-shell?
    Befehle wie cd, dir oder copy?

    Du sollst in einer Datei alle Vorkommen von '\t' durch Leerzeichen ersetzen.
    Dabei steht '\t' für ein Zeichen mit dem Wert 9 (So wie der \ 92 hat)
    Damit das nicht ganz so einfach ist, ist die Anzahl nicht fest, sondern abhängig von der Position in der Zeile.
    Spiel mal mit der Tabulatortaste in deinem Editor rum.



  • Djangou schrieb:

    Doch, es funktioniert soweit

    Wie kann das sein, wenn ich es nicht mal kompiliert bekomme?

    In Funktion »main«:
    21:22: Fehler: Anfrage nach Element »good« in etwas, was keine Struktur oder Variante ist
    

    Sag mir, wie du das kompiliert bekommen hast, ja?

    Djangou schrieb:

    Wie kann ich denn prüfen, ob die Datei gelesen wurde

    Prüfung auf (void*)0 (auch bekannt als NULL ) sollte reichen.

    Djangou schrieb:

    nur halt mit dem argv[1] konnte ich das noch nicht testen.

    Allein vom Code kann ich ja schon sehen, dass das so nicht funktionieren KANN. '\t' ist eine Escape-Sequenz, die besteht aus einem Byte:

    $ perl -e 'printf("\t");' | hd
    00000000  09                                                |.|
    00000001
    

    Ein Byte: 0x09. Du versuchst aber zwei Bytes einzulesen und zu interpretieren. Entweder hast du nicht getestet oder du lügst uns an.



  • dachschaden schrieb:

    Entweder hast du nicht getestet oder du lügst uns an.

    Das kann man aber auch etwas freundlicher sagen, Herr dachschaden.



  • SeppJ schrieb:

    *: Der Standard (K.3.5.4.1, 6) empfiehlt aber ausdrücklich, fgets zu benutzen! Bin fast vom Stuhl gefallen, als ich gelesen habe, dass der Standard sich so weit vor wagt, konkrete Empfehlungen zu machen!

    Die Implementierung von gets_s (und dem ganzen anderen _s-Krempel) ist doch optional, oder? Von daher ist die Empfehlung doch durchaus nachvollziehbar, auch wenn sie im Rahmen eines solchen Dokuments abgegeben wird.



  • @dachschade
    oder du hast nicht richtig gelesen ...
    Ich habe oben geschrieben, das ich das mit dem good entfernt habe.

    Also muss man das mit dem char_ = fgetc(fP);
    durch if(char_ = fgetc(fP) != NULL)
    ersetzen?

    Ich habe auch ein Beispiel gekriegt und die Ausgabe sieht bei mir genau so aus, also so:

    Eingabe
    567\tabc
    234567\tabc
    23456789\tabc
    123456789012345\tabc

    Ausgabe
    567 abc
    234567 abc
    23456789 abc
    123456789012345 abc

    Also läuft das ganze doch richtig?

    @EOP
    Danke aber ich brauche niemanden der mich hier beschützt. Letztendlich ist es ja sowieso "unpersönlich" und daraus mache ich mir dann eh nicht viel. 😉
    Allerdings weiß man ja auch nie, wie das ganze wirklich gemeint ist.



  • Ich habe mal die Ausgabe in Code-Tags gesetzt. Da bleiben die Leerzeichen und Tabulatoren erhalten.

    Djangou schrieb:

    Ich habe auch ein Beispiel gekriegt und die Ausgabe sieht bei mir genau so aus, also so:

    Eingabe
    567\tabc
    234567\tabc
    23456789\tabc
    123456789012345\tabc

    Ausgabe

    567	abc
    234567	abc
    23456789	abc
    123456789012345	abc
    

    Also läuft das ganze doch richtig?

    In der Ausgabe sind Tabulatoren (zwischen den Ziffern und den Buchstaben).

    In deinem ersten Post hieß es noch:

    Aufgabe für Djangou schrieb:

    Dabei ist aber jeder Tabulator (in C dargestellt durch '\t') in die entsprechende Anzahl Leerzeichen umzuwandeln.

    Wo sind also die Leerzeichen? Da sind Tabulatoren drin.

    Deine Aufgabe ist es, das was du da als Ausgabe hast in einen Text ohne Tabulatoren zu wandeln, der aber genauso aussieht.

    \t ist eine Escapesequenz (aus zwei Zeichen). Diese wird vom Compiler in das eine Steuerzeichen mit dem Wert 9 umgewandelt. DArum geht es hier aber nicht.
    Du sollst das ZEichen mit dem Wert 9 in eine Anzahl Leerzeichen umwandeln.

    Öffne mal den Editor von Windows (notepad.exe) und gib den Text (Eingabe von oben) ein
    Statt \t drückst du die Tabulatortaste (links neben Q). Abspeichern nicht vergessen.
    Du kannst jetzt den Zwischenraum bei den Tabulatoren nur als ganzes auswählen.
    In der Ausgabe sollen da stattdessen Leerzeichen stehen. Da kannst du dann jedes Leerzeichen einzeln auswählen.



  • Hmm ... stimmt.
    
    Wie kann man das denn am besten machen?
    Ich hatte jetzt einmal versucht die Zeichen zu zählen und demnach zu reagieren aber das funktioniert aber leider nicht so ganz.
    
    So sieht das ganze derzeit aus: (Ich habe mal die main weggelassen, weil sich ja nichts geändert hat)
    
    void expand(FILE *filePointer)
    {
    	int i = 0;
    	int counter = 0;
    	int character = 0;
    
    	if(filePointer != NULL) 
    	{	
    		while((character = fgetc(filePointer)) != EOF)
            {        
    			counter++;	
    
    			if(counter >= 8)	
    				counter = 0;
    
            	if(character == '\\') // oder char = 92
    			{ 			
    				counter--;
    				character = fgetc(filePointer);
    				counter++;
    
    				if(counter >= 8)	
    					counter = 0;
    
    				if(character == 't') // oder char == 116
    				{ 					
    					counter--;
    
    					if(counter >= 8)	
    						counter = 0;
    
    					switch(counter)
    					{
    						case 0:
    							i = 8;
    							break;
    						case 1:
    							i = 7;
    							break;
    						case 2: 
    							i = 6;
    							break;
    						case 3: 
    							i = 5;
    							break;
    						case 4:
    							i = 4;
    							break;
    						case 5: 
    							i = 3;
    							break;
    						case 6: 
    							i = 2;
    							break;
    						case 7: 
    							i = 9;
    							break;
    					}
    
    					while(i != 1)
    					{
    						// printf("%d   ", counter);
    						putchar(32);
    						printf("%d", i);
    						i--;
    					}
    						//putchar(32); printf("\t");
    				}
    				else if(character == 'n')
    				{
    					counter = 0;
    				}
    				else 
    				{
    					putchar(92); // entspricht einem \
    					putchar(character);
    				}		
    			}
    			else 
    				putchar(character); // Die Zeichen ausgeben
    		}
    	}
    	else 
    		printf("Die Datei existiert nicht oder Sie konnte nicht geöffnet werden!");
    }
    
    567	abc
    234567	abc
    23456789	abc
    123456789012345	abc
    


  • So soll das ganze nachher aussehen:

    http://i.imgur.com/wnVMwDi.png



  • In deiner Eingabedatei (wenn sie richtig ist), kommt weder das Zeichen \ noch t noch n drin vor.
    Also sind deine Vergleiche (Zeile 24, 34 und 78) nicht richtig.
    (Komischerweise war der Vergelich in deinem ersten Post richtig.)

    Sehrwohl kommen der Tabulator und und Newline drin vor.

    Und bei deinem switch gibt es einen Zusammenhang zwischen counter, i und der Zahl mit der du counter immer vergleichst.
    Diesen Zusammenhang kann man mit einer Subtraktion ausdrücken.

    Komischerweise war der Vergelich in deinem ersten Post richtig



  • @EOP: Könnte ich. Aber warum sollte ich?

    @OP: Schau dir mal diese kleine Referenzimplementierung an:

    #include <limits.h> /*Fuer size_t*/
    #include <string.h> /*Fuer strlen*/
    #include <stdlib.h> /*Fuer die EXIT_*-Konstanten*/
    #include <stdio.h>  /*Fuer printf und solche Spaesse.*/
    
    /*Seit mich DirkB darauf aufmerksam gemacht hat, dass %lu nicht immer korrekt
    **ist, aber da %z erst in C99 vorgestellt wurde ... :p*/
    #if defined(__x86_64__) || defined(_M_X64)
     #ifdef _WIN64
      #ifdef __CYGWIN__
       #define SIZE_T_PRINTF "%lu"
      #else
       #define SIZE_T_PRINTF "%I64u"
      #endif
     #else
      #define SIZE_T_PRINTF "%lu"
     #endif
    #else
     #define SIZE_T_PRINTF "%u"
    #endif
    
    /*Anzahl der Zeichen, die ein Tabstopp wert ist.*/
    #define SPACES_PER_TAB (4)
    
    /*Zeichen, welches das Ersetzen auslösen soll.*/
    #define SEARCH_CHAR '\t'
    
    int main(int argc,char*argv[])
    {
            size_t index_args,
                    index_string,
                    index_string_visual,
                    spaces_current,
                    spaces_left_to_print,
                    string_length;
    
            /*Keine Daten angegeben.*/
            if(argc<2)
            {
                    fprintf(stderr,"Usage: %s <parameters>\n",argv[0]);
                    exit(EXIT_FAILURE);
            }
    
            for(index_args=1;index_args<argc;index_args++)
            {
                    /*Anfang des Strings.*/
                    printf("Chunk No. " SIZE_T_PRINTF "\n"
                            "===============================================================================\n",
                            index_args);
    
                    /*Ansgabe mit gleichzeitiger Ersetzung.*/
                    string_length=strlen(argv[index_args]);
                    for(index_string=0,index_string_visual=0;index_string<string_length;index_string++,index_string_visual++)
                    {
                            if(argv[index_args][index_string]==SEARCH_CHAR)
                            {
                                    spaces_current=0;
                                    spaces_left_to_print=SPACES_PER_TAB-index_string_visual%SPACES_PER_TAB;
    
                                    while(spaces_current++<spaces_left_to_print)
                                            printf(" ");
                                    index_string_visual+=spaces_left_to_print-1;
                            }
                            else
                                    printf("%c",argv[index_args][index_string]);
                    }
    
                    /*Ende des Strings.*/
                    printf("\n\n");
            }
    
            /*Alles lief besser als erwartet.*/
            exit(EXIT_SUCCESS);
    }
    

    Ist nicht das komplette Program, funktioniert aber:

    $ perl -e 'printf("\t 131312 \t13\t\t123 12s3r12j13aj\tddsad")' | xargs -0 ./tab 
    Chunk No. 1
    ===============================================================================
         131312     13      123 12s3r12j13aj    ddsad
    

    Wenn man einen Tabsize von 8 eingibt, funzt es auch:

    $ perl -e 'printf("\t 131312 \t13\t\t123 12s3r12j13aj\tddsad")' | xargs -0 ./tab
    Chunk No. 1
    ===============================================================================
             131312         13              123 12s3r12j13aj        ddsad
    

    War aber jetzt schnell zusammengehackt. Ich übernehme also keine Garantie für Fehler.

    EDIT: Ich soll mich hinlegen, habe die Prüfung für VS-Compiler vergessen. 😕



  • dachschaden schrieb:

    /*Seit mich DirkB darauf aufmerksam gemacht hat, dass %lu nicht immer korrekt
    **ist, aber da %z erst in C99 vorgestellt wurde ... :p*/
    #ifdef __x86_64__
    #define SIZE_T_PRINTF "%lu"
    #else
    #define SIZE_T_PRINTF "%u"
    #endif
    

    Wofür soll das sein?
    Was bewirkt das?
    Warum verzichtest du auf C99?

    Warum erkärst du nicht, was dein Programm anders macht gegenüber der Aufgabenstellung?



  • DirkB schrieb:

    Warum verzichtest du auf C99?

    Diese Diskussion hatten wir bereits.

    DirkB schrieb:

    Warum erkärst du nicht, was dein Programm anders macht gegenüber der Aufgabenstellung?

    Weil der OP hier eine Teillösung bekommt und lernen soll, diese selbstständig in seine Aufgabenstellung einzubinden.



  • dachschaden schrieb:

    DirkB schrieb:

    Warum verzichtest du auf C99?

    Diese Diskussion hatten wir bereits.

    Wenn du damit eine Kompatibilität zu alten C-Compilern von Microsoft haben willst, dann funktioniert das so nicht.

    __x86_64__ ist bei Visual-Studio nicht definiert.
    long ist auch auf Windows bei 64-Bit-Programmen "nur" 32-Bit groß.
    size_t ist aber auf Windows bei 64-Bit-Programmen durchaus 64-Bit groß.

    Bei anderen Compilern als GCC bekommst du also %u. Und das ist aber auch nicht immer richtig.



  • DirkB schrieb:

    __x86_64__ ist bei Visual-Studio nicht definiert.

    Hast recht, habe noch die Prüfung auf _M_X64 eingefügt und %I64u verwendet.

    DirkB schrieb:

    long ist auch auf Windows bei 64-Bit-Programmen "nur" 32-Bit groß.
    size_t ist aber auf Windows bei 64-Bit-Programmen durchaus 64-Bit groß.

    Das weiß ich. Deswegen verwende ich auch nur size_t.



  • dachschaden schrieb:

    DirkB schrieb:

    __x86_64__ ist bei Visual-Studio nicht definiert.

    Hast recht, habe noch die Prüfung auf _M_X64 eingefügt.

    Dann ist das immer noch falsch.

    dachschaden schrieb:

    DirkB schrieb:

    long ist auch auf Windows bei 64-Bit-Programmen "nur" 32-Bit groß.
    size_t ist aber auf Windows bei 64-Bit-Programmen durchaus 64-Bit groß.

    Das weiß ich.

    Anscheinend nicht. %lu steht für unsigned long .
    Was hat das mit size_t zu tun, wenn _M_X64 definiert ist?
    Und was hat %u mit size_t in den anderen Fällen zu tun?

    dachschaden schrieb:

    Deswegen verwende ich auch nur size_t.

    Wenn man davon ausgehen kann, dass int ausreicht, dann nimmt man int ²
    Und in deinem Programm reicht int aus.


Anmelden zum Antworten