O tempo passa, as linguagens evoluem. Saímos das linguagens baseadas em caracteres Ascii e agora entramos na era Unicode. Entretanto, algumas pessoas ainda tratam strings como os velhos caracteres de 7 bits do passado.
No mundo Unicode, o quão longa é uma String?
Você pode estar tentando a dizer: Fácil, simplesmente use o método length() para isso. Geralmente, a resposta estará correta, mas esse não é o único meio, e também nem sempre é o meio correto. Na verdade, existem três maneiras de se obter o tamanho de uma String:
Você pode obter o número de unidades de char;
Você pode obter o número de caracteres ou code points;
Você pode obter o número de bytes.
Contando o unidades de char
O java usa o padrão Unicode para definir seus caracteres. A definição inicial do unicode era de caracteres de tamanho fixo, com 16 bits, entre U+0000 até U+FFFF - o símbolo U+ significa um caracter válido Unicode, representado em seguida pro seu código hexadecimal. Convenientemente e não por acaso, o java adotou uma largura fixa de dois bytes para o tipo char. Assim, um char representa qualquer caracter de 16 bits.
A maior parte dos programadores é familiar com o metodo length. O código a seguir conta o número de caracteres numa string de exemplo. Note que essa String possui uma série de caracteres simples e alguns definidos com a notação \u. A notação \u é a equivalente em Java ao padrão unicode U+, e é usada para definir um caracter unicode de 16 bits através de sua notação hexadecimal.
private String testString = "abcdu5B66uD800uDF30";
int charCount = testString.length();
System.out.printf("caracteres: %dn", charCount);
O método Length conta o número de caracteres na String.
O código de exemplo imprime: caracteres: 7
Contando unidades de caracteres (ou code points)
Entretanto, anos mais tarde o padrão Unicode 4.0 definiu um número significativamente maior do que U+FFFF. Isso fez com que o Java passasse a ser incapaz de de representar todos os caracteres. A solução para esse problema surgiu a partir do Java 5, quando pares de chars de 16 bits passaram a ser utilizados para representar um caracter unicode. Esses pares passaram a ser conhecidos como pares substitutos (surrogate pairs).
Apesar de um char ainda poder representar um caracter unicode até U+FFFF, apenas um par pode representar caracteres suplementares. Os valores suplementares possuem o valor mais alto do par no intervalo entre U+DC00 até U+DFFF. O padrão também define um algoritmo para mapear entre um par substituto e um caracter acima de U+FFFF. Com esses pares é possível representar qualquer caracter no padrão Unicode. Mas isso também significa que um caracter de bits pode ser uma unidade de código que não representa um caracter completo unicode (code point).
O método length() não conta os caracteres suplementares, pois ele apenas conta unidades char, sem se preocupar com que eles significam. Os programadores do Java 5 forneceram então um novo método, chamado codePointCount(int beginIndex, int endIndex) capaz de lidar com essa tarefa. Esse método informa quantos caracteres Unicode estão entre os dois indices. Os índices referem-se a unidades de char, como no método length().
Dessa forma, se você subtrair endIndex - begin index para a totalidade de uma String, obterá o mesmo valor que o método length(). Entretanto, esse valor poderá ser completamente diferente do valor de retorno do método codePointCount. Se o seu texto contiver pares substitutos, o tamanho retornado por length() e pelo método codePointCount certamente será diferente. Um surrogate pair define um único code point, mas pode ser definido por uma ou duas unidades de caracteres.
Vejamos um exemplo:
private String testString = "abcdu5B66uD800uDF30";
int charCount = testString.length();
int characterCount = testString.codePointCount(0, charCount);
System.out.printf("Número de caracteres: %dn", characterCount);
Esse exemplo imprime:
Número de caracteres: 6
A variável testString contém dois caracteres interessantes, que são os ideogramas japoneses significando ?aprendizado? e um caracter chamado GOTHIC LETTER AHSA. A letra japonesa tem o valor unicode U+5B66, que pode ser representada por apenas um caracter unicode. Entretanto, a letra gótica tem o valor representado pelo par \uD800\uDF30. Como esse par representa apenas um caracter unicode, o valor total de caracteres na String é 6, e não 7.
Contando o número de Bytes
Quantos bytes tem uma String? A resposta varia de acordo com o charset utilizado. A razão para que isso seja perguntado, geralmente é satisfazer o tamanho de um campo num banco de dados. O método getBytes() converte os caracteres Unicode em uma representação orientada a bytes, retornando um byte[]. Uma codificação orientada a bytes é a UTF8, que é diferente de todas as representações unicode pois pode representar de maneira adequada qualquer code point Unicode.
O código abaixo converte o texto num array de bytes:
byte[] utf8 = null;
int byteCount = 0;
try {
utf8 = str.getBytes("UTF-8");
byteCount = utf8.length;
} catch (UnsupportedEncodingException ex) {
ex.printStackTrace();
}
System.out.printf(“Número de bytes em UTF-8: %dn”, byteCount);O charset destino ( UTF8 ) é quem define quantos bytes serão gerados. O UTF8 transforma um code point unicode em uma até quadro unidades de código de 8 bits (um byte). Assim, os caracteres a, b, c e d juntos requerem apenas um total de quatro bytes. O caracter japonês passa a representar 3 bytes. Finalmente a letra Gótica representa quatro bytes. O resultado total exibido é esse:
Número de bytes em UTF8 Byte: 11
Em resumo
A menos que você utilize caracteres suplementares, você nunca verá diferença entre os valores de retorno de length e codePointCount. Entretanto, caso você queira enviar seus produtos para a crescente China ou para o Japão, você certamente se deparará com esse tipo de caracter, e ficará muito satisfeito por saber que esse método existe.
Transmissões de rede, ou gravações em bancos de dados, recomendam o formato UTF8. Nesse caso, mais uma vez o tamanho da String irá variar enormemente.
Vários métodos para avaliar o tamanho foram apresentados, agora, cabe a você analisar qual é a opção correta para medir sua String.