Se você pode modificar a sequência:
// 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;
}
Se você não pode modificar a string, pode usar basicamente o mesmo 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
é uma variável local e sua alteração não altera o ponteiro original que está sendo passado. As chamadas de função em C são sempre passadas por valor, nunca passadas por referência.free()
função. Muito pelo contrário - projetei isso para evitar a necessidade de alocação de memória para obter eficiência. Se o endereço passado foi alocado dinamicamente, o responsável pela chamada ainda é responsável por liberar essa memória e o responsável pela chamada precisa garantir que não substitua esse valor pelo valor retornado aqui.isspace
aunsigned char
, caso contrário, você invocar um comportamento indefinido.Aqui está um que muda a string para a primeira posição do seu buffer. Você pode querer esse comportamento para que, se você alocou dinamicamente a sequência, ainda pode liberá-la no mesmo ponteiro que trim () retorna:
Teste de correção:
O arquivo de origem foi trim.c. Compilado com 'cc -Wall trim.c -o trim'.
fonte
isspace
aunsigned char
, caso contrário, você invocar um comportamento indefinido.isspace()
então por que haveria uma diferença entre" "
e"\n"
? Eu adicionei testes de unidade para novas linhas e parece OK para mim ... ideone.com/bbVmqo*(endp + 1) = '\0';
. O teste de exemplo na resposta usa um buffer de 64 que evita esse problema.Minha solução A sequência deve ser alterável. A vantagem, acima de algumas das outras soluções, de que ela move a parte não espacial para o início, para que você possa continuar usando o ponteiro antigo, caso precise liberá-lo () mais tarde.
Esta versão cria uma cópia da string com strndup () em vez de editá-la no lugar. strndup () requer _GNU_SOURCE, então talvez você precise criar seu próprio strndup () com malloc () e strncpy ().
fonte
trim()
chama UB ses
for""
comoisspace()
seria a primeira chamadaisspace(p[-1])
ep[-1]
não necessariamente faz referência a um local legal.isspace
aunsigned char
, caso contrário, você invocar um comportamento indefinido.if(l==0)return;
para evitar comprimento zero strAqui está minha mini biblioteca C para aparar à esquerda, direita, ambos, todos, no lugar e separados, e aparar um conjunto de caracteres especificados (ou espaço em branco por padrão).
conteúdo de strlib.h:
conteúdo de strlib.c:
A única rotina principal faz tudo. Ele apara se src == dst , caso contrário, funciona como as
strcpy
rotinas. Apara um conjunto de caracteres especificado no delim da stringou espaço em branco se nulo. Apara esquerda, direita, ambos e tudo (como tr). Não há muito e itera sobre a string apenas uma vez. Algumas pessoas podem reclamar que o corte à direita começa à esquerda, no entanto, não é necessário nenhum strlen que comece à esquerda. (De uma forma ou de outra, você precisa chegar ao final da sequência para obter as guarnições corretas, para que você possa fazer o trabalho à medida que for avançando.) . Como a solução funciona da esquerda para a direita e itera apenas uma vez, ela pode ser expandida para funcionar também em fluxos. Limitações: não funciona em cadeias unicode .fonte
dtab[*d]
não é convertido*d
paraunsigned int
antes de usá-lo como um índice de matriz. Em um sistema com char assinado, será lido odtab[-127]
que causará bugs e possivelmente travará.dtab[*delim++]
porque oschar
valores do índice devem ser convertidos emunsigned char
. O código assume 8 bitschar
.delim
deve ser declarado comoconst char *
.dtab[0xFF & (unsigned int)*d]
seria mais claro comodtab[(unsigned char)*d]
. O código funciona em cadeias codificadas em UTF-8, mas não remove seqüências de espaçamento não ASCII.Aqui está minha tentativa de uma função de ajuste simples, mas correta no local.
fonte
while ((end >= begin) && isspace(str[end]))
evitar UB quandostr is
"". Prevents
str [-1] `.isspace
aunsigned char
, caso contrário, você invocar um comportamento indefinido.<ctype.h>
destinam-se a trabalhar com ints, que representam umunsigned char
ou o valor especialEOF
. Consulte stackoverflow.com/q/7131026/225757 .Tarde para a festa
Características:
1. Apare o início rapidamente, como em várias outras respostas.
2. Depois de ir para o final, corte a direita com apenas 1 teste por loop. Como @ jfm3, mas funciona para uma sequência de espaços em branco)
3. Para evitar um comportamento indefinido quando
char
é assinadochar
, faça a conversão*s
paraunsigned char
.@chqrlie comentou que o item acima não altera a sequência aparada. Para fazer isso ....
fonte
Aqui está uma solução semelhante à rotina de modificação no local @ adam-rosenfields, mas sem recorrer desnecessariamente ao strlen (). Como @jkramer, a string é ajustada à esquerda no buffer para que você possa liberar o mesmo ponteiro. Não é ideal para cadeias grandes, pois não usa memmove. Inclui os operadores ++ / - mencionados pelo @ jfm3. Testes de unidade baseados em FCTX incluídos.
fonte
Outro, com uma linha fazendo o trabalho real:
fonte
%n
especificador de conversão, e no final é apenas mais simples fazê-lo manualmente, receio.Não gostei da maioria dessas respostas porque elas fizeram um ou mais dos seguintes ...
Aqui está a minha versão:
fonte
isspace
aunsigned char
, caso contrário, você invocar um comportamento indefinido.while (isspace((unsigned char) *szWrite)) szWrite++;
impediria isso. O código também copia todo o espaço em branco à direita.*szWrite = *szRead
quando os ponteiros não são iguais ignoraria as gravações nesse caso, mas adicionamos outra comparação / ramificação. Com a CPU / MMU / BP moderna, não tenho idéia se essa verificação seria uma perda ou um ganho. Com processadores e arquiteturas de memória mais simples, é mais barato fazer a cópia e pular a comparação.Muito tarde para a festa ...
Solução de varredura direta de passagem única sem retorno. Cada caractere na cadeia de origem é testado exatamente
uma vezduas vezes. (Portanto, deve ser mais rápido que a maioria das outras soluções aqui, especialmente se a cadeia de origem tiver muitos espaços à direita.)Isso inclui duas soluções, uma para copiar e aparar uma sequência de origem em outra sequência de destino e a outra para aparar a sequência de origem no local. Ambas as funções usam o mesmo código.
A sequência (modificável) é movida no local, portanto, o ponteiro original permanece inalterado.
fonte
'\0'
e testada comisspace()
. Parece um desperdício testar todos os personagensisspace()
. O retorno do final da sequência deve ser mais eficiente para casos não patológicos.trim()
ESTÁ BEM. Caixa de canto:trim2(char *d, const char *s)
apresenta problemas ao sed,s
sobrepor es < d
.trim()
se comportar? Você está pedindo para aparar e copiar uma sequência na memória ocupada pela própria sequência. Ao contráriomemmove()
, isso requer determinar o comprimento da cadeia de origem antes de fazer o corte em si, o que requer a varredura de toda a cadeia por um tempo adicional. Melhor escrever umartrim2()
função diferente que saiba copiar a fonte para o destino de trás para frente e provavelmente use um argumento adicional para o comprimento da string da fonte.Não sei ao certo o que você considera "indolor".
C strings são bastante dolorosas. Podemos encontrar a primeira posição de caractere sem espaço em branco trivialmente:
Podemos encontrar a última posição de caractere não-espaço em branco com dois movimentos triviais semelhantes:
(Poupei o trabalho de usar os operadores
*
e++
ao mesmo tempo.)A questão agora é o que você faz com isso? O tipo de dados em questão não é realmente um grande resumo robusto
String
e fácil de se pensar, mas, na verdade, quase nada além de uma matriz de bytes de armazenamento. Na falta de um tipo de dados robusto, é impossível escrever uma função que faça o mesmo que achomp
função do PHperytonby . O que essa função em C retornaria?fonte
do { q--; } ...
de saber*q != 0
.Use uma biblioteca de cadeias , por exemplo:
... como você diz que esse é um problema "comum", sim, você precisa incluir um #include ou não, e ele não está incluído na libc, mas não invente seu próprio trabalho de hack, armazenando ponteiros aleatórios e size_t, que só levam a estouros de buffer.
fonte
Se você estiver usando
glib
, poderá usar g_strstripfonte
Apenas para continuar crescendo, mais uma opção com uma string modificável:
fonte
strlen()
retorna umsize_t
que pode exceder o intervalo deint
. espaço em branco não é restrito ao caractere de espaço. Finalmente, mas mais importante: Comportamento indefinido ativadostrcpy(string, string + i * sizeof(char));
porque as matrizes de origem e destino se sobrepõem. Use emmemmove()
vez destrcpy()
.while (isspace((int)string[i])) string[i--] = '\0';
pode fazer um loop além do início da string. Você deve combinar esse loop com as linhas anteriores e seguintes e escreverwhile (i > 0 && isspace((unsigned char)string[--i])) { string[i] = '\0'; } size_t end = i;
end
pois não apontava para o byte nulo à direita e vocêend = ++i;
ainda tinha um problema para cadeias contendo todos os caracteres de espaço em branco. Eu apenas consertei o código.Sei que tenho muitas respostas, mas posto aqui minha resposta para ver se minha solução é boa o suficiente.
fonte
isspace(*str)
UB quando*str < 0
.size_t n
é bom, mas a interface não informa o chamador de maneira alguma sobren
ser muito pequeno para uma sequência cortada completa. Consideretrim(out, 12, "delete data not")
A maneira mais fácil de ignorar espaços à esquerda em uma string é imho,
fonte
" foo bar "
.Ok, esta é a minha opinião sobre a questão. Eu acredito que é a solução mais concisa que modifica a string no lugar (
free
funcionará) e evita qualquer UB. Para strings pequenas, provavelmente é mais rápido que uma solução que envolva memmove.fonte
b > str
teste é necessário apenas uma vez.*b = 0;
necessário apenas uma vez.isspace
ajuda a aparar todos os espaços em branco.strndup
para criar um novo buffer de string excluindo espaços.fonte
strndup()
não faz parte do padrão C, mas apenas do Posix. Mas, como é muito fácil de implementar, não é grande coisa.trim_space("")
retornaNULL
. Eu esperaria um ponteiro para""
.int len;
deveria sersize_t len;
.isspace(in[len - 1])
UB quandoin[len - 1] < 0
.while (isspace((unsigned char) *in) in++;
anteslen = strlen(in);
seria mais eficiente que a posteriorwhile(len && *in && isspace(*in)) ++in, --len;
Pessoalmente, eu rolaria o meu. Você pode usar o strtok, mas precisa tomar cuidado (principalmente se estiver removendo caracteres iniciais) para saber qual é a memória.
Livrar-se dos espaços à direita é fácil e bastante seguro, pois você pode simplesmente colocar um 0 no topo do último espaço, contando desde o final. Livrar-se dos espaços principais significa mudar as coisas. Se você quiser fazê-lo no lugar (provavelmente sensato), basta mudar tudo de volta para um personagem até que não haja espaço à frente. Ou, para ser mais eficiente, você pode encontrar o índice do primeiro caractere não espacial e mudar tudo de volta por esse número. Ou, você pode simplesmente usar um ponteiro para o primeiro caractere não espacial (mas, em seguida, você precisa ter cuidado da mesma maneira que no strtok).
fonte
fonte
Um pouco tarde para o jogo, mas vou jogar minhas rotinas na briga. Provavelmente, eles não são os mais eficientes, mas acredito que estão corretos e são simples (com o
rtrim()
empurrão do envelope da complexidade):fonte
char
argumento paraisspace()
a(unsigned char)
evitar um comportamento indefinido em valores potencialmente negativos. Evite também mover a corda, seltrim()
for necessário.Até agora, a maioria das respostas segue um destes procedimentos:
strlen()
primeiro, fazendo uma segunda passagem por toda a cadeia.Esta versão faz apenas uma passagem e não retrocede. Portanto, ele pode ter um desempenho melhor que os outros, embora apenas se for comum ter centenas de espaços à direita (o que não é incomum ao lidar com a saída de uma consulta SQL).
fonte
strspn()
estrcspn()
em um circuito fechado. Isso é muito ineficiente e a sobrecarga diminui a vantagem não comprovada do passe único para frente.strlen()
geralmente é expandido em linha com código muito eficiente, não é uma preocupação real. Aparar o início e o fim da sequência será muito mais rápido do que testar todos os caracteres da sequência quanto à brancura, mesmo no caso especial de sequências com muito poucos ou nenhum caractere não branco.Esta é a implementação mais curta possível em que consigo pensar:
fonte
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; }
Essas funções modificarão o buffer original, portanto, se alocado dinamicamente, o ponteiro original poderá ser liberado.
fonte
rstrip()
chama um comportamento indefinido na sequência vazia.lstrip()
é desnecessariamente lento na string com uma longa porção inicial de caracteres de espaço em branco.isspace()
não deve receber umchar
argumento porque invoca um comportamento indefinido em valores negativos diferentes deEOF
.O que você acha sobre o uso da função StrTrim definida no cabeçalho Shlwapi.h.? É simples e bastante definidor por conta própria.
Detalhes podem ser encontrados em:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb773454(v=vs.85).aspx
Se você tiver,
char ausCaptain[]="GeorgeBailey ";
StrTrim(ausCaptain," ");
isso dará
ausCaptain
como"GeorgeBailey"
não"GeorgeBailey "
.fonte
Para aparar minhas cordas dos dois lados, uso o oldie, mas o bobo;) Ele pode aparar qualquer coisa com ascii menor que um espaço, o que significa que os caracteres de controle também serão aparados!
fonte
size_t
vez deunsigned int
. O código possui muitos testes redundantes e invoca um comportamento indefinidostrncpy(strData,&strData[S],L)
porque as matrizes de origem e destino se sobrepõem. Use emmemmove()
vez destrncpy()
.Estou incluindo apenas o código porque o código postado até o momento parece subótimo (e ainda não tenho o representante para comentar).
strndup()
é uma extensão GNU. Se você não o possui ou algo equivalente, faça o seu. Por exemplo:fonte
isspace(0)
é definido como falso, você pode simplificar as duas funções. Mova também omemmove()
interior doif
bloco.Aqui eu uso a alocação dinâmica de memória para aparar a string de entrada na função trimStr. Primeiro, descobrimos quantos caracteres não vazios existem na string de entrada. Em seguida, alocamos uma matriz de caracteres com esse tamanho e cuidamos do caractere terminado nulo. Quando usamos essa função, precisamos liberar a memória dentro da função principal.
fonte
Aqui está como eu faço isso. Ele apara a sequência no lugar, portanto, não se preocupe em desalocar uma sequência retornada ou perder o ponteiro para uma sequência alocada. Pode não ser a resposta mais curta possível, mas deve ficar clara para a maioria dos leitores.
fonte
fonte