Skip to content

Latest commit

 

History

History
361 lines (270 loc) · 12 KB

Representacao_de_Strings.md

File metadata and controls

361 lines (270 loc) · 12 KB

Representação de Strings

Embora uma string seja definida como uma sequência de caracteres de um dado alfabeto, cada linguagem de programação implementa strings de uma forma particular, com determinadas vantagens e desvantagens. Em geral, as bibliotecas padrão das linguagens oferecem várias funções para manipulação de strings.

C

Em C, uma string é implementada como um array de caracteres terminado em zero (\0). Esta implementação é a mais sintética o possível em termos de memória: é reservado espaço apenas para armazenar os caracteres da string mais o terminador \0. Em contrapartida, a ausência deste marcador pode levar a execução errônea de várias das funções de strings, além de que rotinas simples, como determinar o tamanho da string, tem complexidade O(n), contrastando com as implementações que utilizam memória adicional e que podem retornar o tamanho em O(1).

Abaixo segue um exemplo de declaração e inicialização de um string em C.

char s1[101];
char s2[] = "Test";

A strings s1, não inicializada, comporta até 100 caracteres (mais o terminador); já a string s2, inicializada com o valor "Test", não exige que seja informado o número de caracteres (o compilador completa esta informação automaticamente).

Importante notar que, devido a aritmética de ponteiros da linguagem, as strings em C tem como primeiro elemento indexado em zero, não em um. Assim, s[2] representa o terceiro, e não o segundo, elemento da string.

A biblioteca padrão do C oferece o cabeçalho string.h, onde são declaradas várias funções para a manipulação de strings, e o cabeçalho stdlib.h, que traz funções para conversão de strings para valores numéricos. Algumas destas funções são ilustradas no código abaixo.

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

int main()
{
    char a[50] = "Test";
    char b[] = "TEP";
    char *p;

    double d;
    int i;
    long long ll;

    // Tamanho da string
    int s = strlen(a);      // s = 4, zero terminador não é computado
    s = strlen(b);          // s = 3

    // Comparação
    s = strcmp(a, b);       // s > 0, "Test" sucede "TEP" na ordem lexicográfica
    s = strcmp(b, a);       // s < 0, "TEP" precede "Test" na ordem lexicográfica

    s = strncmp(a, b, 1);   // s = 0, as strings são iguais no primeiro caractere
    s = strncmp(a, b, 2);   // s > 0, "Te" sucede "TE" na ordem lexicográfica

    // Cópia
    strcpy(a, b);           // a = "TEP"
    strncpy(a, "SA", 2);    // a = "SAP"

    // Acesso aos elementos individuais
    a[2] = 'T';             // a = "SAT"
    a[0] = b[2];            // a = "PAT"

    // Conversão de string para valores numéricos
    strcpy(a, "123.45");
    d = atof(a);            // d = 123.45
    i = atoi(a);            // i = 123

    strcpy(a, "10000000000000000000000000000000000000000");
    ll = strtoll(a, NULL, 2);   // ll = 1099511627776, conversão de base binária

    // Concatenação
    strcpy(b, "xyz");       // b = "xyz"
    strcpy(a, "abcba");     // a = "abcba"

    strcat(a, b);           // a = "abcbaxyz"
    strncat(a, a, 3);       // a = "abcbaxyzabc"

    // Busca de caracteres
    p = strchr(a, 'b');     // p - a = 1, índice da primeira ocorrência de 'b'
    p = strrchr(a, 'b');    // p - a = 9, índice da última ocorrência de 'b'
    p = strstr(a, "cba");   // p - a = 2, índice da primeira coorrência de "cba"
    p = strstr(a, "dd");    // p = NULL, "dd" não é substring de a

    i = strspn(a, "abc");   // i = 5, a[0..4] contém apenas caracteres em "abc"
    i = strcspn(a, "z");    // i = 7, a[0..6] contém caracteres diferentes de "z"

    return 0;
}

