Pesquisar

quinta-feira, 12 de novembro de 2020

Strings no Delphi

    Ao longo da vida do Delphi, em todas a idas e vindas, Borland, Code Gear e Embarcadero, o Delphi já utilizou alguns tipos de strings, vamos explorar brevemente esses tipos.

    Delphi 1 - String é um alias para ShortString (tamanho máximo de até 255);

    Delphi 2 até Delphi 2007 - String é um alias para AnsiString (tamanho máximo de até 2Gb);

    Delphi 2009 até hoje - String é um alias para UnicodeString (virtualmente sem tamanho, porem, por causa da codificação ocupa o dobro do tamanho);

Todas estas variantes de strings ainda estão disponíveis no Delphi 10.4 e podem ser utilizada.

Diferença básica entre os tipos:


ShortString

Nada mais é do que um ponteiro de caracteres, herdado do turbo pascal (Borland), o compilador pré aloca 256 caracteres para este tipo de dados, mesmo que seja utilizado menos que isso. Ou seja, sempre é ocupado 256 bytes;

É possível especificar o tamanho máximo para estas strings, porem esse valor não pode ultrapassar 255;

Ex:

  var

    lTemp1: string[50];  //Pré aloca 51 bytes, 50 para utilização e 1 byte de controle.

    lTemp2: string[256]; //Erro, o tamanho máximo é de 255 mais 1 de controle, então a declaração de uma shortstring de 256 bytes não é possível.

    lTemp3: shortstring; //Pré aloca 256 bytes, 255 para utilização e 1 de controle.

Toda a estrutura de dados do tipo ShortString é armazenado na memória stack, tornado a velocidade de acesso a esse tipo de estrutura extremamente rápido, porem com a atual velocidade dos processadores e acesso a memória, esta velocidade simplesmente não é percebido até mesmo por computadores mais antigos. No entanto, se vc for utilizar alguma estrutura de dados em que seja necessário repetir algumas milhares de vezes uma string, talvez valha a pena avaliar a utilização deste tipo;


AnsiString (Também conhecida como Long String)

    Na versão 2 do Delphi, a Borland introduziu a AnsiString, um tipo melhorado de ShortString, mas com algumas diferenças significativas.

    Agora era possível utilizar strings com o tamanho de até 2Gb de tamanho. Além disso, o Delphi agora era capaz de alocar uma quantidade de memória mais condizente com sua utilização. A alocação dinâmica era possível com este tipo de dado que utilizava um NULL TERMINATOR para identificar o final da string e não apenas um payload de tamanho no início da estrutura de dados.

    Foi introduzido tb um contador de referencia, para que strings iguais não se repetissem. Tornado assim o AnsiString um tipo de dado extremamente versátil de ser utilizado.

Ex:

  var

    lTemp1: AnsiString;

    lTemp2: AnsiString;

  begin 

    lTemp1:= 'Teste';

    lTemp2:= lTemp1; //Neste momento, o contador de referencia para a string é incrementado e a string não é copiáda, mas referenciada em lTemp2;

    //Caso o valor das string 'Teste' seja alterado em qualquer uma das variáveis, o compilador realiza uma cópia da mesma e o contador de referencia será decrementado, isso de forma totalmente transparente ao programador.

  end;

    Por muito tempo esta foi a utilização padrão do delphi, e muitas DLLs foram criadas com este padrão de string.

    O ponteiro de uma string local sempre aponta para a memória de acesso rápido (stack), mas o conteúdo da string, geralmente aponta para a memória mais lenta, a heap.


WideString

    Este tipo de dado é muito semelhante a AnsiString, porem foi criado para manter a compatibilidade com funções que era utilizada com Windows 64.

    Ele se comporta do mesmo modo que o AnsiString, no entanto os caracteres são unicode, ocupando o dobro do tamanho de espaço.


UnicodeString

    É o tipo padrão de string utilizado pelo Delphi desde a sua versão 2009. Para todos os efeitos funciona como a AnsiString, mas sem a limitação dos 2Gb e ainda é unicode. Geralmente utiliza a codificação UTF-16, mas pode ser configurada para utilizar a codificação UTF-8.

    A alteração do tipo string de AnsiString para UnicodeString ocasionou um rebuliço na comunidade, pois várias bibliotecas deixaram de manter a compatibilidade. O melhor exemplo disso é a carga de DLL que utilizava o padrão AnsiString (DLLs criadas em Delphi 7 em sua maioria) em versão mais novas do Delphi (2009 e superior). Claro que a solução era a tipagem correta na chamada dessas DLLs, mas como isso nunca havia ocorrido, muito poucos programadores tiveram essa visão.


Resumo

    AnsiString: string composta por caracteres ASCII. Cada Char possui exatamente 1 bytes. Um ponteiro para uma AnsiString (^AnsiString) equivale a char* em C;

    WideString: existe apenas por compatibilidade com o Windows. Cada Char possui 2 bytes, e deve ser utilizada em funções da Win32 com parâmetros LPWSTR, realizando um cast para PWChar;

    UnicodeString: string unicode. Por padrão UTF-16 (ou pelo menos era quando pesquisei pela última vez), mas pode assumir outras codificações, como UTF-8.

    ShortString: equivale a string antiga do Pascal, com sua limitação de de 255 caracteres.

    String: nas versões mais novas do Delphi (2009 em diante), equivale a UnicodeString. Antigamente equivalia a AnsiString.

    Tanto AnsiString quanto UnicodeString são mais do que um simples "array of Char", sendo que elas possuem informações de página de código e tamanho. Porém, para facilitar o cast destes tipos para PChar e suas variações, estas informações ficam nos endereços anteriores ao retornado pelo operador @.


Conversão

    A conversão entre elas é feita automaticamente. Único cuidado que deve ser tomado, é que dados podem ser perdidos durante a conversão devido ao tipo não suportar alguma característica da string de origem, gerando assim um "Warning" no compilador.

    O compilador ainda pode fazer um cast implícito para o tipo adequado, mas isso também gera um "Warning" no compilador.

    Por exemplo, converter UnicodeString para AnsiString, pode haver perda devido aos caracteres Unicode poderem ocupar mais que 1 byte.

    Conversão de AnsiString (ou UnicodeString) para ShortString, haverá perda de dados se a string de origem for maior que 255 (Length(origem) > 255).


Conclusão

    Não sou um especialista em strings de Delphi, e tentei explicar brevemente como o mecanismo de escrita de strings funciona. Por óbvio que existe muito mais coisa por traz de um tipo complexo como a strings, mas espero ter ajudado minimanete o leitor a resolver possíveis problemas de compatibilidade que podem aparecer na vida do developer.