[英]Reverse a utf-8 string in Java
我試圖編寫一個Java代碼來反轉Java中的utf-8字符串。 采訪中有人問我。 但是我想知道是否將字節轉換為位並從位中獲取代碼點,那么我們該如何做代碼。 大概那是面試官想要的。
class Ideone
{
public static void main(String[] args) {
String s ="Ž®aͻ";char[] ch = new char[s.length()];
StringBuilder sb = new StringBuilder(s);
StringBuilder rev = new StringBuilder();
for (int i=0; i< s.length(); i++) {
int x = sb.codePointAt(i);
char[] y = Character.toChars(x);
rev.append(y);
}
System.out.println(rev.reverse());
}
}
首先,所有Java字符串都是UTF-16編碼的,而不是UTF-8編碼的。 這對於諸如反轉字符串之類的任務非常重要,因為字符將占用的字節數取決於編碼。 在UTF-8中,字節數是可變的,而在UTF-16中,字節數始終是兩個字節。 一個char
是16位數據,即使它只是表示ASCII。 UTF-8可以8位編碼ASCII,但是可能需要更多的時間來表示其他字符。
因為一個char
是16位,所以大多數字符(包括您示例中的Ž®aͻ
)都可以很好地適合單個char
,並且沒有問題。 但是,某些字符(尤其是Emoji屬於此類)不能用單個char
表示,現在我們正在處理代理對 。 在處理可能具有代理對的文本時,您必須非常小心地進行字符串操作,因為大多數Java API(尤其是String
上的幾乎每個方法)都無法正確處理它們。
舉一個更好的例子,考慮字符串"👶👧👩👵💀🤖"
。 六個字符吧? 不符合Java!
String s ="👶👧👩👵💀🤖";
System.out.println("String: " + s);
System.out.println("Length: " + s.length());
System.out.println("Chars: " + Arrays.toString(s.toCharArray()));
System.out.println("Split: " + Arrays.asList(s.split("")));
打印:
String: 👶👧👩👵💀🤖
Length: 12
Chars: [?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?]
Split: [?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?]
現在,某些API 可以正確處理代理對,例如StringBuilder.reverse()
:
如果序列中包含任何代理對,則將它們視為反向操作的單個字符。 因此,從高到低的替代物的順序永遠不會顛倒。
假設為了面試的緣故,您不能使用此方法(或者,可以理解的是,您無法當場回憶它是否安全),則可以使用String.codePoints()
)遍歷String的代碼點 。 String.codePoints()
。 這使您可以安全地撤消內容:
List<String> chars = s.codePoints()
.mapToObj(i -> String.valueOf(Character.toChars(i)))
.collect(Collectors.toList());
Collections.reverse(chars);
System.out.println(chars.stream().collect(Collectors.joining()));
印刷品:
🤖💀👵👩👧👶
如dimo414所述 , StringBuilder.reverse()
正確處理代理對:
如果序列中包含任何代理對,則將它們視為反向操作的單個字符。 因此,從高到低的替代物的順序永遠不會顛倒。
這意味着Piyush的回答是好的,除了它使用StringBuffer
,您不應該使用它。
如果您堅持要自己反轉一個String
(即UTF-16,UTF-8),則可以像下面的代碼那樣進行操作,該代碼向后迭代字符並處理代理(如果您不在意, if
刪除if
語句)關於代理人):
private static String reverse(String input) {
StringBuilder buf = new StringBuilder();
for (int i = input.length() - 1; i >= 0; i--) {
char c = input.charAt(i);
if (i > 0 && Character.isSurrogate(c)) {
char c2 = input.charAt(i - 1);
if (Character.isSurrogate(c2)) {
buf.append(c2);
i--;
}
}
buf.append(c);
}
return buf.toString();
}
但是,您的問題是說“ utf-8字符串” ,而UTF-8是字符串的字節編碼,因此,如果要這樣做,您首先需要獲取UTF-8字節,然后反轉這些字節,最后轉換為回到一個String
:
private static String reverse(String input) {
byte[] utf8bytes = input.getBytes(StandardCharsets.UTF_8);
utf8bytes = reverseUtf8(utf8bytes);
return new String(utf8bytes, StandardCharsets.UTF_8);
}
要反轉UTF-8 ,您需要向后處理,而要做到這一點,您需要了解編碼的工作方式。
0
到127
范圍內的Unicode字符被編碼為一個字節( 0xxxxxxx
)。 所有其他Unicode字符都被編碼為一個字節塊,從11xxxxxx
字節開始,其余為10xxxxxx
字節,因此我們可以檢測到此類字節塊並將其保留。
private static byte[] reverseUtf8(byte[] input) {
byte[] reversed = new byte[input.length];
for (int i = input.length - 1, j = 0; i >= 0; i--) {
byte b = input[i];
if ((b & 0x80) == 0) {
reversed[j++] = b;
} else {
int k = i;
while (k > 0 && (input[k] & 0xC0) == 0x80)
k--;
System.arraycopy(input, k, reversed, j, i - k + 1);
j += i - k + 1;
i = k;
}
}
return reversed;
}
首先,您可以將“ utf-8字符串”的含義轉換為“ utf-16字符串”( java.lang.String
),也許是從utf-8代碼單元的字節數組
CharsetDecoder decoder = StandardCharsets.UTF_8.newDecoder();
decoder.onUnmappableCharacter(CodingErrorAction.REPORT);
decoder.onMalformedInput(CodingErrorAction.REPORT);
ByteBuffer buffer = ByteBuffer.wrap(bytes);
String s = decoder.decode(buffer).toString();
System.out.println(s);
因此,您將獲得一系列UTF-16代碼單元,其中一個或兩個對一個Unicode代碼點進行編碼,一個基本代碼點以及零個或多個“組合”代碼點形成一個字素簇,這在大多數人中會稱為字符他們看到了。 大概您想顛倒字素簇的順序。 幸運的是,Java提供了各種文本中斷迭代器,包括一個用於語言環境中的字形中斷的迭代器。
Locale locale = Locale.ENGLISH;
StringBuilder reversed = new StringBuilder();
BreakIterator boundary = BreakIterator.getCharacterInstance(locale);
boundary.setText(s);
int end = boundary.last();
for (int start = boundary.previous();
start != BreakIterator.DONE;
end = start, start = boundary.previous()) {
reversed.append(s.substring(start,end));
}
System.out.println(reversed.toString());
有一個資格考試的故事,第一頁說在開始之前要閱讀整個考試,最后一頁說要寫你的名字並上交而不寫其他任何東西。 那么,正確的答案是否正確?在Java中沒有“ utf-8 字符串 ”之類的東西嗎? 如果不是,則必須詢問“ utf-8字符串”的含義。
而且,仍然存在關於如何逆轉連字之類的問題。 應該將“ fl”反轉為“ lf”嗎?
稍后您還可以問,在反轉弦重要的情況下,該公司從事哪種類型的項目?
這應該工作。
String string = "Ž®aͻ";
String reverse = new StringBuilder(string).reverse().toString();
System.out.println(reverse);
不知道這是否可以接受,但是您應該利用Java 8功能來像這樣反轉字符串
List<String> chars = Arrays.asList(oldStr.split(""));
Collections.reverse(chars);
String newStr = chars.stream()
.collect(Collectors.joining(""));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.