Funções de string em C

maio 7, 2007 às 3:17 | Publicado em artigos, C, linux, string | 12 Comentários

Perdendo meu tempo em canais de IRC (no #motd da freenode pra ser mais exato), numa rara conversa útel com o Narcotic veio a idéia de falar e comentar exemplos de uso de várias funções de manuseio de strings em C (geralmente começado com str). São várias, mas irei comentar 3 funções dessas por post. Com isso espero esclarecer as dúvidas de muitas pessoas, principalmente aquelas novatas na linguagem e as esquecidas (como eu! 😀 ).

Todas as funções são documentadas nas man pages, e as mesmas estou usando como base. As funções dos exemplos tem as mesmas declarações para melhor ser assimilado na consulta. Para ubuntu users: $ sudo apt-get install manpages-dev

Função strstr():

char *strstr(const char *haystack, const char *needle);

A função strstr() procura a primeira ocorrência de uma string (needle) em outra string determinada (haystack). Caso a condição seja satisfeita, um ponteiro com posição da ocorrência será retornado. Caso contrário, NULL será retornado.

(Para melhor visualizar, faça o download do fonte: strstr_ex.c)

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

int main(int argc, char **argv) {

        char *haystack = "Essa eh uma frase de exemplo para encontrarmos uma string.";
        char *needle = *(argv+1);
        char *ret;

        /* Ha alguma ocorrencia de needle em haystack? */
        if ((ret = strstr(haystack, needle)) != NULL) { 
                fprintf(stdout, "needle ENCONTRADO\\n");
                /* O retorno eh o ponteiro para a primeira ocorrencia em diante. */
                fprintf(stdout, "retorno: %s\\n", ret);
        } else
                fprintf(stdout, "needle NAO ENCONTRADO\\n");

        return 0;
}

ip_fix@konoha:~/C$ gcc -Wall -o strstr_ex strstr_ex.c
ip_fix@konoha:~/C$ ./strstr_ex teste
needle NAO ENCONTRADO
ip_fix@konoha:~/C$ ./strstr_ex exemplo
needle ENCONTRADO
retorno: exemplo para encontrarmos uma string.
ip_fix@konoha:~/C$ ./strstr_ex encontrarmos
needle ENCONTRADO
retorno: encontrarmos uma string.
ip_fix@konoha:~/C$

Como esperado, quando há uma ocorrência satisfeita o ponteiro é retornado a partir desse resultado.

Função strdup():

char *strdup(const char *s);

A função strdup() é muito útil em situações onde temos que alocar um espaço na memória para copiar determinada string. Sem utiliza-lá, teríamos que alocar o espaço antes com malloc(), para em seguida usar strncpy(), por exemplo, para copiar a string. A função retorna um ponteiro com a string já alocada (podendo ser liberado com free() depois de seu uso) em caso de sucesso, ou ENOMEM como erro caso a memória não tenha sida alocada por insuficiência.

(Para melhor visualizar, faça o download do fonte: strdup_ex.c)

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

int main(int argc, char **argv) {

        char *string;
        char *s = *(argv+1);

        /* SEM strdup() */

        /* Alocamos a memoria com malloc(). */
        string = (char *)malloc(strlen(s)); 
        /* Copiamos o conteudo de s pra dentro de string. (agora que possui espaco alocado). */
        strncpy(string, s, strlen(s)+1); 
        fprintf(stdout, "Sem strdup(): %s\\n", string);
        /* Liberamos o espaco alocado. */
        free(string); 

        /* COM strdup() */

        /* Alocamos a memoria exata e jah copiamos o conteudo s para dentro da string. */
        string = strdup(s);
        fprintf(stdout, "Com strdup(): %s\\n", string);
        /* Liberamos o espaco alocado (dessa vez por strdup()). */
        free(string);

        return 0;
}

ip_fix@konoha:~/C$ gcc -Wall -o strdup_ex strdup_ex.c
ip_fix@konoha:~/C$ ./strdup_ex "teste de string"
Sem strdup(): teste de string
Com strdup(): teste de string
ip_fix@konoha:~/C$

Fica bem claro a facilidade de usar strdup() em vista de que não precisamos ficar chamando malloc() e strncpy() toda vez que precisamos gravar alguma string numa variável já que a função faz todo o serviço. 😀

Função strtok():

char *strtok(char *s, const char *delim);

A função strtok() é muito útil quando precisamos “quebrar” nossa string em algum delimitador comum. Ele separa os tokens de tal forma que podemos trabalhar individualmente em cada pedaço. A função retorna um ponteiro para o próximo token ou retorn NULL caso tenha chegado ao fim.

(Para melhor visualizar, faça o download do fonte: strtok_ex.c)

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

int main() {

        /* Alocamos e copiamos a frase dentro de s. */
        char *s = strdup("Essa eh uma frase de exemplo."); 
        /* Delimitador. Nesse caso quebraremos a string nos espacos em branco. */
        char *delim = " "; 
        char *res;

        /* Primeira chamada antes do laco. */
        res = strtok(s, delim);

        while (res != NULL) {
                fprintf(stdout, "%s\\n", res);
                /* Repetimos a chamada ate o fim da string. */
                res = strtok(NULL, delim);
        }

        /* Liberamos o espaco alocado. */
        free(s); 

        return 0;
}

ip_fix@konoha:~/C$ gcc -Wall -o strtok_ex strtok_ex.c
ip_fix@konoha:~/C$ ./strtok_ex
Essa
eh
uma
frase
de
exemplo.
ip_fix@konoha:~/C$

Assim vemos grande utilidade quando pensamos em “parsear” algum tipo de dados que possua algum delimitador em comum. Claro que coisas simples, mas já um ponta pé inicial de implementações mais complexas que podem ser adaptadas.

Encerro por hoje esse post básico mas que tenho certeza que deixa claro para muita gente o uso dessas simples funções que muitas vezes é uma mão na roda em algumas tarefas. Em breve postarei mais funções interessantes com exemplos. Espero ter mais retornos no comentários, adoro dúvidas e sugestões! 😛

Anúncios

Lista encadeada em C

maio 3, 2007 às 2:55 | Publicado em artigos, C, linux, programacao | 30 Comentários

Fazia alguns meses que estava parado com a programação em linguagem C e só “dedicado” (nunca consigo ficar full time em algo) a linguagem Python. Mas depois de ler o livro A Prática da Programação (obrigado Ruda Moura :)) me fez ter aquele pique novamente de praticar já que sempre fiz algumas coisas profissionais e por diversão com a linguagem.

