首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Java中反转utf-8字符串

在Java中反转utf-8字符串
EN

Stack Overflow用户
提问于 2017-06-25 06:40:25
回答 5查看 3.3K关注 0票数 1

我试图用Java编写一段java代码来反转utf-8字符串。我在一次面试中被问到了这个问题。然而,我想知道,如果我将字节转换为比特,并从比特中获得代码点,那么我们如何进行编码。这可能就是面试官想要的。

代码语言:javascript
复制
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());

    }

}
EN

回答 5

Stack Overflow用户

发布于 2017-06-25 07:54:16

首先,所有Java字符串都是UTF-16编码,而不是UTF-8编码。这对于像颠倒字符串这样的任务很重要,因为一个字符占用的字节数取决于编码。在UTF-8中,字节数是可变的,而在UTF-16中,字节数始终是两个字节。char是16位数据,即使它只是表示ASCII码。UTF-8可以对8位的ASCII进行编码,但可能需要更多的编码来表示其他字符。

因为char是16位的,所以大多数字符(包括示例中的Ž®aͻ )都很适合单独的char,没有任何问题。然而,一些字符(特别是表情符号属于这一类)不能用单个char来表示,现在我们使用surrogate pairs。在处理可能有代理项对的文本时,必须非常小心字符串操作,因为大多数Java (尤其是String上的几乎每个方法)都不能正确地处理它们。

更好的例子是,考虑字符串""。六个字,对吧?不是根据Java!

代码语言:javascript
复制
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("")));

这将打印:

代码语言:javascript
复制
String: 
Length: 12
Chars:  [?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?]
Split:  [?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?]

现在,一些API确实可以正确处理代理项对,例如StringBuilder.reverse()

如果序列中包含任何代理项对,则这些代理项对将被视为反向操作的单个字符。因此,高低代理的顺序永远不会颠倒。

为了便于面试,假设您不能使用此方法(或者,可以理解的是,您不能当场回忆它是否安全),您可以使用String.codePoints()遍历字符串的代码点。这使您可以安全地反转内容:

代码语言:javascript
复制
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()));

打印:

代码语言:javascript
复制
票数 6
EN

Stack Overflow用户

发布于 2017-06-25 09:06:14

作为dimo414 mentionedStringBuilder.reverse()可以正确处理代理项对:

如果序列中包含任何代理项对,则这些代理项对将被视为反向操作的单个字符。因此,高低代理的顺序永远不会颠倒。

这意味着除了使用不应该使用的StringBuffer之外,answer by Piyush是很好的。

如果您坚持自己反转一个String (它是UTF-16、UTF-8),那么您可以像这样做,它向后迭代字符,并处理代理(或者,如果您不关心代理,则删除if语句):

代码语言:javascript
复制
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

代码语言:javascript
复制
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,您需要向后处理,为此,您需要了解编码是如何工作的。

0127范围内的Unicode字符被编码为一个字节(即位0xxxxxxx)。所有其他Unicode字符都被编码为一个字节块,以11xxxxxx字节开头,其余的是10xxxxxx字节,因此我们可以检测到这样的字节块并保留它们。

代码语言:javascript
复制
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;
}
票数 4
EN

Stack Overflow用户

发布于 2017-06-25 10:31:50

首先,您可以将"utf-8字符串“的含义转换为"utf-16字符串”(java.lang.String),可能是将utf-8代码单元的字节数组转换为

代码语言:javascript
复制
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提供了各种文本分隔符迭代器,包括一个用于区域设置中的字素分隔符的迭代器。

代码语言:javascript
复制
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”吗?

你也可以在以后问,这家公司做什么类型的项目,其中反向字符串是重要的?

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/44741592

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档