[英]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.