Resolvi falar sobre lista encadeada em C poque alguns contatos no meu MSN sempre me perguntam sobre e também porque é muito utlizado em diversos algoritmos. Apesar de já ter alguns códigos prontos resolvi refazer dando ênfase nas sugestões do livro, que realmente recomendo já que fala sobre listas, árvores, hash, entre outros mais.

Chega de papo, Show me the Code!, o código abaixo implementa um exemplo de uma lista simples encadeada onde os dados não são ordenados e a forma que são armazenados é igual uma pilha, onde o último dado a entrar é o primeiro a sair, conhecido também como LIFO (Last In, First Out).

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

#define BUFFER 64

/* Estrutura da lista declarada para armazenar nossos dados. */
typedef struct lista {
        char *nome;
        int idade;
        struct lista *proximo;
} Dados;

/* Prototipo das funcoes de manuseio dos dados. */
Dados *inicia_dados(char *nome, int idade);
Dados *insere_dados(Dados *dados, char *nome, int idade);
void exibe_dados(Dados *dados);
void busca_dados(Dados *dados, char *chave);
Dados *deleta_dados(Dados *dados);
int checa_vazio(Dados *dados);

/* Prototipo das funcoes do menu.*/
void insere(void);
void exibe(void);
void busca(void);
void deleta(void);

/* Inicializa a estrutura de dados principal. */
Dados *principal = NULL;

/* Cria a nova lista apontando o proximo no para NULL. */
Dados *inicia_dados(char *nome, int idade) {

        Dados *novo;

        novo = (Dados *)malloc(sizeof(Dados));
        novo->nome = (char *)malloc(strlen(nome)+1);
        strncpy(novo->nome, nome, strlen(nome)+1);
        novo->idade = idade;
        novo->proximo = NULL;

        return novo;
}

/* Como a lista nao esta mais vazia, apontamos o proximo no para lista anterior. */
Dados *insere_dados(Dados *dados, char *nome, int idade) {

        Dados *novo;

        novo = (Dados *)malloc(sizeof(Dados));
        novo->nome = (char *)malloc(strlen(nome)+1);
        strncpy(novo->nome, nome, strlen(nome)+1);
        novo->idade = idade;
        novo->proximo = dados;

        return novo;
}

/* Percorre todos os campos da lista e imprime ate o ponteiro proximo chegar em NULL. */
void exibe_dados(Dados *dados) {

        fprintf(stdout, "Cadastro:\\n\\n");

        fprintf(stdout, "------------------------\\n");

        for (; dados != NULL; dados = dados->proximo) {
                fprintf(stdout, "Nome: %s\\n", dados->nome);
                fprintf(stdout, "Idade: %d\\n", dados->idade);
                fprintf(stdout, "------------------------\\n");
        }

        getchar();
}

