K&R Übung 1-9: Ausgabe der Eingabe, wobei mehrere Leerzeichen durch ein einzelnes Leerzeichen ersetzt werden
Lesezeit: 8 Minuten
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
Bin_ich_hilfreich
In der for-Schleife-Anweisung haben Sie den Fehler.
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.
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
alexo_o
Sie rufen getchar zweimal in der Schleifeninitialisierung auf:
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):
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
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:
Beachten Sie, dass Sie initialisieren müssen lch mit einem anderen Wert als Leerzeichen.
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.
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.
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)
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!):
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.
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. EINwhile()
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 auszusetzenfor
.– 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