Função C para remover espaços ao redor de uma string

Criado: 19/04/2026 00:03 -03

Conteúdo

Introdução

Nesse post eu vou lhe ensinar a como implementar passo a passo o que está no título e vou explicar em detalhes o funcionamento dela. O que quero é uma função que quando recebe um argumento como por exemplo "    isac    " , retorne "isac". Muito simples. Vamos ver como fazer isso usando C.

Primeiro, qual deve ser a lógica? Começando no ínicio da string, enquanto o caractere for um espaço ou qualquer outro escape code semelhante é pra remover esse caractere. Depois é para ir até o fim da string e fazer o mesmo processo porém percorrendo a string de trás para frente.

Strings em C são arrays de caracteres, e quando passamos um array de char como argumento ele decai para char *, então estaremos o tempo todo lidando com ponteiros o que facilita muito o nosso trabalho.

// Parte 1
char *
trim(char *str)
{
  if (str == NULL) return NULL; // Erro
  return NULL; // Será substituído depois
}

Descobrindo o endereço do começo e do fim da string

Primeiro de tudo, precisamos saber onde fica o começo e o fim da string. O começo é fácil. O próprio ponteiro aponta para o começo da string, por exemplo:

char *text = "isac";
printf("'%c'", *text); // Saída -> 'i'

Mas e o fim da string? Também é simples. Basta lembrar que a função strlen() retorna o comprimento da string e que a posição do último caractere será sempre o comprimento da string menos 1.

char *text = "isac";
printf("%zd", strlen(text) - 1); // Saída -> 3 que é justamente a posição da letra 'c'

Então vamos definir dois ponteiros, um para o começo e outro para o final da string.

// Parte 2
#include <string.h>

char *
trim(char *str)
{
  if (str == NULL) return NULL;
  
  char *start = str;
  char *end = str + strlen(str) - 1; // Estamos mudando o endereço do ponteiro
  return NULL;
}

Lógica de remover caracteres

Agora vamos implementar a lógica de remover caracteres. O que na verdade vamos fazer é simplemente mover o ponteiro para frente ou para trás. No fundo no fundo não estaremos apagando algo da memória. Vamos começar do início. Enquanto houver um caractere que seja espaço ou semelhante, mova o ponteiro para frente. Vamos usar o while para isso. A forma mais neat que conheço é essa:

// Parte 2
#include <string.h>
#include <stdbool.h>

char *
trim(char *str)
{
  if (str == NULL) return NULL;
  
  char *start = str;
  char *end = str + strlen(str) - 1;
  
  while (*start) {
    bool remove = false;
    remove = (*start == ' ' || *start == '\n' || *start == '\t' ||
              *start == '\r' || *start == '\f' || *start == '\v');
              
    if (!remove) break;
    
    start++;
  }
  return NULL;
}

Vamos por partes. Primeiro, o loop while executa o que o segue enquanto a sua condição for diferente de 0. Um char também é um número(que vai de -128 a 127)(e.g. você pode usar tanto 'a' quanto 97 para representar a mesma coisa), então enquanto o caractere for diferente de 0(ou '\0') que indica o fim da string ele vai executar o que o segue.

O operador unário * quando usado em um ponteiro ele retorna o valor que o ponteiro aponta. Nesse casso *start retornará um char.

As seguintes linhas:
remove = (*start == ' ' || *start == '\n' || *start == '\t' ||
              *start == '\r' || *start == '\f' || *start == '\v');

Poderiam ser implementadas como um gigante if, mas ficaria muito feio:

if (*start == ' ' || *start == '\n' || *start == '\t' || start == '\r' || *start == '\f' || *start == '\v') {
  remove = true;
}

Se remove = true o statement if (!remove) break; será ignorado não saindo do loop e posteriormente irá incrementar o ponteiro em 1 com start++;, 'removendo' o caráctere atual.

A lógica para remover os caracteres do final é semelhante. A principal diferença está na condição do loop while.

// Parte 3
#include <string.h>
#include <stdbool.h>

char *
trim(char *str)
{
  if (str == NULL) return NULL;
  
  char *start = str;
  char *end = str + strlen(str) - 1;
  
  while (*start) {
    bool remove = false;
    remove = (*start == ' ' || *start == '\n' || *start == '\t' ||
              *start == '\r' || *start == '\f' || *start == '\v');
              
    if (!remove) break;
    
    start++;
  }
  
  while (end > start) {
    bool remove = false;
    remove = (*end == ' ' || *end == '\n' || *end == '\t' ||
              *end == '\r' || *end == '\f' || *end == '\v');
              
    if (!remove) break;
    
    end--;
  }
  
  return NULL;
}

