Do que trata o artigo

Este artigo trata sobre Strings, como funcionam, do que são formadas, as mudanças que houve e como podemos fazer para migrar para o novo Delphi. Contaremos uma breve história sobre as mudanças ocorridas desde o Delphi 3 e daremos alguns exemplos.

Para que serve

Programas feitos em Delphi 2010 que usam bibliotecas/dll’s feitas em Delphi 7 não vão funcionar, devido a problemas relativos ao tamanho e organização dos dados. O mesmo se aplica a programas feitos em Delphi 7 utilizando bibliotecas compiladas em Delphi 2010.

Em que situação o tema é útil

O tema é útil tanto para migração de aplicações para a nova versão do Delphi como também para compatibilização com bibliotecas legadas. Também será útil como uma introdução ao Unicode e a primeira etapa para a globalização de sua aplicação.

Resumo do DevMan

Se ao usar uma dll legada com o novo Delphi você recebeu uma mensagem de Access Violation ou até viu algumas Strings suas corromperem-se com caracteres inválidos e ilegíveis isso aconteceu por causa do novo formato de Strings. Vamos explicar detalhadamente o que mudou, o que você pode fazer e até onde você pode chegar com as novas características do Delphi. Além disso, seremos introduzidos ao Unicode e suas possibilidades. Criaremos exemplos com dll’s que imitam situações da vida real e solucionaremos alguns dos problemas mais comuns.

Strings são um dos maiores avanços na programação. Elas abstraem sequências de caracteres que formam palavras e frases, e nos permitem trabalhar com essas sequências de forma transparente, sem a necessidade de trabalhar com vetores de caracteres.

Infelizmente não há um padrão na implementação de Strings entre as várias linguagens e frameworks. Enquanto no C++ as strings são ponteiros para vetores de caracteres com tamanho variável e terminadas em null, as Strings no Delphi são ponteiros para estruturas contendo informações sobre comprimento, contagem de referências e o vetor de caracteres propriamente dito.

Já os caracteres sempre foram caracteres, e sua implementação sempre foi padrão, certo? Errado. Ao longo dos anos a definição de caracteres também sofreu mudanças. Algumas linguagens sofreram a mudança cedo, outras sofreram essa mudança mais tarde. Algumas linguagens já nasceram com o conceito de caractere Unicode. O Delphi vem preparando o terreno para essa mudança desde a versão 3.

Os caracteres que conhecemos e estamos acostumados são os caracteres ASCII, caracteres formados por apenas 1 byte que podem assumir 256 valores diferentes. Na tabela ASCII encontramos, do 0 ao 127 (caracteres formados por apenas 7 bits) os caracteres de controle (enter, tab, backspace entre outros), as letras maiúsculas, minúsculas, números e símbolos. Dos valores 128 a 255 podemos encontrar a parte “estendida” do ASCII, que abrange cedilha e vogais acentuadas, mas que pode variar de acordo com a linguagem e o país. Esses são os caracteres de um byte que usávamos no Turbo Pascal e que continuamos usando até antes do Delphi 2009. Nossas Strings, Chars e PChars eram baseados nesses caracteres.

Desde o Delphi 3 existiam as WideStrings, formadas por WideChars. Os WideChars eram caracteres formados por dois bytes cada um, que poderiam assumir 65536 valores, podendo conter uma gama maior de caracteres, incluindo caracteres de língua estrangeira.

O Delphi 2005 já permitia que o código fonte fosse salvo em utf-8, e o Delphi 2007 já possuía drivers para dbExpress que funcionavam com Unicode. Além disso, desde o Delphi 6 era possível usar Unicode graças aos componentes TNT, um conjunto de controles como os da VCL que, usando WideStrings, permitia a entrada de caracteres de várias linguagens.

O que é Unicode

Unicode é um consórcio ou acordo mundial cujo objetivo é criar padrões para suportar múltiplos caracteres e linguagens nos computadores e softwares.

As 128 posições “altas” do ASCII simplesmente não funcionariam, porque há linguagens que necessitam de mais caracteres do que os 128 restantes, além de símbolos de pontuação e outros símbolos usados em matemática e filosofia. Além disso, o número de padrões diferentes para a parte “alta” do ASCII criou sim uma despadronização.

Existem vários “sabores” de implementação de Unicode, são os chamados Unicode Transformation Format (Formato de Transformação Unicode) ou UTF. Os padrões de UTF são: UTF-7, UTF-8, UTF-16, UTF-32. Os padrões UTF-16 e UTF-32 podem ainda ser Big Endian ou Litle Endian, referindo-se à orientação que um caractere composto por vários bytes deve ser lido: a partir da direita ou a partir da esquerda.

