Fator e Terminologia
Como você provavelmente sabe, o Java usa o UTF-16 para representar o String
. O tipo de dados char
e a classe Character
são baseados na especificação Unicode original, que define caracteres como entidades de 16 bits de largura fixa. O padrão Unicode foi alterado desde então para permitir caracteres cuja representação requeira mais de 16 bits. Portanto, na representação UTF-16, há caracteres (Code Points) representados por 1 (um) e alguns outros caracteres representados por 2 (dois) valores char (Code Points).
Exemplo
Caracter: A
“Representação UTF-16” em Java: "\u0041"
Caracter: A Duplo na matematica, letra utilizada na matemática.
“Representação UTF-16” em Java: "\uD835\uDD38"
O primeiro é direto; o segundo é um pouco mais interessante; esse caracter único (Code Point) é representado por dois escapes Unicode. Isso significa algumas coisas:
- Este caracter único é representado por dois
char
(ouCharacter
) valores (unidades de código). - O
lenght()
destaString
é dois. - O método
toCharArray()
retorna uma matriz char (char[]
), que possui dois elementos (0xD835
e0xDD38
respectivamente). - Tanto
charAt(0)
quantocharAt(1)
retornam algo (e nãoStringIndexOutOfBoundsException
), mas esses valores não são caracteres válidos. - Se você faz alguma manipulação de caracteres, você precisa considerar este caso e lidar com esses caracteres, que consistem em dois
char
(substitutos). - Portanto, a maior parte do código de manipulação de caracteres que já escrevemos está provavelmente quebrada.
Isso basicamente significa que você provavelmente não quer fazer nenhuma manipulação de caracteres.
A quebra ao inverter String
Neste ponto, você pode ter uma boa idéia do que está errado com esta solução (muito comumente utilizada) para inverter uma String:
static String reverse(String original) {
String reversed = "";
for (int i = original.length() - 1; 0 <= i; i--) {
reversed += original.charAt(i);
}
return reversed;
}
Vamos ver isto em ação:
String str = "\uD835\uDD38BC"; // Three characters: A, B, C (4 chars)
System.out.println(str); // prints ABC (A is the double-struck A)
System.out.println(reverse(str)); // prints CB??
Se você executar o método acima, ele produzirá uma String
como esta: "CB\uDD38\uD835"
. C
e B
estão corretos, mas \uDD38\uD835
é inválido, é por isto que você vÊ ??
quando você imprimi-lo. O método não deveria tê-los invertido; o resultado válido seria "CB\uD835\uDD38"
(CBA
(A Duplo na matematica)).
Solução
Normalmente, não escrever código para resolver problemas é uma boa ideia:
static String reverse(String original) {
return new StringBuilder(original).reverse().toString();
}
Se você quiser dar um pequeno passo adiante, aqui está o exemplo utilizando Java 8+:
Function<String, String> reverse = s -> new StringBuilder(s).reverse().toString();
Se você está curioso para saber como isso é implementado, veja o que o método reverse() do StringBuild faz (está na classe AbstractStringBuilder
).
Então, quantas manipulações de String
incorretas você já viu? Deixe-nos saber nos comentários abaixo!