/* Percorre cada ponta comparando o nome com a chave. */
void busca_dados(Dados *dados, char *chave) {

        int achou = 0;

        fprintf(stdout, "Cadastro:\\n\\n");
        for (; dados != NULL; dados = dados->proximo) {
                if (strcmp(chave, dados->nome) == 0) {

                        fprintf(stdout, "------------------------\\n");
                        fprintf(stdout, "Nome: %s\\n", dados->nome);
                        fprintf(stdout, "Idade: %d\\n", dados->idade);
                        fprintf(stdout, "------------------------\\n");
                        achou++;
                }
        }

        if (achou == 0)
                fprintf(stdout, "Nenhum resultado encontrado.\\n");
        else
                fprintf(stdout, "Foram encontrados %d registros.\\n", achou);

        sleep(1);
}

/* Deleta o ultimo registro inserido. */
Dados *deleta_dados(Dados *dados) {

        Dados *novo;

        novo = dados->proximo;

        free(dados->nome);
        free(dados);

        fprintf(stdout, "O ultimo registro inserido foi deletado com sucesso.\\n");
        sleep(1);

        return novo;
}

/* Apena checa se a lista e NULL ou nao. */
int checa_vazio(Dados *dados) {

        if (dados == NULL) {
                fprintf(stdout, "Lista vazia!\\n");
                sleep(1);
                return 1;
        } else
                return 0;
}

/* Obtem os dados necessarios para chamar as funcoes de manuseio de dados. */
void insere(void) {

        char *nome;
        int idade;

        nome = (char *)malloc(BUFFER);

        fprintf(stdout, "\\n\\nDigite o Nome: \\n----> ");
        scanf("%s", nome);
        fprintf(stdout, "\\n");

        fprintf(stdout, "Digite a Idade: \\n----> ");
        scanf("%d", &idade);
        fprintf(stdout, "\\n");

        if (principal == NULL) 
                principal = inicia_dados(nome, idade);
        else 
                principal = insere_dados(principal, nome, idade);
}

void exibe(void) {

        if (!checa_vazio(principal))
                exibe_dados(principal);
}

void busca(void) {

        char *chave;

        if (!checa_vazio(principal)) {

                chave = (char *)malloc(BUFFER);

                fprintf(stdout, "Digite o nome para buscar: \\n--> ");
                scanf("%s", chave);

                busca_dados(principal, chave);
        }
}

void deleta(void) {

        if (!checa_vazio(principal))
                principal = deleta_dados(principal);
}

int main(void) {

        char escolha;

        do {
                system("/usr/bin/clear"); /* Nao lembro de nada melhor! 😛 */
                fprintf(stdout, "\\n\\t\\tCadastro de Pessoas\\n\\n");
                fprintf(stdout, "Escolha uma opcao: \\n");
                fprintf(stdout, "1 - Insere Dados\\n");
                fprintf(stdout, "2 - Exibe Dados\\n");
                fprintf(stdout, "3 - Busca Dados\\n");
                fprintf(stdout, "4 - Deleta Dados\\n");
                fprintf(stdout, "5 - Sair\\n\\n");

                scanf("%c", &escolha);

                switch(escolha) {
                        case '1':
                                insere();
                                break;

                        case '2':
                                exibe();
                                break;

                        case '3':
                                busca();
                                break;

                        case '4':
                                deleta();
                                break;

                        case '5':
                                exit(0);
                                break;

                        default:
                                fprintf(stderr,"Digite uma opcao valida!\\n");
                                sleep(1);
                                break;
                }

                getchar(); /* E para impedir sujeira na entrada da escolha. Nao lembro de nada melhor tambem. 😛 */
        }

        while (escolha > 0); /* Loop Principal. */

        return 0;
}

Veja o uso de uma única lista (principal) que no decorrer das chamadas das funções de manuseio de dados, ela vai se alterando com cada ponteiro retornado, tornando mais fácil seu uso.

Não tenho muito que falar porque fiz esse código correndo em umas 2 horas (mas que levou 2 dias por causa de conversas paralelas no MSN, Gmail, entre outros! =P), inclusive nem fiz mais tratamentos devidos mas enfim, a idéia é quem tiver dúvidas, que pergunte nos comentários! Eu AMO responder dúvidas (inteligentes) e acredito que sempre tem alguma visto que a linguagem C em si é um tanto quanto confusa pra maioria (principalmente pra mim!) mas que sempre podem ser sanadas!

Ainda quero postar exemplos melhores na prática, como por exemplo códigos a nível de kernel (LKM – Loadable Kernel Module) onde temos estruturas interessantes para criarmos block devices, mexer com file operations, hookar funções de rede entre outros! Há uma vasta diversidade para treinarmos mais e melhorar nossos códigos! Espero falar mais em breve. Por enquanto até a próxima! 😛

E quem quiser baixar o fonte direto está aqui: lista_simples_encadeada.c

Aguardo as dúvidas! 😀

Crie um website ou blog gratuito no WordPress.com.
Entries e comentários feeds.