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
tounsigned 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:
Prueba de corrección:
El archivo fuente fue trim.c. Compilado con 'cc -Wall trim.c -o trim'.
fuente
isspace
tounsigned 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.
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 ().
fuente
trim()
invoca UB sis
es""
comoisspace()
sería la primera llamadaisspace(p[-1])
yp[-1]
no necesariamente hace referencia a una ubicación legal.isspace
tounsigned char
, de lo contrario invocas un comportamiento indefinido.if(l==0)return;
para evitar str de longitud ceroAquí 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).
contenido de strlib.h:
contenido de strlib.c:
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 .fuente
dtab[*d]
no convierte*d
aunsigned 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 loschar
valores de índice deben ser convertidos aunsigned char
. El código asume 8 bitschar
.delim
debe declararse comoconst char *
.dtab[0xFF & (unsigned int)*d]
sería más claro comodtab[(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.
fuente
while ((end >= begin) && isspace(str[end]))
para evitar UB cuandostr is
"". Prevents
str [-1] `.isspace
tounsigned char
, de lo contrario invocas un comportamiento indefinido.<ctype.h>
están destinadas a trabajar con ints, que representan unounsigned char
o el valor especialEOF
. 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 firmachar
, se envía*s
aunsigned char
.@chqrlie comentó que lo anterior no desplaza la cadena recortada. Para hacerlo ...
fuente
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.
fuente
Otro, con una línea haciendo el trabajo real:
fuente
%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:
fuente
isspace
tounsigned 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 vezdos 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.
fuente
'\0'
y luego conisspace()
. Parece un desperdicio probar con todos los personajesisspace()
. 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 sed,s
superponen ys < d
.trim()
comportarse? Está pidiendo recortar y copiar una cadena en la memoria ocupada por la cadena misma. A diferenciamemmove()
, 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 unartrim2()
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:
Podemos encontrar la última posición del personaje sin espacios en blanco con dos movimientos triviales similares:
(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 lachomp
función de PHperytonby . ¿Qué devolvería tal función en C?fuente
do { q--; } ...
de saber*q != 0
.Use una biblioteca de cadenas , por ejemplo:
... 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.
fuente
Si está usando
glib
, entonces puede usar g_strstripfuente
Solo para mantener este crecimiento, una opción más con una cadena modificable:
fuente
strlen()
devuelve unsize_t
que puede exceder el rango deint
. el espacio en blanco no está restringido al carácter de espacio. Finalmente, pero lo más importante: comportamiento indefinidostrcpy(string, string + i * sizeof(char));
porque las matrices de origen y destino se superponen. Usar enmemmove()
lugar destrcpy()
.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 yend = ++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.
fuente
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 den
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,
fuente
" 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.fuente
b > str
prueba solo se necesita una vez.*b = 0;
Solo se necesita una vez.isspace
Ayuda a recortar todos los espacios en blanco.strndup
para crear un nuevo buffer de cadena excluyendo espacios.fuente
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("")
vuelveNULL
. Esperaría un puntero para hacerlo""
.int len;
debería sersize_t len;
.isspace(in[len - 1])
UB cuandoin[len - 1] < 0
.while (isspace((unsigned char) *in) in++;
anteslen = 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).
fuente
fuente
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):fuente
char
argumento deisspace()
que(unsigned char)
para evitar un comportamiento indefinido en valores negativos potenciales. También evite mover la cuerdaltrim()
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).
fuente
strspn()
ystrcspn()
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:
fuente
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.
fuente
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 unchar
argumento porque invoca un comportamiento indefinido en valores negativos diferentes deEOF
.¿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 "
.fuente
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.
fuente
size_t
lugar deunsigned int
. El código tiene muchas pruebas redundantes e invoca un comportamiento indefinidostrncpy(strData,&strData[S],L)
porque las matrices de origen y destino se superponen. Usar enmemmove()
lugar destrncpy()
.Solo incluyo código porque el código publicado hasta ahora parece subóptimo (y aún no tengo el representante para comentar).
strndup()
es una extensión de GNU. Si no lo tiene o algo equivalente, enrolle el suyo. Por ejemplo:fuente
isspace(0)
se define como falso, puede simplificar ambas funciones. También mueva elmemmove()
interior delif
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.
fuente
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.
fuente
fuente