[erledigt] Head First C: manuelles Terminieren eines Strings

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
Benutzeravatar
paedubucher
Beiträge: 937
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

[erledigt] Head First C: manuelles Terminieren eines Strings

Beitrag von paedubucher » 01.05.2016 12:36:31

Aus Neugier habe ich mir seit vielen Jahren wieder einmal ein Buch aus der "Head First"-Serie von O'Reilly gekauft, nämlich Head First C. Der Einstieg ist zu einem grossen Teil Repetition für mich, da und dort lerne ich aber doch etwas Neues. Beim Kapitel über Strings bin ich aber etwas in Grübeln gekommen.
Zunächst geht es um folgenden Code, womit ein Suchbegriff eingelesen werden soll:

Code: Alles auswählen

char search_for[80];
printf("Search for: ");
scanf("%79s", search_for); // ich hätte fgets(search_for, 80, stdin); gemacht
search_for[strlen(search_for) - 1] = '\0'; // die entscheidende Zeile
find_track(search_for);
Bei der zweitletzten Zeile bin ich ins Grübeln gekommen.
  • Mit fgets wäre der String garantiert null-terminiert (richtig?)
  • Schaut strlen auf den Speicher oder arbeitet es mit der Nullterminierung? In letzterem Fall wäre es ja völlig sinnlos...
  • Der Code ist falsch: das letzte Zeichen meines Suchbegriffs wird immer mit '\0' überschrieben.
Hier der ganze "korrekte" Code:

Code: Alles auswählen

#include <stdio.h>
#include <string.h>

char tracks[][80] = {
    "I left my heart in Harvard Med School",
    "Newark, Newark ‒ a wonderful town",
    "Dancing with a Dork",
    "From here to maternity",
    "The girl from Iwo Jima",
};

void find_track(char search_for[])
{
    int i;
    for (i = 0; i < 5; i++) {
        if (strstr(tracks[i], search_for)) {
            printf("Track %i: '%s'\n", i, tracks[i]);
        }
    }
}

int main()
{
    char search_for[80];
    printf("Search for: ");
    scanf("%79s", search_for);
    int len = strlen(search_for);
    printf("%d\n", len);
    search_for[len - 1] = '\0';
    find_track(search_for);
    return 0;
}
Der Suchbegriff wird in allen möglichen Strings im globalen Array gefunden. Und es scheint (gemäss Beispiel im Buch) auch zu funktionieren:

Code: Alles auswählen

./text_search
Search for: town
Track 1: 'Newark, Newark ‒ a wonderful town'
Da aber das letzte Zeichen des Suchbegriffes jeweils mit '\0' überschrieben wird, scheint das Programm nicht in jedem Fall zu funktionieren:

Code: Alles auswählen

./text_search
Search for: X
Track 0: 'I left my heart in Harvard Med School'
Track 1: 'Newark, Newark ‒ a wonderful town'
Track 2: 'Dancing with a Dork'
Track 3: 'From here to maternity'
Track 4: 'The girl from Iwo Jima'
Es wird einfach nach einem leeren String gesucht, der überall zu finden ist.
Wenn ich das manuelle Terminieren mit '\0' weglasse, muss ich dann fgets verwenden, oder funktioniert es auch mit dem scanf-Ansatz?
Zuletzt geändert von paedubucher am 01.05.2016 19:33:08, insgesamt 2-mal geändert.
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Benutzeravatar
paedubucher
Beiträge: 937
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Head First C: manuelles Terminieren eines Strings?

Beitrag von paedubucher » 01.05.2016 12:41:25

In den Errata habe ich gerade folgendes gefunden:
The sample code for the jukebox application uses the "fgets" function to get the user's search string. However, fgets will add the newline (\n) character to the end of the string when the user presses the enter key to submit their search string. This newline character must be removed and replaced with the null character (\0) in order for it to actually match one of the song titles in the tracks[] array. Without replacing the newline character, the code as listed on this page does not work (however, the screenshot on the next page shows it functioning properly).
Wenn der Code wirklich fgets verwenden würde, dann wäre die fragliche Zeile ja eine Ersetzung von '\n' durch '\0'. Mit scanf wird aber kein '\n' eingelesen. Da scheint mir ein ziemliches Chaos veranstaltet worden zu sein...
Nachtrag: Da gibt es noch unconfirmed errata. :facepalm:
Da wurde wieder mal mit heisser Nadel gestrickt...
Meine Verständnisfrage bleibt dennoch: strlen sucht doch nach '\0', was das manuelle Terminieren im Beispielcode ja sinnlos machen würde, richtig?
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

JTH
Moderator
Beiträge: 3077
Registriert: 13.08.2008 17:01:41
Wohnort: Berlin

Re: Head First C: manuelles Terminieren eines Strings?

Beitrag von JTH » 01.05.2016 14:42:40

paedubucher hat geschrieben:Meine Verständnisfrage bleibt dennoch: strlen sucht doch nach '\0', was das manuelle Terminieren im Beispielcode ja sinnlos machen würde, richtig?
Richtig, es ist überflüssig. scanf() null-terminiert die gefundene Zeichenkette immer. Im Gegenteil, wie du schon bemerkt hast, ersetzt du damit das letzte Nicht-Null-Zeichen in dem eingegebenen String. In deinem Beispiel, wo du nur nach „X“ suchst, ersetzt du damit sogar das einzige Zeichen in dem String.
Manchmal bekannt als Just (another) Terminal Hacker.

Benutzeravatar
paedubucher
Beiträge: 937
Registriert: 22.02.2009 16:19:02
Lizenz eigener Beiträge: GNU Free Documentation License
Wohnort: Schweiz
Kontaktdaten:

Re: Head First C: manuelles Terminieren eines Strings?

Beitrag von paedubucher » 01.05.2016 19:32:25

JTH hat geschrieben:
paedubucher hat geschrieben:Meine Verständnisfrage bleibt dennoch: strlen sucht doch nach '\0', was das manuelle Terminieren im Beispielcode ja sinnlos machen würde, richtig?
Richtig, es ist überflüssig. scanf() null-terminiert die gefundene Zeichenkette immer. Im Gegenteil, wie du schon bemerkt hast, ersetzt du damit das letzte Nicht-Null-Zeichen in dem eingegebenen String. In deinem Beispiel, wo du nur nach „X“ suchst, ersetzt du damit sogar das einzige Zeichen in dem String.
Vielen Dank für deine Antwort. Ich habe testhalber einmal den eingelesenen Suchstring ausgegeben, und gesehen, dass er leer ist, wenn man nur ein Zeichen eingibt. Tolle Wurst, O'Reilly...
Habe nun, ach! Java
Python und C-Sharp,
Und leider auch Visual Basic!
Durchaus programmiert mit heissem Bemühn.
Da steh' ich nun, ich armer Tor!
Und bin so klug als wie zuvor.

Antworten