Um ponteiro é um endereço de memória, e esse endereço por sua vez é um número, então podemos fazer comparações entre ponteiros. O bloco while (end > start) end--; em outras palavras significa 'a partir do final, enquanto não estivermos a uma unidade de distância do começo, volte uma unidade'.

Dois ponteiros e um sonho

Certo, agora temos dois ponteiros. Um aponta para o primeiro caractere legível e outro para o último caractere legível. E o que fazemos com eles? Precisamos de três coisas para criar uma nova string. Vamos marcar com ✓ o que temos e com ✗ o que não temos ainda.

  1. O endereço do começo da string ✓
  2. O comprimento da string ✗
  3. Algum lugar para colocar a string ✗

Calculando o comprimento da nova string

O comprimento da nova string será end - start + 1. Para entender isso basta executar o seguinte exemplo:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int
main(void)
{
  char *str = "isac";
  char *start = str;
  char *end = str + strlen(str) - 1;
  
  size_t len = end - start + 1;
  
  printf("string: %s\n", str);
  printf("start: %p -> '%c'\n", start, *start);
  printf("end: %p -> '%c'\n", end, *end);
  printf("end - start: %d\n", (end - start));
  printf("length: %zd\n", len);
  
  return EXIT_SUCCESS;
}

Cuja saída será, por exemplo:

string: isac
start: 0x402000 -> 'i'
end: 0x402003 -> 'c'
end - start: 3
length: 4

Certo, sabemos como calcular o comprimento da nova string. Agora, vamos adicionar isso ao código.

// Parte 4
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

char *
trim(char *str)
{
  if (str == NULL) return NULL;
  
  char *start = str;
  char *end = str + strlen(str) - 1;
  
  while (*start) {
    bool remove = false;
    remove = (*start == ' ' || *start == '\n' || *start == '\t' ||
              *start == '\r' || *start == '\f' || *start == '\v');
              
    if (!remove) break;
    
    start++;
  }
  
  while (end > start) {
    bool remove = false;
    remove = (*end == ' ' || *end == '\n' || *end == '\t' ||
              *end == '\r' || *end == '\f' || *end == '\v');
              
    if (!remove) break;
    
    end--;
  }
  
  /* Testamos se end >= start para que caso o cálculo venha
   * a produzir um número negativo esse calculo não seja realizado
   * mas sim substituido por zero.
   */
  size_t len = (end >= start) ? (end - start + 1) : 0;
  
  return NULL;
}

Precisamos de um espaço - Código final

Agora, as última coisa que precisamos fazer é arranjar um lugar para colocar a nova string, colocar a nova string nesse espaço e retornar esse espaço. Primeiro vamos alocar memória manualmente. É bem simples. Precisamos apenas de espaço suficiente para a string e o caractere nulo; malloc(len + 1) vai servir. Agora, precisamos mover a string em si para esse espaço. Vamos usar a função memcpy() para isso. Essa função copia o tanto de bytes que queremos de um endereço para outro. memcpy(new_str, start, len) servirá. Em resumo, copiará para new_str len bytes a partir de start

#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

/*
 * Function to remove spaces and similar characters around a string
 */
char *
trim(char *str)
{
  if (str == NULL) return NULL;
  
  char *start = str;
  char *end = str + strlen(str) - 1;
  
  while (*start) {
    bool remove = false;
    remove = (*start == ' ' || *start == '\n' || *start == '\t' ||
              *start == '\r' || *start == '\f' || *start == '\v');
              
    if (!remove) break;
    
    start++;
  }
  
  while (end > start) {
    bool remove = false;
    remove = (*end == ' ' || *end == '\n' || *end == '\t' ||
              *end == '\r' || *end == '\f' || *end == '\v');
              
    if (!remove) break;
    
    end--;
  }
  
  size_t len = (end >= start) ? (end - start + 1) : 0;
  char *new_str = malloc(len + 1);
  if (new_str == NULL) return NULL;
  
  if (len > 0) memcpy(new_str, start, len);
  
  new_str[len] = '\0';
  return new_str;
}

Eu apenas adicionei uma condição extra para não realizar uma cópia desnecessária de 0 caracteres. E para finalizar adicionei o caractere nulo na última posição da string. Vejamos a função em ação:

Exemplo de uso da função

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

char *
trim(char *str)
{
--- Código da função aqui ---
}

int
main(void)
{
  char *name = "    isac     ";
  char *trimmed = trim(name);

  printf("'%s' -> '%s'\n", name, trimmed);
  free(trimmed); // MUITO IMPORTANTE, NÃO ESQUEÇA 
                 // DE LIBERAR A MEMÓRIA ALOCADA PARA A NOVA STRING
  
  return EXIT_SUCCESS;
}

Saída:

'    isac     ' -> 'isac'