K&R Übung 1-9: Ausgabe der Eingabe, wobei mehrere Leerzeichen durch ein einzelnes Leerzeichen ersetzt werden

Lesezeit: 8 Minuten

Benutzer-Avatar
slaxx

Ich habe einige Bücher über C durchgearbeitet und versucht, meine C-Beine zu bekommen (Seebeine! Bekommst du es?!). Ich habe gerade Übung 1-9 aus dem K&R-Buch beendet, die als Referenz lautet: “Schreiben Sie ein Programm, um seine Eingabe in seine Ausgabe zu kopieren und jede Zeichenfolge mit einem oder mehreren Leerzeichen durch ein einzelnes Leerzeichen zu ersetzen”. Ich habe allerdings eine Frage dazu, was mit meinem Code los ist…

#include <stdio.h>

//Copy input to output. Replace each string of multiple spaces with one single space

int main(int argc, char *argv[]){

    int ch, lch;      // Variables to hold the current and last characters, respectively


    /* This loop should 'put' the current char, then store the current char in lc,
     * loop back, 'get' a new char and check if current and previous chars are both spaces.
     * If both are spaces, do nothing. Otherwise, 'put' the current char
     */

    for(ch = getchar(); (ch = getchar()) != EOF; lch = ch){
            if(ch == ' ' && lch == ' ')
                    ;
            else putchar(ch);
    }

    return 0;
}

Das funktioniert meistens, bis auf die allererste Zeicheneingabe. Wenn beispielsweise die erste Zeile eingegeben wird

"This        is   a test"

meine Codeausgaben

"his is a test". 

Nach dem Wegfall der allerersten Zeicheneingabe arbeitet das Programm konsequent daran, die Anforderungen der Übung zu erfüllen.

Kann mir jemand eine Vorstellung von dem Fehler geben, den ich in meiner Schleife gemacht habe und der das Problem verursacht? Jeder andere Rat ist auch willkommen.

  • Beachten Sie, dass Sie verwenden lch Variable im Schleifenkörper, obwohl sie bis nicht initialisiert ist nach die erste Schleifeniteration. Erwägen Sie, Warnungen in Ihrem Compiler zu aktivieren, er würde dieses Problem wahrscheinlich erkennen und davor warnen, damit Sie es beheben können.

    – Hyde

    23. Juli 2015 um 21:07 Uhr

  • Kleiner Kommentar zum Codierungsstil: Speichern for() for-Schleifen, die einen Index/Zeiger um einen festen Betrag erhöhen. EIN while() loop ist hier angesagt.

    – chux – Wiedereinsetzung von Monica

    23. Juli 2015 um 21:32 Uhr


  • Ist es wirklich ärgerlich anzudeuten, dass es gut wäre, expliziter zu erwähnen, worauf sich „das K&R-Buch“ bezieht, und dass der Titel der Frage auch besser wäre, wenn er einen Aspekt dessen, worauf sich die Frage bezieht, deutlich machen würde (in direkter Sinn – da das Problem weitgehend unabhängig von seiner Quelle ist und jemand, der C kennt und eine gute Antwort geben könnte, aber noch nichts von “dem K&R-Buch” gehört hat, die Frage überspringen könnte)?

    – jfhc

    24. Juli 2015 um 7:21 Uhr

  • @chux – das ist nicht was for ist für. Es ist für eine Schleife, die einen Initialisierungsteil, einen Bedingungsteil und einen Post-Iterationsteil hat. Die Arbeit mit Iteratoren kommt einem sofort in den Sinn. Soweit ich das beurteilen kann, ist an seiner Verwendung von nichts auszusetzen for.

    – Dawor

    24. Juli 2015 um 9:35 Uhr


  • @jfhc Wo schlagen Sie vor, einen C-Programmierer zu finden, der noch nichts vom traditionellen Standardbuch gehört hat? Wenn es sie gibt, ist es unwahrscheinlich, dass sie die Frage in ihrem richtigen historischen Kontext beurteilen können.

    – Dreier

    24. Juli 2015 um 10:04 Uhr


Benutzer-Avatar
Bin_ich_hilfreich

In der for-Schleife-Anweisung haben Sie den Fehler.

for(ch = getchar(); (ch = getchar()) != EOF; lch = ch){...}