Interessante notar que, neste mesmo cabeçalho, são definidas três funções que permitem manipular a memória (memcmp(), memset(), memcpy()), através de comparações, atribuições e cópia, respectivamente.

Outro cabeçalho útil para a manipulação de strings em C é o ctype.h, onde são definidas funções para a manipulação de caracteres. Algumas destas funções são ilustradas abaixo.

#include <stdio.h>
#include <ctype.h>

int main()
{
    char a[] = "Test with numbers: 0x1234";
    int r;

    a[0] = tolower(a[0]);       // a = "test with numbers: 0x1234"
    a[10] = toupper(a[10]);     // a = "test with Numbers: 0x1234"

    r = isalpha(a[0]);          // r > 0, 't' é alfabético
    r = isalpha(a[19]);         // r = 0, '0' não é alfabético
    r = isalnum(a[19]);         // r > 0, '0' não é alfanumérico
    r = isblank(a[18]);         // r = 0, ' ' é um espaço em branco
    r = isdigit(a[1]);          // r = 0, 'e' não é um dígito decimal
    r = isxdigit(a[1]);         // r > 0, 'e' é um dígito hexadecimal

    return 0;
}

C++

Embora seja possível utilizar a abordagem e as bibliotecas citadas anteriormente para a linguagem C em C++, existe uma representação em C++ de strings baseada em classes. O uso de classes para representar strings traz a vantagem de poder manter outras informações sobre a string sempre atualizadas e com acesso em O(1); por outro lado, esta representação demanda mais memória do que a representação em C. Existem técnicas para tentar reduzir a memória utilizada, como a Small String Optimization.

A classe fundamental dentre as várias classes que representam strings em C++ é a std::string. O seu comportamento e seus principais métodos são ilustrados no código abaixo.

#include <iostream>

using namespace std;

int main()
{
    string a = "Test";
    string b = "TEP";

    // Tamanho da string
    auto s = a.size();      // s = 4, zero terminador não é computado
    s = b.size();           // s = 3

    // Comparação
    auto r = (a == b);      // r = false, "Test" e "TEP" são distintas
    r = (a != b);           // r = true, "Test" e "TEP" são distintas
    r = (a < b);            // r = false, "Test" sucede "TE" na ordem lexicográfica
    r = (a > b);            // r = true, "Test" sucede "TE" na ordem lexicográfica

    s = strncmp(a, b, 1);   // s = 0, as strings são iguais no primeiro caractere
    s = strncmp(a, b, 2);   // s > 0, "Te" sucede "TE" na ordem lexicográfica

    // Cópia
    a = b;                  // a = "TEP"

    // Acesso aos elementos individuais
    a[1] = 'A';             // a = "TAP"
    auto c = a.front();     // c = 'T', primeiro elemento
    c = a.back();           // c = 'P', último elemento

    // Conversão de string para valores numéricos
    a = "123.45";
    auto d = stod(a);       // d = 123.45;
    auto i = stoi(a);       // i = 123

    a = "10000000000000000000000000000000000000000";
    auto ll = stoll(a, NULL, 2);   // ll = 1099511627776, conversão de base binária

    // Conversão de valores numéricos para strings
    a = to_string(999);     // a = "999"
    a = to_string(9.99);    // a = "9.99"

    // Concatenação
    b = "xyz";              // b = "xyz"
    a = "abcba";            // a = "abcba"

    a +=  b;                // a = "abcbaxyz"
    a += a.substr(0, 3);    // a = "abcbaxyzabc"

    // Busca de caracteres
    auto p = a.find('b');   // p = 1, índice da primeira ocorrência de 'b'
    p = a.rfind('b');       // p = 9, índice da última ocorrência de 'b'
    p = a.find("cba");      // p = 2, índice da primeira coorrência de "cba"
    p = a.find("dd");       // p = string::npos, "dd" não é substring de a

    p = a.find_first_not_of("abc");     // i = 5, a[0..4] contém apenas caracteres em "abc"
    p = a.find_first_of("z");           // i = 7, a[0..6] contém caracteres diferentes de "z"

    return 0;
}

