Ich versuche, einige Daten vom Benutzer abzurufen und an eine andere Funktion in gcc zu senden. Der Code ist ungefähr so.
printf("Enter your Name: ");
if (!(fgets(Name, sizeof Name, stdin) != NULL)) {
fprintf(stderr, "Error reading Name.\n");
exit(1);
}
Ich finde jedoch, dass es \n
am Ende einen Zeilenumbruch hat . Wenn ich also eintrete, wird John
es gesendet John\n
. Wie entferne ich das \n
und sende eine richtige Zeichenfolge.
if (!fgets(Name, sizeof Name, stdin))
(Verwenden Sie zumindest nicht zwei Negationen! und! =)if (fgets(Name, sizeof Name, stdin)) {
.if (fgets(Name, sizeof Name, stdin) == NULL ) {
!
:Antworten:
Der etwas hässliche Weg:
Der etwas seltsame Weg:
Beachten Sie, dass die
strtok
Funktion nicht wie erwartet funktioniert, wenn der Benutzer eine leere Zeichenfolge eingibt (dh nur die Eingabetaste drückt). Es lässt den\n
Charakter intakt.Natürlich gibt es auch andere.
quelle
strtok()
threadsicher (sie verwendet den lokalen Thread-Speicher für den Status zwischen Aufrufen). Trotzdem ist es im Allgemeinen immer noch besser, die nicht standardmäßige (aber häufig genug)strtok_r()
Variante zu verwenden.strtok
Ansatz (und sie funktioniert mit leeren Eingaben). In der Tat ist eine gute Möglichkeit zu implementierenstrtok
,strcspn
und zu verwendenstrspn
.*strchrnul(Name, '\n') = '\0';
.strchr(Name, '\n') == NULL
dann neben "Eingabe zu lang für Puffer, Flag-Fehler" andere Möglichkeiten bestehen: Der letzte Text instdin
endete nicht mit einem'\n'
oder einem seltenen eingebetteten Nullzeichen.Vielleicht verwendet die einfachste Lösung eine meiner wenig bekannten Lieblingsfunktionen
strcspn()
:Wenn Sie möchten, dass es auch funktioniert
'\r'
(z. B. wenn der Stream binär ist):Die Funktion zählt die Anzahl der Zeichen, bis sie a
'\r'
oder a trifft'\n'
(mit anderen Worten, sie findet das erste'\r'
oder'\n'
). Wenn es nichts trifft, stoppt es bei'\0'
(Rückgabe der Länge der Zeichenfolge).Beachten Sie, dass dies auch dann gut funktioniert, wenn keine neue Zeile vorhanden ist, da
strcspn
bei a angehalten wird'\0'
. In diesem Fall wird die gesamte Linie einfach zu ersetzen'\0'
mit'\0'
.quelle
buffer
als beginnt mit'\0'
etwas , dass Ursachen für die Trauerbuffer[strlen(buffer) - 1] = '\0';
Ansatz.strcspn()
. Eine der nützlicheren Funktionen in der Bibliothek, IMO. Ich habe beschlossen, heute eine Reihe gängiger C-Hacks wie diesen zu schreiben und zu veröffentlichen. Einestrtok_r
Implementierung mitstrcspn
undstrspn
war eine der ersten: codepad.org/2lBkZk0w ( Warnung: Ich kann nicht garantieren, dass sie fehlerfrei ist ; sie wurde hastig geschrieben und hat wahrscheinlich einige). Ich weiß noch nicht, wo ich sie veröffentlichen werde, aber ich beabsichtige, es im Geiste der berühmten "Bit Twiddling Hacks" zu machen.fgets()
. Diesstrcspn()
scheint der einzig richtige Einzeiler zu sein.strlen
ist schneller - wenn auch nicht so einfach.fgets()
Eingabe . Welches ist immer auch die erste Newline.quelle
fgets(buf, size, ....)
->strlen(buf) == 0
. 1)fgets()
liest als ersteschar
a'\0'
. 2)size == 1
3) gibtfgets()
zurück,NULL
dann kann derbuf
Inhalt alles sein. (OPs Codesize_t ln = strlen(name); if (ln > 0 && name[ln-1] == '\n') name[--ln] = '\0';
ln
wäre -1, außer dass die Tatsachesize_t
vorzeichenlos ist und somit in einen zufälligen Speicher schreibt. Ich denke, Sie möchten verwendenssize_t
und überprüfen,ln
ist> 0.strlen
) kann viel effizienter implementiert werden als eine einfache char-by-char-Suche. Aus diesem Grund würde ich diese Lösung besser in Betracht ziehen als einestrchr
oder einestrcspn
basierte.Im Folgenden finden Sie einen schnellen Ansatz zum Entfernen eines Potenzials
'\n'
aus einer von gespeicherten Zeichenfolgefgets()
.Es verwendet
strlen()
mit 2 Tests.Verwenden Sie jetzt
buffer
undlen
nach Bedarf.Diese Methode hat den Nebeneffekt eines
len
Werts für nachfolgenden Code. Es kann leicht schneller sein alsstrchr(Name, '\n')
. Ref YMMV, aber beide Methoden funktionieren.buffer
, aus dem Originalfgets()
wird"\n"
unter Umständen nicht enthalten :A) Die Zeile war zu lang,
buffer
so dass nurchar
vor dem'\n'
gespeichert wirdbuffer
. Die ungelesenen Zeichen bleiben im Stream.B) Die letzte Zeile in der Datei endete nicht mit a
'\n'
.Wenn in die Eingabe
'\0'
irgendwo Nullzeichen eingebettet sind , enthält die von gemeldete Längestrlen()
nicht die'\n'
Position.Einige andere Antworten:
strtok(buffer, "\n");
zu entfernen , schlägt die ,'\n'
wennbuffer
ist"\n"
. Aus dieser Antwort - nach dieser Antwort geändert, um vor dieser Einschränkung zu warnen.Das Folgende schlägt in seltenen Fällen fehl, wenn das erste
char
Lesen vonfgets()
ist'\0'
. Dies geschieht, wenn die Eingabe mit einem Embedded beginnt'\0'
. Dannbuffer[len -1]
wird derbuffer[SIZE_MAX]
Zugriff auf Speicher sicherlich außerhalb des legitimen Bereichs vonbuffer
. Etwas, das ein Hacker versuchen oder finden könnte, wenn er törichterweise UTF16-Textdateien liest. Dies war der Stand einer Antwort, als diese Antwort geschrieben wurde. Später wurde es von einem Nicht-OP so bearbeitet, dass es Code wie die Antwort dieser Antwort enthält""
.sprintf(buffer,"%s",buffer);
ist undefiniertes Verhalten: Ref . Außerdem werden keine führenden, trennenden oder nachfolgenden Leerzeichen gespeichert. Jetzt gelöscht .[Bearbeiten aufgrund einer guten späteren Antwort ] Es gibt keine Probleme mit dem 1-Liner
buffer[strcspn(buffer, "\n")] = 0;
außer der Leistung im Vergleich zumstrlen()
Ansatz. Die Leistung beim Trimmen ist normalerweise kein Problem, da der Code E / A ausführt - ein schwarzes Loch der CPU-Zeit. Wenn der folgende Code die Länge der Zeichenfolge benötigt oder sehr leistungsbewusst ist, verwenden Sie diesenstrlen()
Ansatz. Sonststrcspn()
ist das eine gute Alternative.quelle
strlen(buffer)
wenn die Puffergröße mithilfe von dynamisch zugewiesen wirdmalloc
?buffer = malloc(allocation_size); length = strlen(buffer);
ist schlecht - Daten im Speicher, auf die von gezeigtbuffer
wird, sind unbekannt.buffer = malloc(allocation_size_4_or_more); strcpy(buffer, "abc"); length = strlen(buffer);
ist OKDirekt, um das '\ n' aus der fgets-Ausgabe zu entfernen, wenn jede Zeile '\ n' hat.
Andernfalls:
quelle
strnlen
stattstrlen
.n
erhöht die Sicherheit nicht auf magische Weise. In diesem Fall würde der Code tatsächlich gefährlicher. Ähnlich verhält es sich mitstrncpy
einer schrecklich unsicheren Funktion. Der Beitrag, auf den Sie verlinkt haben, ist ein schlechter Rat.""
) kläglich fehl . Auchstrlen()
kehrtsize_t
nichtint
.Für einzelnes '\ n' Trimmen,
für mehrfaches '\ n' Trimmen,
quelle
if
wenn Sie einfach eine Bedingung mit schreiben können&&
? Diesewhile
Schleife hat eine seltsame Struktur; es könnte einfach seinwhile (length > 0 && string[length-1] == '\n') { --length; string[length] = '\0'; }
.size_t length = strlen(string); if (length > 0 && string[length-1] == '\n') { string[length-1] = '\0'; }
. Dies spiegelt auch die zweite Definition besser wider (nur mitif
stattwhile
).Mein Neuling Weg ;-) Bitte lassen Sie mich wissen, ob das richtig ist. Es scheint für alle meine Fälle zu funktionieren:
quelle
Die Schritte zum Entfernen des Zeilenumbruchs auf die vielleicht offensichtlichste Weise:
NAME
mithilfe derstrlen()
Kopfzeilestring.h
. Beachten Sie, dassstrlen()
das Beenden nicht berücksichtigt wird\0
.\0
Zeichen beginnt oder nur ein Zeichen enthält (leere Zeichenfolge). In diesem Fallsl
wäre,0
da,strlen()
wie ich oben sagte, das nicht zählt\0
und beim ersten Auftreten davon stoppt:'\n'
. Wenn dies der Fall ist, ersetzen Sie\n
durch a\0
. Beachten Sie, dass die Indexzählungen bei beginnen,0
sodass wir Folgendes tun müssenNAME[sl - 1]
:Hinweis: Wenn Sie bei der
fgets()
Zeichenfolgenanforderung nur die Eingabetaste gedrückt haben (der Zeichenfolgeninhalt bestand nur aus einem Zeilenumbruchzeichen), ist dieNAME
Zeichenfolge danach eine leere Zeichenfolge.if
Anweisung zusammenfassen, indem wir den Logikoperator verwenden&&
:Wenn Sie eine Funktion zur Verwendung dieser Technik
fgets
bevorzugen , indem Sie Ausgabezeichenfolgen im Allgemeinen verarbeiten, ohne sie jedes Mal neu einzugeben, finden Sie hierfgets_newline_kill
:In Ihrem Beispiel wäre dies:
Beachten Sie, dass diese Methode nicht funktioniert, wenn in die Eingabezeichenfolge
\0
s eingebettet ist. Wenn dies der Fallstrlen()
wäre, würde nur die Anzahl der Zeichen bis zum ersten zurückgegeben\0
. Dies ist jedoch kein gängiger Ansatz, da die meisten Funktionen zum Lesen von Zeichenfolgen normalerweise beim ersten anhalten\0
und die Zeichenfolge bis zu diesem Nullzeichen verwenden.Abgesehen von der Frage für sich. Vermeiden Sie doppelte Negationen, die Ihren Code unklar machen :
if (!(fgets(Name, sizeof Name, stdin) != NULL) {}
. Sie können einfach tunif (fgets(Name, sizeof Name, stdin) == NULL) {}
.quelle
\n
durch a\0
am Ende einer Zeichenfolge ist eine Möglichkeit, die neue Zeile zu "entfernen". Das Ersetzen von\n
Zeichen innerhalb einer Zeichenfolge ändert die Zeichenfolge jedoch grundlegend. Es ist nicht ungewöhnlich, Zeichenfolgen mit absichtlich mehreren Zeilenumbrüchen zu haben, und dies würde die Enden dieser Zeichenfolgen effektiv abschneiden. Um solche Zeilenumbrüche zu entfernen , muss der Array-Inhalt nach links verschoben werden, um das zu überschreiben\n
.fgets()
?fgets()
. Aber ich verstehe Ihren Einwand nicht: Sie sind derjenige, der Code für die Behandlung mehrerer Zeilenumbrüche vorschlägt.strlen
usw. verwendet habe. Begründung dafür, dass es sich nicht um ein Duplikat handelt: 1. Erläuterung des Codes in Schritten. 2. Wird als funktions- und kontextbasierte Lösung bereitgestellt. 3. Hinweis, um doppelte Negationsausdrücke zu vermeiden.Tim Čas ein Liner ist erstaunlich für Strings, die durch einen Aufruf von fgets erhalten werden, weil Sie wissen, dass sie am Ende eine einzelne Newline enthalten.
Wenn Sie sich in einem anderen Kontext befinden und Zeichenfolgen verarbeiten möchten, die möglicherweise mehr als eine neue Zeile enthalten, suchen Sie möglicherweise nach strrspn. Es ist nicht POSIX, was bedeutet, dass Sie es nicht auf allen Unices finden. Ich habe eine für meine eigenen Bedürfnisse geschrieben.
Für diejenigen, die nach einem Perl-Chomp-Äquivalent in C suchen, ist dies meiner Meinung nach das Richtige (Chomp entfernt nur die nachfolgende Newline).
Die strrcspn-Funktion:
quelle
'\n'
(oder wenn die Zeichenfolge ist""
).strrcspn
wenn es keine gibt\n
.goto end;
statt verwendenreturn len;
?goto
s in Ihrem Code: eine nutzlosegoto
, die durch einereturn
Anweisung ersetzt werden kann, und eine rückwärtsgoto
, die als böse angesehen wird. Die Verwendungstrchr
hilft bei der Implementierungstrrspn
undstrrcspn
auf einfachere Weise:size_t strrspn(const char *s, const char *accept) { size_t len = strlen(s); while (len > 0 && strchr(accept, s[len - 1])) { len--; } return len; }
undsize_t strrcspn(const char *s, const char *reject) { size_t len = strlen(s); while (len > 0 && !strchr(reject, s[len - 1])) { len--; } return len; }
Wenn die Verwendung
getline
eine Option ist - ohne die Sicherheitsprobleme zu vernachlässigen und Zeiger zu klammern -, können Sie Zeichenfolgenfunktionen vermeiden, dagetline
die Anzahl der Zeichen zurückgegeben wird. So etwas wie untenHinweis : Die [ Sicherheitsprobleme ] mit
getline
sollten jedoch nicht vernachlässigt werden.quelle
Die folgende Funktion ist Teil der String-Verarbeitungsbibliothek, die ich auf Github verwalte. Es entfernt und unerwünschte Zeichen aus einer Zeichenfolge, genau das, was Sie wollen
Ein Beispiel für eine Verwendung könnte sein
Vielleicht möchten Sie andere verfügbare Funktionen überprüfen oder sogar zum Projekt beitragen :) https://github.com/fnoyanisi/zString
quelle
*
in entfernen*src++;
und machenbad
,token
undd
const char *
. Auch warum nichtstrchr
statt verwendenzChrSearch
?*src
kann nicht'\0'
in deinerzStrrmv
Funktion sein.strchr
Sie sollten es versuchen. Dieser Code durchläuft im Grunde die Zeichenfolge, bis er das '\ n' findet. Wenn es gefunden wird, wird das '\ n' durch den Nullzeichen-Terminator '\ 0' ersetzt.
Beachten Sie, dass Sie Zeichen und keine Zeichenfolgen in dieser Zeile vergleichen. Dann müssen Sie strcmp () nicht verwenden:
da Sie einfache Anführungszeichen und keine doppelten Anführungszeichen verwenden. Hier ist ein Link zu einfachen oder doppelten Anführungszeichen, wenn Sie mehr wissen möchten
quelle
for(int i = 0; i < strlen(Name); i++ )
Wirdstrlen(Name)
viele Male aufgerufen (SchleifenänderungenName[]
). Mit einer LängeN
ist dies eineO(N*N)
Lösung. Es ist nur ein Anrufstrlen(Name)
erforderlich, um eine O (N) -Lösung bereitzustellen. Unklar, warumint i
statt verwendet wirdsize_t i
. Betrachten Siefor(size_t i = 0; i < Name[i]; i++ )
for (size_t i = 0; Name[i]; i++) { if (Name[i] == '\n') { Name[i] = '\0'; break; } }
Probier diese:
quelle
len = strlen(str)
kann überlaufen:strlen
kehrt zurücksize_t
, nichtint
. Was ist mit den seltsamenif (len>0) if (...)
Bedingungen? Weißt du nichts davon&&
? Wenn Sie mehrere nachfolgende Instanzen von CR / LF entfernen möchten, warum sollten Sie sich auf 5 beschränken? Warum nicht alle entfernen? Warum hat die Funktion einenint
Rückgabetyp, wenn sie immer zurückgibt0
? Warum nicht einfach zurückkehrenvoid
?