Hier speichern Sie das erste Zeichen in ch und testen dann erneut, ob (ch! = EOF), indem Sie die Zeicheneingabe erneut lesen.

Entfernen ch=getchar() aus der Initialisierungsanweisung; Lass es im zweiten Teil sein.

for(;(ch = getchar()) != EOF; lch = ch){...}

Außerdem müssen Sie Ihren lch initialisieren, bevor Sie ihn ausführen, da in lch kein Wert gespeichert ist, bevor Sie in der ersten Iteration der Schleife einen Vergleich durchführen. Also lass lch=0 zuerst initialisiert werden.

for(lch = 0; (ch = getchar()) != EOF; lch = ch){...}

Erwägen Sie, Warnungen in Ihrem Compiler zu aktivieren, er würde dieses Problem wahrscheinlich erkennen und davor warnen, damit Sie es beheben können.

Das obige würde dein Problem lösen.

(Danke an Blue Moon und Hyde, die mir geholfen haben, die Antwort zu ändern.)

  • Ein weiteres Problem ist das lch wird während der ersten Iteration nicht initialisiert. Daraus ergibt sich UB.

    – PP

    23. Juli 2015 um 21:07 Uhr

  • @BlueMoon-Danke, habe nicht genau hingesehen. Erwähnte Ihren Namen in der Antwort für die bereitgestellte Hilfe.

    – Bin_ich_hilfreich

    23. Juli 2015 um 21:11 Uhr

  • Zwei Stilpunkte zum Hinzufügen: 1. Verwenden ; da die null-Anweisung eine schlechte Idee ist, besser zu verwenden {}, so ist es offensichtlich beabsichtigt. 2. Es ist noch besser, die if-Anweisung umzukehren, damit die nicht benötigt wird else Teil.

    – Deduplizierer

    24. Juli 2015 um 14:40 Uhr

  • @Deduplicator-Danke für den Hinweis. Übrigens, Sie könnten entweder selbst die erforderliche Bearbeitung vornehmen oder versuchen, sie als neue Antwort zu posten. Trotzdem nochmal DANKE für die Verbesserungsvorschläge…

    – Bin_ich_hilfreich

    24. Juli 2015 um 14:44 Uhr

  • for(;( = …);…) … Ah ja, ich wusste, dass es einen Grund für all diese C-Bücher gab, dass seine Syntax die am schrecklichsten missbrauchte Sache auf dem Planeten ist.

    – Darren Ringer

    24. Juli 2015 um 22:36 Uhr

Benutzer-Avatar
alexo_o

Sie rufen getchar zweimal in der Schleifeninitialisierung auf:

 for(ch = getchar(); (ch = getchar()) != EOF; lch = ch)

Stattdessen sollten Sie es einmal in der Initialisierung aufrufen (um das erste Zeichen zu erhalten) und dann am Ende der Iteration (um die nächsten Zeichen zu erhalten):

int ch, lch = 0; // avoid using uninitialized variable

for(ch = getchar(); ch != EOF; lch = ch)
{
        if(ch == ' ' && lch == ' ')
                ;
        else putchar(ch);

        ch = getchar();
} 

AKTUALISIERT: Vielen Dank an Blue Moon und Shekhar Suman für den Hinweis auf das Problem mit lch

  • Dies scheint mir die einzige Antwort zu sein, die es tatsächlich für Nicht-C-Programmierer lesbar macht (ohne viel mentale Gymnastik).

    – Darren Ringer

    24. Juli 2015 um 22:37 Uhr

Benutzer-Avatar
Sergej Kalinitschenko

Das Problem ist, dass die erste Iteration Ihrer Schleife aufruft getchar zweimal – einmal beim Initialisieren der ch Variable, und noch einmal bei der Überprüfung ch gegen EOF.

Fallenlassen ch = getchar() wird dieses Problem beheben:

for( lch="?" ; (ch = getchar()) != EOF; lch = ch) {
    ...
}

Beachten Sie, dass Sie initialisieren müssen lch mit einem anderen Wert als Leerzeichen.

Benutzer-Avatar
QUentin

Du rufst an getchar() einmal vor Beginn der Schleife, dann einmal pro Iteration in der for Bedingung. Das erste abgerufene Zeichen wird somit verworfen.

