Let us suppose we want to revert the following String "áe".
The unicode for that is "\u0061\u0301\u0065".
The naive aproach of reverting it would be char by char
private static String reverseStringNaive(String s) {
char[] characters = new char[s.length()];
for (int i = s.length() - 1; i >= 0; i--) {
int j = s.length() - i - 1;
characters[j] = s.charAt(i);
}
return new String(characters);
}
which gives us "éa"(\u0065\u0301\u0061) when we hope to get "eá" (\u0065\u0061\u0301). The accute accent "´" should stick together with the "a", not change to the "e".
The following code gives me the expected result for that String:
开发者_运维百科private static String reverseString(String s) {
char[] characters = new char[s.length()];
for (int i = s.length() - 1; i >= 0; i--) {
int j = s.length() - i - 1;
if (Character.isLetterOrDigit(s.charAt(i)) || Character.isISOControl(s.charAt(i))) {
characters[j] = s.charAt(i);
} else {
characters[j] = s.charAt(i-1);
characters[j+1] = s.charAt(i);
i--;
}
}
return new String(characters);
}
I'm checking if each character is Letter, Digit or ISO Control. If not, I'm assuming it should stick together with the previous character.
The question is, are there other things I should check or worry about? Is my aproach still naive?
Your issue could also be resolved by converting the string into the canonical decomposition form NFC. Basically, the java.text.Normalizer class can be used to combine accents and other combining characters with their base characters so you will be able to reverse properly.
All these other ideas (String.reverse(), StringBuffer.reverse()) will correctly reverse the characters in your buffer, but if you start with decomposed characters, you might not get what you expect :).
In some "decomposition forms", accent characters are stored separate from their base forms (as separate characters), but in "combined" form they are not. So in one form "áe" is stored as three characters, and in the other, combined form, as two.
However, such normalization isn't sufficient for handling other kinds of character combination, nor can it account for characters in the Unicode astral planes, which are stored as two characters (or more?) in Java.
Thanks to tchrist for pointing out the ICU support for text segmentation including extended grapheme clusters such as the one identified in the comments below (see virama). This resource seems to be the authoritative source of information on this kind of stuff.
精彩评论