O UTF-7 faz parte de um conjunto de formatos obsoletos ou que tiveram de ser criados para necessidades específicas, mas não fazem parte do padrão.

O UTF-32 é o que desperdiça maior espaço, visto que todos os caracteres são formados por 4 bytes, de largura fixa, mesmo os “baixos” #0 até #127 compatíveis com ASCII. Apesar disso, o UTF-32 é o que apresenta a maior facilidade de conter todos os tipos de caracteres, de qualquer linguagem do mundo com nenhum processamento para isso. O UTF-32 é muito usado no mundo UNIX.

Já no padrão UTF-16 todos os caracteres têm 16 bits, ou 2 bytes. Isso nos daria a possibilidade de trabalharmos com até 65536 caracteres, o que representa apenas uma fração dos caracteres existentes no mundo inteiro. Felizmente o UTF-16 é expansível: ele permite caracteres de tamanho variável (podendo ter ou dois ou quatro bytes) com o uso de caracteres compostos e Surrogate Pairs, como veremos mais a frente. Uma desvantagem é que caracteres do #0 ao #255, de apenas um byte, serão representados por dois bytes: um idêntico ao que era na tabela ASCII e o outro será um #0. O UTF-16 é o mais usado nas APIs dos sistemas operacionais, máquinas virtuais JAVA, .Net Framework e por isso foi adotado para o Delphi.

O UTF-8 é o mais versátil, compacto e o mais usado na WEB. No UTF-8 um caractere pode ter 1, 2 ou até 4 bytes, dependendo de alguns valores “de corte” dos bytes menos significativos (mais a esquerda). Por exemplo, um caractere de #0 até #127 é representado por 1 byte, do #128 até o #2047 passa a ser representado por 2 bytes, do #2048 até o #65535 passa a ser representado por 3 bytes e do #65536 em diante passa a ser representado por 4 bytes. O UTF-8 não apresenta os problemas de desperdício de espaço com caracteres nulos que o UTF-16 e o UTF-32 apresentam.

No Delphi 2010 trabalharemos com os caracteres no formato UTF-16. Isso porque tanto o Delphi como a API do Windows podem trabalhar com WideStrings. O mapeamento de caracteres de 16 bits para WideStrings é, além de mais fácil, natural e transparente. O UTF-16 desperdiça menos espaço que o UTF-32, também pode ser expansível e o Delphi possui o tipo de String UTF8String e o Encoding UTF-8 caso seja necessário salvar arquivos nesse formato.

Por ser mais usado, o UTF-16 geralmente é chamado simplesmente de Unicode, inclusive no próprio Delphi e no .Net Framework, mas como vimos, Unicode vai muito além disso.

O que mudou

Com as mudanças uma coisa que não existe mais é o conceito de caractere. Pelo menos não como o conhecemos. Isso porque em um ambiente Unicode os conceitos que trabalhamos são o de Code Unit e Code Point, ou “unidade de código” e “ponto de código” em uma tradução livre e literal.

Além disso, temos os chamados Surrogate Pairs. Um Surrogate é um tipo de caractere, utilizado em algumas linguagens, que altera ou substitui um grupo de dois caracteres. Um Surrogate Pair com certeza tem mais de 2 bytes. Além disso, devido à diversidade de linguagens e regras linguísticas agora suportadas, também existe o conceito de caractere composto. Este pode ter o tamanho de dois caracteres, ou seja, 4 bytes, mas ser exibido como um caractere só. São dois caracteres visualizados como um.

Neste artigo precisaremos de alguma versão do Delphi anterior à 2009, por exemplo, o popular Delphi 7, e precisaremos também de uma versão mais nova, como o Delphi 2010. Criaremos, em ambas as versões, um programa Win32 simples, que contém um Form, um Memo ocupando quase todo o tamanho do Form e um espaço para colocarmos botões. Nos botões faremos testes e imprimiremos mensagens no Memo. Depois de feitos alguns testes, vamos criar no Delphi 7 uma dll com métodos cujos argumentos são PChars, a serem lidos/escritos dentro da dll, e tentaremos usar essa dll no Delphi 2010. Nosso programa de teste, a ser feito tanto no Delphi 7 como no 2010 será como mostra a ...

Quer ler esse conteúdo completo? Tenha acesso completo