Sie müssen auch initialisieren lch vor dem Schleifen, vor dem Vergleichen. Abhängig davon, was Sie tun möchten, wenn das erste Zeichen Ihrer Zeichenfolge ein Leerzeichen ist:

  • Einstellen auf ' ' wird den führenden Leerraum durch “Pre-Matching” kürzen.
  • Wenn Sie es auf etwas anderes setzen, wird das führende Leerzeichen normal behandelt.

Ihr Loop-Header wird (im zweiten Fall):

 for(lch="a" /*arbitrary*/; (ch = getchar()) != EOF; lch = ch)

Danke an Shekar Suman für die Hinweise zu den nicht initialisierten lch.

Benutzer-Avatar
Vlad aus Moskau

Ändern Sie diese Schleife

for(ch = getchar(); (ch = getchar()) != EOF; lch = ch){
        if(ch == ' ' && lch == ' ')
                ;
        else putchar(ch);
}

folgenden Weg

for( lch = EOF; ( ch = getchar() ) != EOF; lch = ch )
{
        if ( ch != ' ' || lch != ' ' ) putchar( ch );
}

Andernfalls lesen Sie am Anfang der Schleife ein Zeichen zweimal.

Auch meiner Meinung nach beschreibt die Aufgabe eine andere Aufgabe

“Schreiben Sie ein Programm, um seine Eingabe in seine Ausgabe zu kopieren und jede Zeichenfolge mit einem oder mehreren Leerzeichen durch ein einzelnes Leerzeichen zu ersetzen.”

Sie sollten jede ganze Zeile mit Leerzeichen durch ein einzelnes Leerzeichen ersetzen.:) Die oben gezeigte Schleife erfüllt diese Aufgabe nicht.

Benutzer-Avatar
ggg

Sofern die Aufgabe nicht darin besteht, dies mit einer for-Schleife zu tun, ist es zum Erlernen der Sprache besser, wenn Sie versuchen, saubereren Code zu erhalten. Sagen Sie sich einfach, was der Code tut, vergleichen Sie zum Beispiel die äquivalente while-Schleife mit der for-Schleife:

//initialize lch to prevent undefined behaviour
//if the first character is a space, it will be printed
lch="A";

// as long as you can read characters
while((ch = getchar()) != EOF) {

    // if either the current character or the previous one is not a space
    if(ch!=' ' || lch!=' ') { 

        //print it
        putchar(ch);
    }

    // remember the current for the next round
    lch = ch;
}

Sobald Sie das While-Konstrukt verstanden haben, können Sie es auch in die hackige For-Schleife umwandeln, aber warum sollten Sie? Das While ist einfacher zu lesen und dem Compiler ist es egal, da beide auf die gleiche Weise kompiliert werden. (wahrscheinlich)

Benutzer-Avatar
Deduplikator

Obwohl es viele richtige Antworten gibt, möchte ich Ihnen einen Hinweis geben, wie Sie dies selbst mit einem Debugger (gdb hier) hätten aufspüren können:

Ändern Sie zuerst den Code so, dass er so aussieht (nur eine Anweisung pro Zeile!):

...

for(ch = getchar(); 
   (ch = getchar()) != EOF; 
   lch = ch){

...

Kompilieren Sie es jetzt mit Symbolen (-g für gcc), führen Sie dann den Code mit einem Debugger aus:

 gdb ./a.out

Setzen Sie einen Haltepunkt bei main():

(gdb) break main

Starten Sie das Programm:

(gdb) run

Sehen Sie, wie es aufhört main():

Breakpoint 1, main (argc=1, argv=0x7fffffffe448) at main.c:15
15      for(ch = getchar(); 
(gdb) 

Gehen Sie den Code durch:

(gdb) step

Verwenden print ch von der gbd-Befehlszeile aus, um interessante Variablen zu untersuchen (ch hier) in verschiedenen Stadien des “laufenden” Codes, während Sie ihn durchlaufen.

Weitere Details zur Steuerung von gbd finden Sie hier: http://beej.us/guide/bggdb/

1386290cookie-checkK&R Übung 1-9: Ausgabe der Eingabe, wobei mehrere Leerzeichen durch ein einzelnes Leerzeichen ersetzt werden

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy