【问题标题】:How can I encode characters like emojis as UTF8 without unpaired surrogate characters?如何在没有未配对代理字符的情况下将表情符号等字符编码为 UTF8?
【发布时间】:2019-09-06 00:33:42
【问题描述】:

我有需要写入 Google BigQuery 的包含各种字符的字符串,这需要严格的 UTF8 字符串。尝试使用各种表情符号输入编写字符串时,出现错误:

java.lang.IllegalArgumentException: Unpaired surrogate at index 3373
    at org.apache.beam.sdk.repackaged.com.google.common.base.Utf8.encodedLengthGeneral(Utf8.java:93)
    at org.apache.beam.sdk.repackaged.com.google.common.base.Utf8.encodedLength(Utf8.java:67)
    at org.apache.beam.sdk.coders.StringUtf8Coder.getEncodedElementByteSize(StringUtf8Coder.java:145)
...

我有一个解决这个问题的方法,只需从字符串中删除所有代理字符:

    private static String removeSurrogates(String query) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < query.length(); i++) {
            char c = query.charAt(i);
            if (!(Character.isHighSurrogate(c) || Character.isLowSurrogate(c))) {
                sb.append(c);
            }
        }
        return sb.toString();
    }

然而,这会产生一个类似

的字符串

???????????????????????????????⚔⌨??????⛳?????????? ?????????????????????????????????????????????⛏??????

减少到只有四个表情符号

⚔⌨⛳⛏

是否有适当的方法将这些字符转换为 UTF8 而不会丢失,并且不使用不成对的代理?

(抱歉,我对一般字符集的理解不是很好)

【问题讨论】:

  • 编码为 UTF8?
  • 输入数据来自一个已经 UTF8 编码的文件。我们测试它是使用 com.google.protobuf.ByteString.isValidUtf8(),然后使用来自 loggedBytes.toStringUtf8() 的字符串,但是当我们尝试写入字符串时,我们会得到未配对的代理异常。
  • 当您将文件读入String 时,该文件将被转换为UTF-16(Java char)。这就是为什么你有代理对。如果你只是将它转换回来,代理对就会消失。
  • 谢谢,您有如何将其转换回来的示例吗?
  • 我发现了问题。我们正在使用 org.apache.commons.lang3.StringEscapeUtils.unescapeHtml4 将字符串中的 HTML 实体转换为它们的非编码形式。这似乎破坏了一些非拉丁字符。例如,传递字符串“Italien ??????????”通过这种方法将其转换为“Italien ???????” (最后一个字符被破坏)。所以,结果不是数据流或大查询问题

标签: java google-bigquery google-cloud-dataflow emoji


【解决方案1】:

Is there a proper way to convert these characters into UTF8 可能,如果您只是发送字符串,它将被转换为 UTF-8。这就是 Java 编码器的工作原理。

如果没有,而你发送的是二进制文件,你可以直接转换:

private static byte[] removeSurrogates(String query) {
    return query.getBytes( "UTF-8" );
}

【讨论】:

    【解决方案2】:

    让我暂时脱离 Java 来说明 BigQuery 可以处理表情符号:

    CREATE TABLE `public_dump.emoji_test`
    AS
    SELECT "????????⚔⌨?⛳?????????????⛏?" emojis
    

    然后测试是否存在:

    SELECT COUNT(*)
    FROM `fh-bigquery.public_dump.emoji_test`
    WHERE emojis LIKE '%?%'
    
    1
    

    用 Python 做这件事很简单:

    插入新数据也不是问题:

    很抱歉,我不知道如何使用 Java 解决此问题,但我希望看到 BigQuery 的 API 能够优雅地处理表情符号的证明对您有所帮助。

    【讨论】:

    • 感谢您的回复。我们的 Dataflow 作业中一定存在问题,即在写入 BigQuery 之前未正确设置编码器或其他问题。我正在努力想出一个可验证的最小示例,因为从原始日志文件到 BigQuery 的流程非常冗长且复杂。
    • 如果这是 Dataflow,也尝试使用该标签
    • 我发现了问题。我们正在使用 org.apache.commons.lang3.StringEscapeUtils.unescapeHtml4 将字符串中的 HTML 实体转换为它们的非编码形式。这似乎破坏了一些非拉丁字符。例如,通过此方法传递字符串“Italien ??”会将其转换为“Italien ??” (最后一个字符被破坏)。所以,结果不是数据流或大查询问题
    【解决方案3】:

    我发现了问题。我们正在使用 org.apache.commons.lang3.StringEscapeUtils.unescapeHtml4 将字符串中的 HTML 实体转换为它们的非编码形式。这似乎破坏了一些非拉丁字符。例如,通过此方法传递字符串“Italien ??”会将其转换为“Italien ??” (最后一个字符被破坏)

    通过此方法传递“????????⚔⌨?⛳?????????????⛏?”将其转换为“? ??????⚔⌨??⛳??????”

    import org.apache.commons.lang3.StringEscapeUtils;
    
    public class CharacterTest {
        public static void main(String[] args) {
            String good = "????????⚔⌨?⛳?????????????⛏?";
            String bad = StringEscapeUtils.unescapeHtml4(good);
            System.out.println(good + "->" + bad);
        }
    }
    
    ????????⚔⌨?⛳?????????????⛏?->????????????????⚔⌨??⛳??????
    

    现在寻找替代的 HTML 实体解码器...

    【讨论】:

      猜你喜欢
      • 2020-04-23
      • 1970-01-01
      • 2014-09-30
      • 2013-09-14
      • 2011-11-02
      • 1970-01-01
      • 2019-01-27
      • 2021-10-18
      • 2016-10-05
      相关资源
      最近更新 更多