Respuestas:
Si puede modificar la cadena:
// 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;
}
Si no puede modificar la cadena, puede usar básicamente el mismo método:
// 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
es una variable local, y cambiarla no cambia el puntero original que se pasa. Las llamadas a funciones en C siempre son pasadas por valor, nunca pasadas por referencia.
free()
función. Todo lo contrario: diseñé esto para evitar la necesidad de asignación de memoria para la eficiencia. Si la dirección ingresada se asignó dinámicamente, la persona que llama sigue siendo responsable de liberar esa memoria, y la persona que llama debe asegurarse de no sobrescribir ese valor con el valor devuelto aquí.
isspace
to unsigned char
, de lo contrario invocas un comportamiento indefinido.
Aquí hay uno que desplaza la cadena a la primera posición de su búfer. Es posible que desee este comportamiento para que si asigna dinámicamente la cadena, todavía puede liberarla en el mismo puntero que trim () devuelve:
char *trim(char *str)
{
size_t len = 0;
char *frontp = str;
char *endp = NULL;
if( str == NULL ) { return NULL; }
if( str[0] == '\0' ) { return str; }
len = strlen(str);
endp = str + len;
/* Move the front and back pointers to address the first non-whitespace
* characters from each end.
*/
while( isspace((unsigned char) *frontp) ) { ++frontp; }
if( endp != frontp )
{
while( isspace((unsigned char) *(--endp)) && endp != frontp ) {}
}
if( frontp != str && endp == frontp )
*str = '\0';
else if( str + len - 1 != endp )
*(endp + 1) = '\0';
/* Shift the string so that it starts at str so that if it's dynamically
* allocated, we can still free it on the returned pointer. Note the reuse
* of endp to mean the front of the string buffer now.
*/
endp = str;
if( frontp != str )
{
while( *frontp ) { *endp++ = *frontp++; }
*endp = '\0';
}
return str;
}
Prueba de corrección:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/* Paste function from above here. */
int main()
{
/* The test prints the following:
[nothing to trim] -> [nothing to trim]
[ trim the front] -> [trim the front]
[trim the back ] -> [trim the back]
[ trim front and back ] -> [trim front and back]
[ trim one char front and back ] -> [trim one char front and back]
[ trim one char front] -> [trim one char front]
[trim one char back ] -> [trim one char back]
[ ] -> []
[ ] -> []
[a] -> [a]
[] -> []
*/
char *sample_strings[] =
{
"nothing to trim",
" trim the front",
"trim the back ",
" trim front and back ",
" trim one char front and back ",
" trim one char front",
"trim one char back ",
" ",
" ",
"a",
"",
NULL
};
char test_buffer[64];
char comparison_buffer[64];
size_t index, compare_pos;
for( index = 0; sample_strings[index] != NULL; ++index )
{
// Fill buffer with known value to verify we do not write past the end of the string.
memset( test_buffer, 0xCC, sizeof(test_buffer) );
strcpy( test_buffer, sample_strings[index] );
memcpy( comparison_buffer, test_buffer, sizeof(comparison_buffer));
printf("[%s] -> [%s]\n", sample_strings[index],
trim(test_buffer));
for( compare_pos = strlen(comparison_buffer);
compare_pos < sizeof(comparison_buffer);
++compare_pos )
{
if( test_buffer[compare_pos] != comparison_buffer[compare_pos] )
{
printf("Unexpected change to buffer @ index %u: %02x (expected %02x)\n",
compare_pos, (unsigned char) test_buffer[compare_pos], (unsigned char) comparison_buffer[compare_pos]);
}
}
}
return 0;
}
El archivo fuente fue trim.c. Compilado con 'cc -Wall trim.c -o trim'.
isspace
to unsigned char
, de lo contrario invocas un comportamiento indefinido.
isspace()
¿por qué habría una diferencia entre " "
y "\n"
? Agregué pruebas unitarias para nuevas líneas y me parece bien ... ideone.com/bbVmqo
*(endp + 1) = '\0';
. La prueba de ejemplo de la respuesta utiliza un búfer de 64 que evita este problema.
Mi solución. La cadena debe ser cambiable. La ventaja sobre algunas de las otras soluciones es que mueve la parte no espacial al principio para que pueda seguir usando el puntero anterior, en caso de que tenga que liberarlo () más tarde.
void trim(char * s) {
char * p = s;
int l = strlen(p);
while(isspace(p[l - 1])) p[--l] = 0;
while(* p && isspace(* p)) ++p, --l;
memmove(s, p, l + 1);
}
Esta versión crea una copia de la cadena con strndup () en lugar de editarla en su lugar. strndup () requiere _GNU_SOURCE, por lo que tal vez necesite hacer su propio strndup () con malloc () y strncpy ().
char * trim(char * s) {
int l = strlen(s);
while(isspace(s[l - 1])) --l;
while(* s && isspace(* s)) ++s, --l;
return strndup(s, l);
}
trim()
invoca UB si s
es ""
como isspace()
sería la primera llamada isspace(p[-1])
y p[-1]
no necesariamente hace referencia a una ubicación legal.
isspace
to unsigned char
, de lo contrario invocas un comportamiento indefinido.
if(l==0)return;
para evitar str de longitud cero
Aquí está mi mini biblioteca C para recortar a la izquierda, derecha, ambas, todas, en su lugar y por separado, y recortar un conjunto de caracteres especificados (o espacios en blanco de forma predeterminada).
#ifndef STRLIB_H_
#define STRLIB_H_ 1
enum strtrim_mode_t {
STRLIB_MODE_ALL = 0,
STRLIB_MODE_RIGHT = 0x01,
STRLIB_MODE_LEFT = 0x02,
STRLIB_MODE_BOTH = 0x03
};
char *strcpytrim(char *d, // destination
char *s, // source
int mode,
char *delim
);
char *strtriml(char *d, char *s);
char *strtrimr(char *d, char *s);
char *strtrim(char *d, char *s);
char *strkill(char *d, char *s);
char *triml(char *s);
char *trimr(char *s);
char *trim(char *s);
char *kill(char *s);
#endif
#include <strlib.h>
char *strcpytrim(char *d, // destination
char *s, // source
int mode,
char *delim
) {
char *o = d; // save orig
char *e = 0; // end space ptr.
char dtab[256] = {0};
if (!s || !d) return 0;
if (!delim) delim = " \t\n\f";
while (*delim)
dtab[*delim++] = 1;
while ( (*d = *s++) != 0 ) {
if (!dtab[0xFF & (unsigned int)*d]) { // Not a match char
e = 0; // Reset end pointer
} else {
if (!e) e = d; // Found first match.
if ( mode == STRLIB_MODE_ALL || ((mode != STRLIB_MODE_RIGHT) && (d == o)) )
continue;
}
d++;
}
if (mode != STRLIB_MODE_LEFT && e) { // for everything but trim_left, delete trailing matches.
*e = 0;
}
return o;
}
// perhaps these could be inlined in strlib.h
char *strtriml(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_LEFT, 0); }
char *strtrimr(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_RIGHT, 0); }
char *strtrim(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_BOTH, 0); }
char *strkill(char *d, char *s) { return strcpytrim(d, s, STRLIB_MODE_ALL, 0); }
char *triml(char *s) { return strcpytrim(s, s, STRLIB_MODE_LEFT, 0); }
char *trimr(char *s) { return strcpytrim(s, s, STRLIB_MODE_RIGHT, 0); }
char *trim(char *s) { return strcpytrim(s, s, STRLIB_MODE_BOTH, 0); }
char *kill(char *s) { return strcpytrim(s, s, STRLIB_MODE_ALL, 0); }
La única rutina principal lo hace todo. Se recorta en su lugar si src == dst , de lo contrario, funciona como las strcpy
rutinas. Recorta un conjunto de caracteres especificados en el delimitación de cadena, o espacio en blanco si es nulo. Recorta izquierda, derecha, ambas y todas (como tr). No hay mucho, e itera sobre la cadena solo una vez. Algunas personas pueden quejarse de que el recorte a la derecha comienza a la izquierda, sin embargo, no se necesita ningún golpe que comience a la izquierda de todos modos. (De una forma u otra, debe llegar al final de la cadena para obtener los ajustes correctos, por lo que también podría hacer el trabajo a medida que avanza). Puede haber argumentos sobre la tubería y los tamaños de caché y demás, quién sabe . Dado que la solución funciona de izquierda a derecha e itera solo una vez, también se puede ampliar para que funcione en secuencias. Limitaciones: no funciona en cadenas unicode .
dtab[*d]
no convierte *d
a unsigned int
antes de usarlo como un índice de matriz. En un sistema con caracteres firmados, esto leerá dtab[-127]
lo que causará errores y posiblemente se bloqueará.
dtab[*delim++]
porque los char
valores de índice deben ser convertidos a unsigned char
. El código asume 8 bits char
. delim
debe declararse como const char *
. dtab[0xFF & (unsigned int)*d]
sería más claro como dtab[(unsigned char)*d]
. El código funciona en cadenas codificadas UTF-8, pero no eliminará secuencias de espaciado que no sean ASCII.
Aquí está mi intento de una función de recorte en el lugar simple pero correcta.
void trim(char *str)
{
int i;
int begin = 0;
int end = strlen(str) - 1;
while (isspace((unsigned char) str[begin]))
begin++;
while ((end >= begin) && isspace((unsigned char) str[end]))
end--;
// Shift all characters back to the start of the string array.
for (i = begin; i <= end; i++)
str[i - begin] = str[i];
str[i - begin] = '\0'; // Null terminate string.
}
while ((end >= begin) && isspace(str[end]))
para evitar UB cuando str is
"" . Prevents
str [-1] `.
isspace
to unsigned char
, de lo contrario invocas un comportamiento indefinido.
<ctype.h>
están destinadas a trabajar con ints, que representan uno unsigned char
o el valor especial EOF
. Consulte stackoverflow.com/q/7131026/225757 .
Tarde a la fiesta de recortes
Características:
1. Recorte el comienzo rápidamente, como en otras respuestas.
2. Después de llegar al final, recorte el derecho con solo 1 prueba por ciclo. Como @ jfm3, pero funciona para una cadena de espacios en blanco)
3. Para evitar un comportamiento indefinido cuando char
se firma char
, se envía *s
a unsigned char
.
Manejo de caracteres "En todos los casos el argumento es un
int
, cuyo valor será representable comounsigned char
o será igual al valor de la macroEOF
. Si el argumento tiene algún otro valor, el comportamiento es indefinido". C11 §7.4 1
#include <ctype.h>
// Return a pointer to the trimmed string
char *string_trim_inplace(char *s) {
while (isspace((unsigned char) *s)) s++;
if (*s) {
char *p = s;
while (*p) p++;
while (isspace((unsigned char) *(--p)));
p[1] = '\0';
}
// If desired, shift the trimmed string
return s;
}
@chqrlie comentó que lo anterior no desplaza la cadena recortada. Para hacerlo ...
// Return a pointer to the (shifted) trimmed string
char *string_trim_inplace(char *s) {
char *original = s;
size_t len = 0;
while (isspace((unsigned char) *s)) {
s++;
}
if (*s) {
char *p = s;
while (*p) p++;
while (isspace((unsigned char) *(--p)));
p[1] = '\0';
// len = (size_t) (p - s); // older errant code
len = (size_t) (p - s + 1); // Thanks to @theriver
}
return (s == original) ? s : memmove(original, s, len + 1);
}
Aquí hay una solución similar a la rutina de modificación en el lugar @ adam-rosenfields pero sin recurrir innecesariamente a strlen (). Al igual que @jkramer, la cadena se ajusta a la izquierda dentro del búfer para que pueda liberar el mismo puntero. No es óptimo para cadenas grandes ya que no usa memmove. Incluye los operadores ++ / - que @ jfm3 menciona. Pruebas unitarias basadas en FCTX incluidas.
#include <ctype.h>
void trim(char * const a)
{
char *p = a, *q = a;
while (isspace(*q)) ++q;
while (*q) *p++ = *q++;
*p = '\0';
while (p > a && isspace(*--p)) *p = '\0';
}
/* See http://fctx.wildbearsoftware.com/ */
#include "fct.h"
FCT_BGN()
{
FCT_QTEST_BGN(trim)
{
{ char s[] = ""; trim(s); fct_chk_eq_str("", s); } // Trivial
{ char s[] = " "; trim(s); fct_chk_eq_str("", s); } // Trivial
{ char s[] = "\t"; trim(s); fct_chk_eq_str("", s); } // Trivial
{ char s[] = "a"; trim(s); fct_chk_eq_str("a", s); } // NOP
{ char s[] = "abc"; trim(s); fct_chk_eq_str("abc", s); } // NOP
{ char s[] = " a"; trim(s); fct_chk_eq_str("a", s); } // Leading
{ char s[] = " a c"; trim(s); fct_chk_eq_str("a c", s); } // Leading
{ char s[] = "a "; trim(s); fct_chk_eq_str("a", s); } // Trailing
{ char s[] = "a c "; trim(s); fct_chk_eq_str("a c", s); } // Trailing
{ char s[] = " a "; trim(s); fct_chk_eq_str("a", s); } // Both
{ char s[] = " a c "; trim(s); fct_chk_eq_str("a c", s); } // Both
// Villemoes pointed out an edge case that corrupted memory. Thank you.
// http://stackoverflow.com/questions/122616/#comment23332594_4505533
{
char s[] = "a "; // Buffer with whitespace before s + 2
trim(s + 2); // Trim " " containing only whitespace
fct_chk_eq_str("", s + 2); // Ensure correct result from the trim
fct_chk_eq_str("a ", s); // Ensure preceding buffer not mutated
}
// doukremt suggested I investigate this test case but
// did not indicate the specific behavior that was objectionable.
// http://stackoverflow.com/posts/comments/33571430
{
char s[] = " foobar"; // Shifted across whitespace
trim(s); // Trim
fct_chk_eq_str("foobar", s); // Leading string is correct
// Here is what the algorithm produces:
char r[16] = { 'f', 'o', 'o', 'b', 'a', 'r', '\0', ' ',
' ', 'f', 'o', 'o', 'b', 'a', 'r', '\0'};
fct_chk_eq_int(0, memcmp(s, r, sizeof(s)));
}
}
FCT_QTEST_END();
}
FCT_END();
Otro, con una línea haciendo el trabajo real:
#include <stdio.h>
int main()
{
const char *target = " haha ";
char buf[256];
sscanf(target, "%s", buf); // Trimming on both sides occurs here
printf("<%s>\n", buf);
}
%n
especificador de conversión, y al final es más simple hacerlo a mano, me temo.
La mayoría de estas respuestas no me gustaron porque hicieron una o más de las siguientes ...
Aquí está mi versión:
void fnStrTrimInPlace(char *szWrite) {
const char *szWriteOrig = szWrite;
char *szLastSpace = szWrite, *szRead = szWrite;
int bNotSpace;
// SHIFT STRING, STARTING AT FIRST NON-SPACE CHAR, LEFTMOST
while( *szRead != '\0' ) {
bNotSpace = !isspace((unsigned char)(*szRead));
if( (szWrite != szWriteOrig) || bNotSpace ) {
*szWrite = *szRead;
szWrite++;
// TRACK POINTER TO LAST NON-SPACE
if( bNotSpace )
szLastSpace = szWrite;
}
szRead++;
}
// TERMINATE AFTER LAST NON-SPACE (OR BEGINNING IF THERE WAS NO NON-SPACE)
*szLastSpace = '\0';
}
isspace
to unsigned char
, de lo contrario invocas un comportamiento indefinido.
while (isspace((unsigned char) *szWrite)) szWrite++;
evitaría eso. Code también copia todo el espacio en blanco final.
*szWrite = *szRead
cuando los punteros no son iguales omitiría las escrituras en ese caso, pero luego hemos agregado otra comparación / rama. Con la CPU / MMU / BP moderna, no tengo idea si esa verificación sería una pérdida o una ganancia. Con procesadores y arquitecturas de memoria más simples, es más económico hacer la copia y saltear la comparación.
Muy tarde a la fiesta ...
Solución de escaneo directo de un solo paso sin retroceso. Todos los caracteres de la cadena de origen se prueban exactamente una vez dos veces. (Por lo tanto, debería ser más rápido que la mayoría de las otras soluciones aquí, especialmente si la cadena de origen tiene muchos espacios finales).
Esto incluye dos soluciones, una para copiar y recortar una cadena de origen en otra cadena de destino, y la otra para recortar la cadena de origen en su lugar. Ambas funciones usan el mismo código.
La cadena (modificable) se mueve en su lugar, por lo que el puntero original permanece sin cambios.
#include <stddef.h>
#include <ctype.h>
char * trim2(char *d, const char *s)
{
// Sanity checks
if (s == NULL || d == NULL)
return NULL;
// Skip leading spaces
const unsigned char * p = (const unsigned char *)s;
while (isspace(*p))
p++;
// Copy the string
unsigned char * dst = (unsigned char *)d; // d and s can be the same
unsigned char * end = dst;
while (*p != '\0')
{
if (!isspace(*dst++ = *p++))
end = dst;
}
// Truncate trailing spaces
*end = '\0';
return d;
}
char * trim(char *s)
{
return trim2(s, s);
}
'\0'
y luego con isspace()
. Parece un desperdicio probar con todos los personajes isspace()
. Retroceder desde el final de la cadena debería ser más eficiente para casos no patológicos.
trim()
OKAY. Caso de esquina: trim2(char *d, const char *s)
tiene problemas cuando se d,s
superponen y s < d
.
trim()
comportarse? Está pidiendo recortar y copiar una cadena en la memoria ocupada por la cadena misma. A diferencia memmove()
, esto requiere determinar la longitud de la cadena de origen antes de hacer el recorte en sí, lo que requiere escanear toda la cadena una vez más. Es mejor escribir una rtrim2()
función diferente que sepa copiar el origen al destino hacia atrás, y probablemente tome un argumento de longitud de cadena de origen adicional.
No estoy seguro de lo que consideras "indoloro".
Las cuerdas C son bastante dolorosas. Podemos encontrar la primera posición de carácter sin espacios en blanco trivialmente:
while (isspace (* p)) p ++;
Podemos encontrar la última posición del personaje sin espacios en blanco con dos movimientos triviales similares:
mientras que (* q) q ++; do {q--; } while (isspace (* q));
(Le he ahorrado el dolor de usar los operadores *
y ++
al mismo tiempo).
La pregunta ahora es ¿qué haces con esto? El tipo de datos en cuestión no es realmente un gran resumen robusto en el String
que es fácil pensar, sino que en realidad no es más que una matriz de bytes de almacenamiento. Al carecer de un tipo de datos robusto, es imposible escribir una función que haga lo mismo que la chomp
función de PHperytonby . ¿Qué devolvería tal función en C?
do { q--; } ...
de saber *q != 0
.
Use una biblioteca de cadenas , por ejemplo:
Ustr *s1 = USTR1(\7, " 12345 ");
ustr_sc_trim_cstr(&s1, " ");
assert(ustr_cmp_cstr_eq(s1, "12345"));
... como usted dice que este es un problema "común", sí, necesita incluir un #include más o menos y no está incluido en libc, pero no vaya a inventar su propio trabajo de pirateo almacenando punteros aleatorios y tamaños_ de esa manera solo conduce a desbordamiento de búfer.
Si está usando glib
, entonces puede usar g_strstrip
Solo para mantener este crecimiento, una opción más con una cadena modificable:
void trimString(char *string)
{
size_t i = 0, j = strlen(string);
while (j > 0 && isspace((unsigned char)string[j - 1])) string[--j] = '\0';
while (isspace((unsigned char)string[i])) i++;
if (i > 0) memmove(string, string + i, j - i + 1);
}
strlen()
devuelve un size_t
que puede exceder el rango de int
. el espacio en blanco no está restringido al carácter de espacio. Finalmente, pero lo más importante: comportamiento indefinido strcpy(string, string + i * sizeof(char));
porque las matrices de origen y destino se superponen. Usar en memmove()
lugar de strcpy()
.
while (isspace((int)string[i])) string[i--] = '\0';
puede hacer un bucle más allá del comienzo de la cadena. Debe combinar este bucle con las líneas anteriores y siguientes y escribirwhile (i > 0 && isspace((unsigned char)string[--i])) { string[i] = '\0'; } size_t end = i;
end
que no apuntaba al byte nulo final y end = ++i;
aún tenía problemas para las cadenas que contenían todos los caracteres de espacio en blanco. Acabo de arreglar el código.
Sé que hay muchas respuestas, pero publico mi respuesta aquí para ver si mi solución es lo suficientemente buena.
// Trims leading whitespace chars in left `str`, then copy at almost `n - 1` chars
// into the `out` buffer in which copying might stop when the first '\0' occurs,
// and finally append '\0' to the position of the last non-trailing whitespace char.
// Reture the length the trimed string which '\0' is not count in like strlen().
size_t trim(char *out, size_t n, const char *str)
{
// do nothing
if(n == 0) return 0;
// ptr stop at the first non-leading space char
while(isspace(*str)) str++;
if(*str == '\0') {
out[0] = '\0';
return 0;
}
size_t i = 0;
// copy char to out until '\0' or i == n - 1
for(i = 0; i < n - 1 && *str != '\0'; i++){
out[i] = *str++;
}
// deal with the trailing space
while(isspace(out[--i]));
out[++i] = '\0';
return i;
}
isspace(*str)
UB cuando *str < 0
.
size_t n
es bueno, sin embargo, la interfaz no informa a la persona que llama de ninguna manera cuando se trata de n
ser demasiado pequeño para una cadena recortada completa. Consideretrim(out, 12, "delete data not")
La manera más fácil de omitir los espacios iniciales en una cadena es, en mi opinión,
#include <stdio.h>
int main()
{
char *foo=" teststring ";
char *bar;
sscanf(foo,"%s",bar);
printf("String is >%s<\n",bar);
return 0;
}
" foo bar "
.
Ok, esta es mi opinión sobre la pregunta. Creo que es la solución más concisa que modifica la cadena en su lugar ( free
funcionará) y evita cualquier UB. Para cadenas pequeñas, probablemente sea más rápido que una solución que involucra memmove.
void stripWS_LT(char *str)
{
char *a = str, *b = str;
while (isspace((unsigned char)*a)) a++;
while (*b = *a++) b++;
while (b > str && isspace((unsigned char)*--b)) *b = 0;
}
b > str
prueba solo se necesita una vez. *b = 0;
Solo se necesita una vez.
#include <ctype.h>
#include <string.h>
char *trim_space(char *in)
{
char *out = NULL;
int len;
if (in) {
len = strlen(in);
while(len && isspace(in[len - 1])) --len;
while(len && *in && isspace(*in)) ++in, --len;
if (len) {
out = strndup(in, len);
}
}
return out;
}
isspace
Ayuda a recortar todos los espacios en blanco.
strndup
para crear un nuevo buffer de cadena excluyendo espacios.strndup()
no es parte del estándar C sino solo Posix. Pero como es bastante fácil de implementar, no es gran cosa.
trim_space("")
vuelve NULL
. Esperaría un puntero para hacerlo ""
. int len;
debería ser size_t len;
. isspace(in[len - 1])
UB cuando in[len - 1] < 0
.
while (isspace((unsigned char) *in) in++;
antes len = strlen(in);
sería más eficiente que la posteriorwhile(len && *in && isspace(*in)) ++in, --len;
Personalmente, rodaría el mío. Puede usar strtok, pero debe tener cuidado al hacerlo (especialmente si está eliminando los personajes principales) para saber qué memoria es qué.
Deshacerse de los espacios finales es fácil y bastante seguro, ya que puede poner un 0 en la parte superior del último espacio, contando desde el final. Deshacerse de los espacios principales significa mover las cosas. Si quieres hacerlo en su lugar (probablemente sensato), puedes seguir cambiando todo hacia atrás un personaje hasta que no haya un espacio inicial. O, para ser más eficiente, puede encontrar el índice del primer carácter no espacial y cambiar todo de nuevo por ese número. O bien, puede usar un puntero al primer carácter no espacial (pero luego debe tener cuidado de la misma manera que lo hace con strtok).
#include "stdafx.h"
#include "malloc.h"
#include "string.h"
int main(int argc, char* argv[])
{
char *ptr = (char*)malloc(sizeof(char)*30);
strcpy(ptr," Hel lo wo rl d G eo rocks!!! by shahil sucks b i g tim e");
int i = 0, j = 0;
while(ptr[j]!='\0')
{
if(ptr[j] == ' ' )
{
j++;
ptr[i] = ptr[j];
}
else
{
i++;
j++;
ptr[i] = ptr[j];
}
}
printf("\noutput-%s\n",ptr);
return 0;
}
Un poco tarde para el juego, pero arrojaré mis rutinas a la refriega. Probablemente no sean los más eficientes, pero creo que son correctos y simples (con rtrim()
empujar el sobre de complejidad):
#include <ctype.h>
#include <string.h>
/*
Public domain implementations of in-place string trim functions
Michael Burr
michael.burr@nth-element.com
2010
*/
char* ltrim(char* s)
{
char* newstart = s;
while (isspace( *newstart)) {
++newstart;
}
// newstart points to first non-whitespace char (which might be '\0')
memmove( s, newstart, strlen( newstart) + 1); // don't forget to move the '\0' terminator
return s;
}
char* rtrim( char* s)
{
char* end = s + strlen( s);
// find the last non-whitespace character
while ((end != s) && isspace( *(end-1))) {
--end;
}
// at this point either (end == s) and s is either empty or all whitespace
// so it needs to be made empty, or
// end points just past the last non-whitespace character (it might point
// at the '\0' terminator, in which case there's no problem writing
// another there).
*end = '\0';
return s;
}
char* trim( char* s)
{
return rtrim( ltrim( s));
}
char
argumento de isspace()
que (unsigned char)
para evitar un comportamiento indefinido en valores negativos potenciales. También evite mover la cuerda ltrim()
si no es necesaria.
La mayoría de las respuestas hasta ahora hacen uno de los siguientes:
strlen()
primero, haciendo un segundo pase a través de toda la cadena.Esta versión solo hace un pase y no retrocede. Por lo tanto, puede funcionar mejor que los demás, aunque solo si es común tener cientos de espacios finales (lo cual no es inusual cuando se trata con el resultado de una consulta SQL).
static char const WHITESPACE[] = " \t\n\r";
static void get_trim_bounds(char const *s,
char const **firstWord,
char const **trailingSpace)
{
char const *lastWord;
*firstWord = lastWord = s + strspn(s, WHITESPACE);
do
{
*trailingSpace = lastWord + strcspn(lastWord, WHITESPACE);
lastWord = *trailingSpace + strspn(*trailingSpace, WHITESPACE);
}
while (*lastWord != '\0');
}
char *copy_trim(char const *s)
{
char const *firstWord, *trailingSpace;
char *result;
size_t newLength;
get_trim_bounds(s, &firstWord, &trailingSpace);
newLength = trailingSpace - firstWord;
result = malloc(newLength + 1);
memcpy(result, firstWord, newLength);
result[newLength] = '\0';
return result;
}
void inplace_trim(char *s)
{
char const *firstWord, *trailingSpace;
size_t newLength;
get_trim_bounds(s, &firstWord, &trailingSpace);
newLength = trailingSpace - firstWord;
memmove(s, firstWord, newLength);
s[newLength] = '\0';
}
strspn()
y strcspn()
en un ciclo cerrado. Esto es muy ineficiente y la sobrecarga empequeñecerá la ventaja no comprobada del pase único hacia adelante. strlen()
generalmente se expande en línea con un código muy eficiente, no es una preocupación real. Recortar el principio y el final de la cadena será mucho más rápido que probar la blancura de cada carácter de la cadena, incluso en el caso especial de cadenas con muy pocos caracteres o no caracteres no blancos.
Esta es la implementación más corta posible que se me ocurre:
static const char *WhiteSpace=" \n\r\t";
char* trim(char *t)
{
char *e=t+(t!=NULL?strlen(t):0); // *e initially points to end of string
if (t==NULL) return;
do --e; while (strchr(WhiteSpace, *e) && e>=t); // Find last char that is not \r\n\t
*(++e)=0; // Null-terminate
e=t+strspn (t,WhiteSpace); // Find first char that is not \t
return e>t?memmove(t,e,strlen(e)+1):t; // memmove string contents and terminator
}
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; }
Estas funciones modificarán el búfer original, por lo que si se asigna dinámicamente, se puede liberar el puntero original.
#include <string.h>
void rstrip(char *string)
{
int l;
if (!string)
return;
l = strlen(string) - 1;
while (isspace(string[l]) && l >= 0)
string[l--] = 0;
}
void lstrip(char *string)
{
int i, l;
if (!string)
return;
l = strlen(string);
while (isspace(string[(i = 0)]))
while(i++ < l)
string[i-1] = string[i];
}
void strip(char *string)
{
lstrip(string);
rstrip(string);
}
rstrip()
invoca un comportamiento indefinido en la cadena vacía. lstrip()
es innecesariamente lento en la cadena con una porción inicial larga de caracteres de espacio en blanco. isspace()
No se debe pasar un char
argumento porque invoca un comportamiento indefinido en valores negativos diferentes de EOF
.
¿Qué piensa sobre el uso de la función StrTrim definida en el encabezado Shlwapi.h.? Es sencillo en lugar de definir por su cuenta.
Los detalles se pueden encontrar en:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx
Si tienes
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
esto te dará ausCaptain
como "GeorgeBailey"
no "GeorgeBailey "
.
Para recortar mis cuerdas de ambos lados, uso el oldie pero el gooody;) Puede recortar cualquier cosa con ascii menos que un espacio, lo que significa que los caracteres de control también se recortarán.
char *trimAll(char *strData)
{
unsigned int L = strlen(strData);
if(L > 0){ L--; }else{ return strData; }
size_t S = 0, E = L;
while((!(strData[S] > ' ') || !(strData[E] > ' ')) && (S >= 0) && (S <= L) && (E >= 0) && (E <= L))
{
if(strData[S] <= ' '){ S++; }
if(strData[E] <= ' '){ E--; }
}
if(S == 0 && E == L){ return strData; } // Nothing to be done
if((S >= 0) && (S <= L) && (E >= 0) && (E <= L)){
L = E - S + 1;
memmove(strData,&strData[S],L); strData[L] = '\0';
}else{ strData[0] = '\0'; }
return strData;
}
size_t
lugar de unsigned int
. El código tiene muchas pruebas redundantes e invoca un comportamiento indefinido strncpy(strData,&strData[S],L)
porque las matrices de origen y destino se superponen. Usar en memmove()
lugar de strncpy()
.
Solo incluyo código porque el código publicado hasta ahora parece subóptimo (y aún no tengo el representante para comentar).
void inplace_trim(char* s)
{
int start, end = strlen(s);
for (start = 0; isspace(s[start]); ++start) {}
if (s[start]) {
while (end > 0 && isspace(s[end-1]))
--end;
memmove(s, &s[start], end - start);
}
s[end - start] = '\0';
}
char* copy_trim(const char* s)
{
int start, end;
for (start = 0; isspace(s[start]); ++start) {}
for (end = strlen(s); end > 0 && isspace(s[end-1]); --end) {}
return strndup(s + start, end - start);
}
strndup()
es una extensión de GNU. Si no lo tiene o algo equivalente, enrolle el suyo. Por ejemplo:
r = strdup(s + start);
r[end-start] = '\0';
isspace(0)
se define como falso, puede simplificar ambas funciones. También mueva el memmove()
interior del if
bloque.
Aquí uso la asignación de memoria dinámica para recortar la cadena de entrada a la función trimStr. Primero, encontramos cuántos caracteres no vacíos existen en la cadena de entrada. Luego, asignamos una matriz de caracteres con ese tamaño y nos ocupamos del carácter terminado en nulo. Cuando usamos esta función, necesitamos liberar la memoria dentro de la función principal.
#include<stdio.h>
#include<stdlib.h>
char *trimStr(char *str){
char *tmp = str;
printf("input string %s\n",str);
int nc = 0;
while(*tmp!='\0'){
if (*tmp != ' '){
nc++;
}
tmp++;
}
printf("total nonempty characters are %d\n",nc);
char *trim = NULL;
trim = malloc(sizeof(char)*(nc+1));
if (trim == NULL) return NULL;
tmp = str;
int ne = 0;
while(*tmp!='\0'){
if (*tmp != ' '){
trim[ne] = *tmp;
ne++;
}
tmp++;
}
trim[nc] = '\0';
printf("trimmed string is %s\n",trim);
return trim;
}
int main(void){
char str[] = " s ta ck ove r fl o w ";
char *trim = trimStr(str);
if (trim != NULL )free(trim);
return 0;
}
Así es como lo hago. Recorta la cadena en su lugar, por lo que no debe preocuparse por desasignar una cadena devuelta o perder el puntero a una cadena asignada. Puede que no sea la respuesta más corta posible, pero debería ser clara para la mayoría de los lectores.
#include <ctype.h>
#include <string.h>
void trim_str(char *s)
{
const size_t s_len = strlen(s);
int i;
for (i = 0; i < s_len; i++)
{
if (!isspace( (unsigned char) s[i] )) break;
}
if (i == s_len)
{
// s is an empty string or contains only space characters
s[0] = '\0';
}
else
{
// s contains non-space characters
const char *non_space_beginning = s + i;
char *non_space_ending = s + s_len - 1;
while ( isspace( (unsigned char) *non_space_ending ) ) non_space_ending--;
size_t trimmed_s_len = non_space_ending - non_space_beginning + 1;
if (s != non_space_beginning)
{
// Non-space characters exist in the beginning of s
memmove(s, non_space_beginning, trimmed_s_len);
}
s[trimmed_s_len] = '\0';
}
}
char* strtrim(char* const str)
{
if (str != nullptr)
{
char const* begin{ str };
while (std::isspace(*begin))
{
++begin;
}
auto end{ begin };
auto scout{ begin };
while (*scout != '\0')
{
if (!std::isspace(*scout++))
{
end = scout;
}
}
auto /* std::ptrdiff_t */ const length{ end - begin };
if (begin != str)
{
std::memmove(str, begin, length);
}
str[length] = '\0';
}
return str;
}