Wenn Sie die Zeichenfolge ändern können:
// Note: This function returns a pointer to a substring of the original string.
// If the given string was allocated dynamically, the caller must not overwrite
// that pointer with the returned value, since the original pointer must be
// deallocated using the same allocator with which it was allocated. The return
// value must NOT be deallocated using free() etc.
char *trimwhitespace(char *str)
{
char *end;
// Trim leading space
while(isspace((unsigned char)*str)) str++;
if(*str == 0) // All spaces?
return str;
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) end--;
// Write new null terminator character
end[1] = '\0';
return str;
}
Wenn Sie die Zeichenfolge nicht ändern können, können Sie grundsätzlich dieselbe Methode verwenden:
// Stores the trimmed input string into the given output buffer, which must be
// large enough to store the result. If it is too small, the output is
// truncated.
size_t trimwhitespace(char *out, size_t len, const char *str)
{
if(len == 0)
return 0;
const char *end;
size_t out_size;
// Trim leading space
while(isspace((unsigned char)*str)) str++;
if(*str == 0) // All spaces?
{
*out = 0;
return 1;
}
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) end--;
end++;
// Set output size to minimum of trimmed string length and buffer size minus 1
out_size = (end - str) < len-1 ? (end - str) : len-1;
// Copy trimmed string and add null terminator
memcpy(out, str, out_size);
out[out_size] = 0;
return out_size;
}
str
ist eine lokale Variable, und durch Ändern wird der ursprüngliche Zeiger, der übergeben wird, nicht geändert. Funktionsaufrufe in C werden immer als Wert übergeben, niemals als Referenz.free()
Funktion ist. Im Gegenteil - ich habe dies so konzipiert, dass aus Effizienzgründen keine Speicherzuweisung erforderlich ist. Wenn die übergebene Adresse dynamisch zugewiesen wurde, ist der Anrufer weiterhin für die Freigabe dieses Speichers verantwortlich, und der Anrufer muss sicherstellen, dass dieser Wert nicht mit dem hier zurückgegebenen Wert überschrieben wird.isspace
tounsigned char
umwandeln, sonst rufen Sie undefiniertes Verhalten auf.Hier ist eine, die die Zeichenfolge an die erste Position Ihres Puffers verschiebt. Möglicherweise möchten Sie dieses Verhalten, damit Sie die Zeichenfolge, wenn Sie sie dynamisch zugewiesen haben, weiterhin auf demselben Zeiger freigeben können, den trim () zurückgibt:
Auf Richtigkeit prüfen:
Die Quelldatei war trim.c. Kompiliert mit 'cc -Wall trim.c -o trim'.
quelle
isspace
tounsigned char
umwandeln, sonst rufen Sie undefiniertes Verhalten auf.isspace()
also auf, warum sollte es einen Unterschied zwischen" "
und geben"\n"
? Ich habe Unit-Tests für Zeilenumbrüche hinzugefügt und es sieht für mich in Ordnung aus*(endp + 1) = '\0';
. Der Beispieltest für die Antwort verwendet einen Puffer von 64, wodurch dieses Problem vermieden wird.Meine Lösung. String muss änderbar sein. Der Vorteil gegenüber einigen anderen Lösungen besteht darin, dass der Nicht-Leerzeichen-Teil an den Anfang verschoben wird, sodass Sie den alten Zeiger weiterhin verwenden können, falls Sie ihn später freigeben müssen ().
Diese Version erstellt eine Kopie der Zeichenfolge mit strndup (), anstatt sie an Ort und Stelle zu bearbeiten. strndup () erfordert _GNU_SOURCE, daher müssen Sie möglicherweise Ihr eigenes strndup () mit malloc () und strncpy () erstellen.
quelle
trim()
Invokes UB wenns
ist""
als der ersteisspace()
Anruf würdeisspace(p[-1])
undp[-1]
verweist nicht unbedingt eine rechtliche Lage.isspace
tounsigned char
umwandeln, sonst rufen Sie undefiniertes Verhalten auf.if(l==0)return;
, umHier ist meine C-Minibibliothek zum Trimmen von links, rechts, beiden, an Ort und Stelle und getrennt sowie zum Trimmen einer Reihe angegebener Zeichen (oder standardmäßig Leerzeichen).
Inhalt von strlib.h:
Inhalt von strlib.c:
Die eine Hauptroutine macht alles. Es wird an Ort und Stelle abgeschnitten , wenn src == dst , andernfalls funktioniert es wie die
strcpy
Routinen. Es schneidet eine Reihe von Zeichen ab, die in der Zeichenfolgenbegrenzung angegeben sindoder Leerzeichen, wenn null. Es schneidet links, rechts, beide und alle (wie tr). Es steckt nicht viel dahinter und es wird nur einmal über die Zeichenfolge iteriert. Einige Leute könnten sich beschweren, dass das Trimmen rechts links beginnt, es ist jedoch keine Strlen erforderlich, die sowieso links beginnt. (Auf die eine oder andere Weise muss man das Ende der Zeichenfolge erreichen, um die richtigen Zuschnitte zu erzielen, damit Sie die Arbeit genauso gut erledigen können, wie Sie möchten.) Es kann Argumente für Pipelining- und Cache-Größen und dergleichen geben - wer weiß . Da die Lösung von links nach rechts funktioniert und nur einmal iteriert, kann sie auch für Streams erweitert werden. Einschränkungen: Es funktioniert nicht mit Unicode- Zeichenfolgen.quelle
dtab[*d]
wird nicht umgewandelt*d
,unsigned int
bevor es als Array-Index verwendet wird. Auf einem System mit signiertem Zeichen wird dies gelesen,dtab[-127]
was zu Fehlern und möglicherweise zum Absturz führen wird.dtab[*delim++]
dachar
Indexwerte umgewandelt werden müssenunsigned char
. Der Code geht von 8 Bit auschar
.delim
sollte als deklariert werdenconst char *
.dtab[0xFF & (unsigned int)*d]
würde klarer alsdtab[(unsigned char)*d]
. Der Code funktioniert mit UTF-8-codierten Zeichenfolgen, entfernt jedoch keine Nicht-ASCII-Abstandssequenzen.Hier ist mein Versuch einer einfachen, aber korrekten Trimmfunktion an Ort und Stelle.
quelle
while ((end >= begin) && isspace(str[end]))
um UB zu verhindern, wennstr is
"". Prevents
str [-1] `.isspace
tounsigned char
umwandeln, sonst rufen Sie undefiniertes Verhalten auf.<ctype.h>
sollen mit Ints arbeiten, die entwederunsigned char
oder den speziellen Wert darstellenEOF
. Siehe stackoverflow.com/q/7131026/225757 .Spät zur Trimmparty
Funktionen:
1. Schneiden Sie den Anfang schnell ab, wie bei einer Reihe anderer Antworten.
2. Trimmen Sie nach dem Ende mit nur 1 Test pro Schleife nach rechts. Wie @ jfm3, funktioniert jedoch für eine reine Leerzeichenfolge.
3. Um undefiniertes Verhalten zu vermeiden, wenn
char
es sich um eine Signatur handeltchar
, setzen Sie*s
aufunsigned char
.@chqrlie kommentierte, dass das oben Gesagte die zugeschnittene Zeichenfolge nicht verschiebt. Um das zu tun ....
quelle
Hier ist eine Lösung ähnlich der direkten Änderungsroutine von @ adam-rosenfields, ohne jedoch unnötig auf strlen () zurückzugreifen. Wie bei @jkramer wird die Zeichenfolge im Puffer nach links angepasst, sodass Sie denselben Zeiger freigeben können. Nicht optimal für große Zeichenfolgen, da memmove nicht verwendet wird. Enthält die ++ / - Operatoren, die @ jfm3 erwähnt. FCTX- basierte Unit-Tests enthalten.
quelle
Eine andere, bei der eine Zeile den eigentlichen Job macht:
quelle
%n
Konvertierungsspezifizierer, und am Ende ist es leider einfacher, dies von Hand zu tun.Die meisten dieser Antworten haben mir nicht gefallen, weil sie eine oder mehrere der folgenden Antworten gegeben haben ...
Hier ist meine Version:
quelle
isspace
tounsigned char
umwandeln, sonst rufen Sie undefiniertes Verhalten auf.while (isspace((unsigned char) *szWrite)) szWrite++;
würde das verhindern. Der Code kopiert auch den gesamten nachgestellten Leerraum.*szWrite = *szRead
wenn die Zeiger nicht gleich sind, die Schreibvorgänge in diesem Fall überspringen, aber dann haben wir einen weiteren Vergleich / Zweig hinzugefügt. Mit moderner CPU / MMU / BP habe ich keine Ahnung, ob diese Prüfung ein Verlust oder ein Gewinn wäre. Mit einfacheren Prozessoren und Speicherarchitekturen ist es billiger, nur die Kopie zu erstellen und den Vergleich zu überspringen.Sehr spät zur Party ...
Single-Pass-Forward-Scan-Lösung ohne Backtracking. Jedes Zeichen in der Quellzeichenfolge wird genau
einmalzweimal getestet . (Es sollte also schneller sein als die meisten anderen Lösungen hier, insbesondere wenn die Quellzeichenfolge viele nachgestellte Leerzeichen enthält.)Dies umfasst zwei Lösungen, eine zum Kopieren und Trimmen einer Quellzeichenfolge in eine andere Zielzeichenfolge und die andere zum Trimmen der Quellzeichenfolge an Ort und Stelle. Beide Funktionen verwenden denselben Code.
Die (veränderbare) Zeichenfolge wird an Ort und Stelle verschoben, sodass der ursprüngliche Zeiger darauf unverändert bleibt.
quelle
'\0'
und dann mitisspace()
. Es scheint verschwenderisch, alle Charaktere mit zu testenisspace()
. Das Zurückverfolgen vom Ende der Zeichenfolge sollte für nicht pathologische Fälle effizienter sein.trim()
OK. Eckfall:trim2(char *d, const char *s)
hat Probleme beid,s
Überlappung unds < d
.trim()
sich in diesem Eckfall verhalten? Sie möchten eine Zeichenfolge zuschneiden und in den Speicher kopieren, der von der Zeichenfolge selbst belegt wird. Im Gegensatz dazumemmove()
muss hierfür die Länge der Quellzeichenfolge bestimmt werden, bevor der Schnitt selbst ausgeführt wird. Dazu muss die gesamte Zeichenfolge ein zusätzliches Mal gescannt werden. Es ist besser, eine anderertrim2()
Funktion zu schreiben , die die Quelle rückwärts in das Ziel kopiert und wahrscheinlich ein zusätzliches Argument für die Länge der Quellzeichenfolge verwendet.Ich bin mir nicht sicher, was Sie für "schmerzlos" halten.
C-Saiten sind ziemlich schmerzhaft. Wir können die erste Nicht-Leerzeichen-Zeichenposition trivial finden:
Wir können die letzte Nicht-Leerzeichen-Charakterposition mit zwei ähnlichen trivialen Zügen finden:
(Ich habe Ihnen den Schmerz erspart, die Operatoren
*
und++
gleichzeitig zu verwenden.)Die Frage ist nun, was machst du damit? Der vorliegende Datentyp ist nicht wirklich eine große, robuste Zusammenfassung
String
, über die man leicht nachdenken kann, sondern kaum mehr als ein Array von Speicherbytes. Ohne einen robusten Datentyp ist es unmöglich, eine Funktion zu schreiben, die die gleichechomp
Funktion wie PHperytonby hat . Was würde eine solche Funktion in C zurückgeben?quelle
do { q--; } ...
zu wissen*q != 0
.Verwenden Sie eine Zeichenfolgenbibliothek , zum Beispiel:
... wie Sie sagen, dies ist ein "häufiges" Problem, ja, Sie müssen ein #include oder so einfügen und es ist nicht in libc enthalten, aber erfinden Sie nicht Ihren eigenen Hack-Job, indem Sie zufällige Zeiger speichern und size_t's nur dazu führen Puffer läuft über.
quelle
Wenn Sie verwenden
glib
, können Sie g_strstrip verwendenquelle
Um dieses Wachstum aufrechtzuerhalten, gibt es noch eine Option mit einer modifizierbaren Zeichenfolge:
quelle
strlen()
Gibt a zurücksize_t
, das den Bereich von überschreiten kannint
. Leerzeichen sind nicht auf das Leerzeichen beschränkt. Schließlich, aber am wichtigsten: Undefiniertes Verhalten,strcpy(string, string + i * sizeof(char));
da sich Quell- und Zielarrays überlappen. Verwenden Siememmove()
anstelle vonstrcpy()
.while (isspace((int)string[i])) string[i--] = '\0';
die Schleife möglicherweise über den Anfang der Zeichenfolge hinausgeht. Sie sollten diese Schleife mit den vorherigen und folgenden Zeilen kombinieren und schreibenwhile (i > 0 && isspace((unsigned char)string[--i])) { string[i] = '\0'; } size_t end = i;
end
nicht auf das nachfolgende Null-Byte zeigte und Sieend = ++i;
immer noch ein Problem mit Zeichenfolgen hatten, die alle Leerzeichen enthielten. Ich habe gerade den Code repariert.Ich weiß, dass es viele Antworten gibt, aber ich poste meine Antwort hier, um zu sehen, ob meine Lösung gut genug ist.
quelle
isspace(*str)
UB wann*str < 0
.size_t n
ist gut, aber die Schnittstelle informiert den Anrufer in keiner Weise, wennn
er zu klein für eine vollständig zugeschnittene Zeichenfolge ist. Betrachten Sietrim(out, 12, "delete data not")
Der einfachste Weg, führende Leerzeichen in einer Zeichenfolge zu überspringen, ist, imho,
quelle
" foo bar "
.Ok, das ist meine Sicht auf die Frage. Ich glaube, es ist die prägnanteste Lösung, die den vorhandenen String modifiziert (
free
funktioniert) und UB vermeidet. Für kleine Zeichenfolgen ist es wahrscheinlich schneller als eine Lösung mit memmove.quelle
b > str
Test wird nur einmal benötigt.*b = 0;
nur einmal benötigt.isspace
hilft, alle Leerzeichen zu kürzen.strndup
erstellen Sie schließlich einen neuen Zeichenfolgenpuffer, indem Sie Leerzeichen ausschließen.quelle
strndup()
ist nicht Teil des C-Standards, sondern nur Posix. Da es jedoch recht einfach zu implementieren ist, ist es keine große Sache.trim_space("")
kehrt zurückNULL
. Ich würde einen Zeiger auf erwarten""
.int len;
sollte seinsize_t len;
.isspace(in[len - 1])
UB wannin[len - 1] < 0
.while (isspace((unsigned char) *in) in++;
vorherlen = strlen(in);
wäre effizienter als das späterewhile(len && *in && isspace(*in)) ++in, --len;
Persönlich würde ich meine eigenen rollen. Sie können strtok verwenden, müssen jedoch darauf achten (insbesondere, wenn Sie führende Zeichen entfernen), dass Sie wissen, welcher Speicher was ist.
Das Entfernen von nachgestellten Leerzeichen ist einfach und ziemlich sicher, da Sie einfach eine 0 über das letzte Leerzeichen setzen und vom Ende zurückzählen können. Führende Räume loszuwerden bedeutet, Dinge zu bewegen. Wenn Sie es an Ort und Stelle tun möchten (wahrscheinlich sinnvoll), können Sie einfach alles um einen Charakter zurückschieben, bis kein führendes Leerzeichen mehr vorhanden ist. Um effizienter zu sein, können Sie auch den Index des ersten Nicht-Leerzeichens finden und alles um diese Zahl zurückschieben. Oder Sie können einfach einen Zeiger auf das erste Nicht-Leerzeichen verwenden (aber dann müssen Sie genauso vorsichtig sein wie bei strtok).
quelle
quelle
Ein bisschen spät zum Spiel, aber ich werde meine Routinen in den Kampf werfen. Sie sind wahrscheinlich nicht die absolut effizientesten, aber ich glaube, sie sind korrekt und einfach (mit
rtrim()
Druck auf die Komplexität):quelle
char
Argument inisspace()
to(unsigned char)
umwandeln, um undefiniertes Verhalten bei potenziell negativen Werten zu vermeiden. Vermeiden Sie es auch, die Zeichenfolge zu verschieben,ltrim()
wenn dies nicht erforderlich ist.Die meisten der bisherigen Antworten lauten wie folgt:
strlen()
zuerst an und machen Sie einen zweiten Durchgang durch die gesamte Zeichenfolge.Diese Version macht nur einen Durchgang und geht nicht zurück. Daher kann es eine bessere Leistung als die anderen erzielen, allerdings nur dann, wenn es üblich ist, Hunderte von nachgestellten Leerzeichen zu haben (was bei der Ausgabe einer SQL-Abfrage nicht ungewöhnlich ist).
quelle
strspn()
undstrcspn()
in einer engen Schleife. Dies ist sehr ineffizient und der Overhead wird den unbewiesenen Vorteil des einzelnen Vorwärtsdurchlaufs in den Schatten stellen.strlen()
wird normalerweise inline mit sehr effizientem Code erweitert, was kein wirkliches Problem darstellt. Das Trimmen des Anfangs und des Endes der Zeichenfolge ist viel schneller als das Testen jedes Zeichens in der Zeichenfolge auf Weißheit, selbst im speziellen Fall von Zeichenfolgen mit sehr wenigen oder keinen nicht weißen Zeichen.Dies ist die kürzest mögliche Implementierung, die ich mir vorstellen kann:
quelle
char *trim(char *s) { char *p = s, *e = s + strlen(s); while (e > s && isspace((unsigned char)e[-1])) { *--e = '\0'; } while (isspace((unsigned char)*p)) { p++; } if (p > s) { memmove(s, p, e + 1 - p); } return s; }
Diese Funktionen ändern den ursprünglichen Puffer. Wenn er dynamisch zugewiesen wird, kann der ursprüngliche Zeiger freigegeben werden.
quelle
rstrip()
Ruft undefiniertes Verhalten für die leere Zeichenfolge auf.lstrip()
ist bei Zeichenfolgen mit einem langen Anfangsanteil an Leerzeichen unnötig langsam.isspace()
sollte keinchar
Argument übergeben werden, da es undefiniertes Verhalten bei negativen Werten aufruft, die sich von unterscheidenEOF
.Was halten Sie von der Verwendung der in der Kopfzeile Shlwapi.h definierten StrTrim-Funktion? Es ist einfach und definiert sich selbst.
Details finden Sie unter:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx
Wenn Sie haben,
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
wird dies
ausCaptain
als"GeorgeBailey"
nicht geben"GeorgeBailey "
.quelle
Um meine Saiten von beiden Seiten zu trimmen, benutze ich den Oldie, aber den Gooody.
quelle
size_t
anstelle von verwendenunsigned int
. Der Code hat viele redundante Tests und ruft undefiniertes Verhalten auf,strncpy(strData,&strData[S],L)
da sich die Quell- und Ziel-Arrays überlappen. Verwenden Siememmove()
anstelle vonstrncpy()
.Ich füge nur Code hinzu, weil der bisher veröffentlichte Code nicht optimal erscheint (und ich noch keinen Repräsentanten habe, der einen Kommentar abgeben kann.)
strndup()
ist eine GNU-Erweiterung. Wenn Sie es nicht haben oder etwas Äquivalentes, rollen Sie Ihr eigenes. Beispielsweise:quelle
isspace(0)
Wird als falsch definiert, können Sie beide Funktionen vereinfachen. Bewegen Sie auch dasmemmove()
Innere desif
Blocks.Hier verwende ich die dynamische Speicherzuordnung, um die Eingabezeichenfolge auf die Funktion trimStr zu trimmen. Zunächst ermitteln wir, wie viele nicht leere Zeichen in der Eingabezeichenfolge vorhanden sind. Dann weisen wir ein Zeichenarray mit dieser Größe zu und kümmern uns um das nullterminierte Zeichen. Wenn wir diese Funktion verwenden, müssen wir den Speicher innerhalb der Hauptfunktion freigeben.
quelle
Hier ist, wie ich es mache. Die Zeichenfolge wird an Ort und Stelle gekürzt, sodass Sie sich keine Sorgen mehr machen müssen, wenn Sie die Rückgabe einer zurückgegebenen Zeichenfolge aufheben oder den Zeiger auf eine zugewiesene Zeichenfolge verlieren. Es ist vielleicht nicht die kürzest mögliche Antwort, aber es sollte den meisten Lesern klar sein.
quelle
quelle