O método c_str() permite obter, a partir de uma string C++, uma representação compatível com a utilizada em C. Deste modo, é possível utilizar funções escritas para strings em C a partir de instâncias C++, como no exemplo abaixo.

#include <iostream>

using namespace std;

int main()
{
    string a = "Test";

    printf("%s\n", a.c_str());

    return 0;
}

Python

Embora as strings em Python também sejam implementadas através de classes, elas podem ser vistas informalmente como listas de caracteres. Para maratonas de programação, o módulo string do python traz constantes bastantes úteis, que trazem listagens de caracteres comuns. O trecho abaixo ilustra algumas delas.

import string

a = string.lowercase        // a = 'abcdefghijklmnopqrstuvwxyz'
b = string.uppercase        // b = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
c = string.letters          // c = a + b
d = string.digits           // d = '0123456789'
x = string.hexdigits        // x = '0123456789abcdefABCDEF'

Mesmo que a solução proposta pela equipe seja escrita em outra linguagem, estas constantes podem ser facilmente acessadas via terminal, importando o módulo e usando o comando (ou função, no Python 3) print.

Outra particularidade do Python é que, ao contrário das linguagens C e C++, ele suporta índices negativos para strings. Por exemplo, s[-1] se refere ao último caractere, s[-2] ao penúltimo, e assim por diante. Outra notação útil é s[::-1], que indica o reverso da string s (isto é, s lida do fim para o começo).

A API para strings em Python contempla ainda muitas outras funções úteis, como strip(), join() e split(), cuja exemplificação foge ao escopo deste texto.

I/O

Cada linguagem tem mecanismos apropriados para a leitura e escritas de strings a partir do terminal. Em C, são utilizadas as funções printf() e scanf():

#include <stdio.h>

int main()
{
    char s[1024];

    scanf("%s", s);

    printf("s = [%s]\n", s);

    return 0;
}

A função scanf() fará a leitura da entrada até encontrar um caractere de espaço (quebra de linha, tabulações, espaço em branco, etc). Se a intenção é ler uma linha na íntegra, deve ser utilizada a função fgets().

#include <stdio.h>

int main()
{
    char line[1024];

    fgets(line, 1024, stdin);

    printf("s = [%s]\n", s);

    return 0;
}

A função fgets() é mais segura que a scanf(), pois utiliza o segundo parâmetro como limite máximo de caracteres (mais o zero terminador) a serem lidos e escritos no primeiro parâmetro. Vale notar que a função fgets() insere, no primeiro parâmetro, o caractere de nova linha, se o encontrar (a função também termina se for encontrado o caractere EOF, que indica o fim do arquivo).

Em C++, strings podem ser lidas e escritas com os operadores << e >> das classes cin e cout, respectivamente:

#include <iostream>

using namespace std;

int main()
{
    string s;

    cin >> s;
    cout << s;

    return 0;
}

A classe cin se comporta de forma semelhante à função scanf(), lendo a entrada até encontrar um caractere que indique um espaço em branco. Para ler linhas inteiras, de forma semelhante à fgets(), basta usar a função getline():

#include <iostream>

using namespace std;

int main()
{
    string line;

    getline(cin, line);

    cout << line;

    return 0;
}

Porém, diferentemente da função fgets(), a função getline() despreza o caractere de fim de linha, e não o insere na string apontada pelo segundo parâmetro.

Referências

AHLGREN, John. Small String Optimization and Move Operations. Acesso em 16/12/2016.

CPPREFERENCE. Null-terminated byte strings. Acesso em 21/12/2016.

CPPREFERENCE. std::basic_string. Acesso em 21/12/2016.

CROCHEMORE, Maxime; RYTTER, Wojciech. Jewels of Stringology: Text Algorithms, WSPC, 2002.

DAVID. A look at std::string implementations in C++. Acesso em 22/12/2016.

HALIM, Steve; HALIM, Felix. Competitive Programming 3, Lulu, 2013.

PYTHON DOCUMENTATION. Common string operations. Acesso em 26/12/16.