Was ist der einfachste und effizienteste Weg, um Leerzeichen aus einer Zeichenfolge in C zu entfernen?
Entfernen Sie Leerzeichen aus einer Zeichenfolge in C
Tyler behandeln
Aaron
Das Einfachste und das Effizienteste passen normalerweise nicht zusammen…
Hier ist eine mögliche Lösung für die In-Place-Entfernung:
void remove_spaces(char* s) {
char* d = s;
do {
while (*d == ' ') {
++d;
}
} while (*s++ = *d++);
}
-
Was passiert, wenn die Eingabequelle aus einem Zeichenfolgenliteral initialisiert wurde?
– Feuer unterdrücken
13. November 2009 um 1:11 Uhr
-
@Suppressingfire: Angenommen, du meinst
RemoveSpaces("blah");
und nichtchar a[] = "blah"; RemoveSpaces(a);
, dann undefiniertes Verhalten. Aber das ist nicht die Schuld dieses Codes. Es wird nicht empfohlen, eine schreibgeschützte Zeichenfolge an eine Funktion zu übergeben, die dokumentiert ist, um die an sie übergebene Zeichenfolge zu ändern (z. B. durch Entfernen von Leerzeichen) 😉– Steve Jessop
13. November 2009 um 1:25 Uhr
-
Ich denke, Sie sollten *i = ‘\0’ tun; schlussendlich.
– Nick Louloudakis
12. November 2013 um 14:14 Uhr
-
* ich = 0 und *i = ‘\0’ ist dasselbe 🙂
– Uxi
31. März 2015 um 14:31 Uhr
-
Wie… Wie funktioniert das? Ich bin neu in C und Zeigern, daher wäre eine exemplarische Vorgehensweise zu dem, was passiert, sehr willkommen
– starscream_disco_party
14. Februar 2016 um 20:57 Uhr
Kornel
Hier ist eine sehr kompakte, aber völlig korrekte Version:
do while(isspace(*s)) s++; while(*d++ = *s++);
Und hier, nur zu meiner Unterhaltung, sind Code-Golf-Versionen, die nicht ganz korrekt sind und die Kommentatoren verärgern.
Wenn Sie ein undefiniertes Verhalten riskieren können und niemals leere Zeichenfolgen haben, können Sie den Körper loswerden:
while(*(d+=!isspace(*s++)) = *s);
Verdammt, wenn Sie mit Leerzeichen nur ein Leerzeichen meinen:
while(*(d+=*s++!=' ')=*s);
Verwenden Sie das nicht in der Produktion 🙂
-
Interessant, die ersten beiden funktionieren auf meiner Maschine. Aber ich denke, all diese sind undefiniert, da die Verwendung von s++ und *s in einer Anweisung zu undefiniertem Verhalten führt?
– Andomar
13. November 2009 um 0:54 Uhr
-
Stellen Sie sicher, dass Sie beim Dereferenzieren nicht über das Ende der Zeichenfolge hinausgehen.
– Casey
13. November 2009 um 0:59 Uhr
-
@Andomar: Der erste ist völlig sicher und gesund. Die letzten beiden sind in der Tat lückenhaft (getestet in GCC4.2).
– Körnel
13. November 2009 um 1:50 Uhr
-
Es “sound” zu nennen, ist vielleicht etwas zu höflich. Alle 3 Versionen sind komplett unlesbar, ohne Performancegewinn. Apple stimmt zu dass Klammern unnötig sind. Ich meine, was sind viele Millionen Dollar an Verlusten und all die Programmierer der Welt, die Sie auslachen, verglichen mit der reinen Qual, die mit dem Schreiben von Klammern verbunden ist?
– Ludin
21. Mai 2015 um 11:35 Uhr
-
Warum ist es notwendig, undefiniertes Verhalten zu riskieren, wenn Sie dieses Risiko mit dem Komma-Operator und a lösen könnten
for
Schleife?– autistisch
24. Mai 2015 um 0:41 Uhr
Lundin
Wie wir den geposteten Antworten entnehmen können, ist dies überraschenderweise keine triviale Aufgabe. Wenn sie mit einer Aufgabe wie dieser konfrontiert werden, scheinen viele Programmierer ihren gesunden Menschenverstand über Bord zu werfen, um den obskursten Schnipsel zu produzieren, der ihnen überhaupt einfallen kann.
Dinge, die man beachten muss:
- Sie möchten eine Kopie der Zeichenfolge erstellen, wobei Leerzeichen entfernt werden. Das Ändern der übergebenen Zeichenfolge ist eine schlechte Vorgehensweise, es kann sich um ein Zeichenfolgenliteral handeln. Außerdem hat es manchmal Vorteile, Saiten so zu behandeln unveränderliche Objekte.
- Sie können nicht davon ausgehen, dass die Quellzeichenfolge nicht leer ist. Es darf nur ein einzelnes Null-Terminierungszeichen enthalten.
- Der Zielpuffer kann nicht initialisierten Müll enthalten, wenn die Funktion aufgerufen wird. Es macht keinen Sinn, es auf Nullterminierung zu prüfen.
- Die Quellcodedokumentation sollte angeben, dass der Zielpuffer groß genug sein muss, um die gekürzte Zeichenfolge aufzunehmen. Der einfachste Weg, dies zu tun, besteht darin, ihn so groß wie die ungetrimmte Saite zu machen.
- Der Zielpuffer muss eine nullterminierte Zeichenfolge ohne Leerzeichen enthalten, wenn die Funktion ausgeführt wird.
- Überlegen Sie, ob Sie alle Leerzeichen oder nur Leerzeichen entfernen möchten
' '
. - Die C-Programmierung ist kein Wettbewerb darüber, wer so viele Operatoren wie möglich in eine einzige Zeile quetschen kann. Es ist eher das Gegenteil, ein gutes C-Programm enthält lesbaren Code (immer die allerwichtigste Qualität), ohne die Programmeffizienz (ziemlich wichtig) zu opfern.
- Aus diesem Grund erhalten Sie keine Bonuspunkte dafür, das Einfügen der Nullterminierung des Zielstrings zu verbergen, indem Sie es Teil des Kopiercodes sein lassen. Machen Sie stattdessen das Einfügen der Nullterminierung explizit, um zu zeigen, dass Sie es nicht nur aus Versehen geschafft haben, es richtig zu machen.
Was ich tun würde:
void remove_spaces (char* restrict str_trimmed, const char* restrict str_untrimmed)
{
while (*str_untrimmed != '\0')
{
if(!isspace(*str_untrimmed))
{
*str_trimmed = *str_untrimmed;
str_trimmed++;
}
str_untrimmed++;
}
*str_trimmed = '\0';
}
In diesem Code bleibt die Quellzeichenfolge „str_untrimmed“ unberührt, was durch die Verwendung der richtigen const-Korrektheit garantiert wird. Es stürzt nicht ab, wenn die Quellzeichenfolge nichts als eine Nullterminierung enthält. Es beendet die Zielzeichenfolge immer mit null.
Die Speicherzuordnung bleibt dem Aufrufer überlassen. Der Algorithmus sollte sich nur darauf konzentrieren, seine beabsichtigte Arbeit zu erledigen. Es entfernt alle Leerzeichen.
Es gibt keine subtilen Tricks im Code. Es wird nicht versucht, so viele Operatoren wie möglich in eine einzige Zeile zu quetschen. Es wird einen sehr schlechten Kandidaten für die abgeben IOCCC. Dennoch liefert es so ziemlich den gleichen Maschinencode wie die obskureren Einzeiler-Versionen.
Wenn Sie etwas kopieren, können Sie jedoch etwas optimieren, indem Sie beide Zeiger als deklarieren restrict
, bei dem es sich um einen Vertrag zwischen dem Programmierer und dem Compiler handelt, bei dem der Programmierer garantiert, dass das Ziel und die Quelle nicht dieselbe Adresse sind. Dies ermöglicht eine effizientere Optimierung, da der Compiler dann ohne Zwischenspeicher direkt von der Quelle zum Ziel kopieren kann.
-
Warum die verwenden
restrict
Stichwort? Es gibt keinen Grund, warum Sie nicht denselben Zeiger als Quelle und Ziel übergeben können sollten, und Ihr Code unterstützt dies.– chqrlie
12. Oktober 2016 um 21:20 Uhr
-
@chqrlie Es könnte sicherlich auf Kosten von langsamerem Code im generischen Anwendungsfall entfernt werden. Ich glaube nicht, dass ich diesen Code bewertet habe, aber ich vermute, dass es keinen großen Unterschied machen sollte.
– Ludin
13. Oktober 2016 um 6:40 Uhr
-
Dies ist die vernünftigste Antwort, die ich gesehen habe. Es ist klar, prägnant und für einen Anfänger gut verständlich! Vielen Dank.
– PageMaker
3. Februar 2021 um 18:24 Uhr
-
Ich würde ersetzen
str_untrimmed
durchscattered
undstr_trimmed
durchcondensed
.– Wolf
12. August 2021 um 12:47 Uhr
-
@Wolf Gut für dich. Hören Sie jetzt bitte auf, die Beiträge anderer mit geringfügigen überflüssigen Änderungen zu zerstören oder den Codierungsstil an Ihre persönlichen Vorlieben anzupassen. Sie haben anscheinend einen zu hohen Ruf, um Änderungen überprüfen zu lassen, oder Sie würden eine Änderungssperre erhalten.
– Ludin
12. August 2021 um 14:38 Uhr
Andomar
In C können Sie einige Strings direkt ersetzen, zum Beispiel einen String, der von strdup() zurückgegeben wird:
char *str = strdup(" a b c ");
char *write = str, *read = str;
do {
if (*read != ' ')
*write++ = *read;
} while (*read++);
printf("%s\n", str);
Andere Zeichenfolgen sind schreibgeschützt, z. B. die im Code deklarierten. Sie müssten diese in einen neu zugewiesenen Speicherbereich kopieren und die Kopie füllen, indem Sie die Leerzeichen überspringen:
char *oldstr = " a b c ";
char *newstr = malloc(strlen(oldstr)+1);
char *np = newstr, *op = oldstr;
do {
if (*op != ' ')
*np++ = *op;
} while (*op++);
printf("%s\n", newstr);
Sie können sehen, warum Menschen andere Sprachen erfunden haben;)
Quark
#include <ctype>
char * remove_spaces(char * source, char * target)
{
while(*source++ && *target)
{
if (!isspace(*source))
*target++ = *source;
}
return target;
}
Anmerkungen;
- Dies behandelt Unicode nicht.
-
Wird dadurch nicht das erste Zeichen übersprungen?
– Aaron
13. November 2009 um 0:17 Uhr
-
Sie sollten den übergebenen Wert umwandeln
isspace
zuunsigned char
da diese Funktion so definiert ist, dass sie einen Wert im Bereich von akzeptiertunsigned char
oder EOF.– Café
13. November 2009 um 0:22 Uhr
-
Es entfernt immer noch das erste Zeichen und schlägt fehl, wenn es mit aufgerufen wird
target
contating ‘\0’ in seinem ersten Element (ich verstehe nicht, was der Zweck ist, seinen Inhalt zu überprüfen). Wechselnwhile(*source++ && *target) {...}
zudo {...} while(*source++);
scheint gut zu funktionieren.– mMontu
24. Mai 2012 um 14:53 Uhr
-
Meinten Sie
ctype.h
?– Spikatrix
20. Mai 2015 um 14:29 Uhr
-
1) Ein anfängliches Leerzeichen kann nicht entfernt werden
source
. 2) Fügt niemals ein abschließendes Nullzeichen an antarget
wennsource == ""
. 3) Abhängig vom Wert intarget[0]
.– chux – Wiedereinsetzung von Monica
20. Mai 2015 um 20:51 Uhr
Alfredo
Wenn Sie immer noch interessiert sind, entfernt diese Funktion Leerzeichen am Anfang der Zeichenfolge, und ich hatte es gerade in meinem Code zum Laufen gebracht:
void removeSpaces(char *str1)
{
char *str2;
str2=str1;
while (*str2==' ') str2++;
if (str2!=str1) memmove(str1,str2,strlen(str2)+1);
}
-
Wird dadurch nicht das erste Zeichen übersprungen?
– Aaron
13. November 2009 um 0:17 Uhr
-
Sie sollten den übergebenen Wert umwandeln
isspace
zuunsigned char
da diese Funktion so definiert ist, dass sie einen Wert im Bereich von akzeptiertunsigned char
oder EOF.– Café
13. November 2009 um 0:22 Uhr
-
Es entfernt immer noch das erste Zeichen und schlägt fehl, wenn es mit aufgerufen wird
target
contating ‘\0’ in seinem ersten Element (ich verstehe nicht, was der Zweck ist, seinen Inhalt zu überprüfen). Wechselnwhile(*source++ && *target) {...}
zudo {...} while(*source++);
scheint gut zu funktionieren.– mMontu
24. Mai 2012 um 14:53 Uhr
-
Meinten Sie
ctype.h
?– Spikatrix
20. Mai 2015 um 14:29 Uhr
-
1) Ein anfängliches Leerzeichen kann nicht entfernt werden
source
. 2) Fügt niemals ein abschließendes Nullzeichen an antarget
wennsource == ""
. 3) Abhängig vom Wert intarget[0]
.– chux – Wiedereinsetzung von Monica
20. Mai 2015 um 20:51 Uhr
Praveen
#include<stdio.h>
#include<string.h>
main()
{
int i=0,n;
int j=0;
char str[]=" Nar ayan singh ";
char *ptr,*ptr1;
printf("sizeof str:%ld\n",strlen(str));
while(str[i]==' ')
{
memcpy (str,str+1,strlen(str)+1);
}
printf("sizeof str:%ld\n",strlen(str));
n=strlen(str);
while(str[n]==' ' || str[n]=='\0')
n--;
str[n+1]='\0';
printf("str:%s ",str);
printf("sizeof str:%ld\n",strlen(str));
}
-
strlen
kehrt zurücksize_t
. Also verwenden%zu
nicht%ld
. Und verwendenint main()
zusammen mitreturn 0;
– Spikatrix
20. Mai 2015 um 14:31 Uhr
-
Zusätzlich,
memcpy
ist zum Kopieren überlappender Speicherbereiche ungeeignet. Verwendenmemmove
stattdessen.– autistisch
21. Mai 2015 um 9:52 Uhr
Das Einfachste und das Effizienteste sind nicht unbedingt dasselbe
– Allan H
13. November 2009 um 0:09 Uhr
@JimFell Der Titel dieser Frage ist (war) sehr irreführend: Es geht nur darum, am Anfang Leerzeichen zu entfernen
– Wolf
12. August 2021 um 12:15 Uhr