【问题标题】:Java - read UTF-8 file with a single emoji symbolJava - 使用单个表情符号读取 UTF-8 文件
【发布时间】:2020-07-28 12:02:05
【问题描述】:

我有一个带有单个 unicode 符号的文件。
该文件以 UTF-8 编码。
它包含一个以 4 个字节表示的符号。
https://www.fileformat.info/info/unicode/char/1f60a/index.htm

F0 9F 98 8A

当我读取文件时,我得到两个符号/字符。

下面的程序打印

?
2
?
?
55357
56842
======================================
��
16
&
======================================
?
2
?
======================================

这是正常的……还是错误?还是我误用了什么?
如何在我的代码中获取单个表情符号?

编辑:还有...我如何为 XML 转义它?

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;

public class Test008 {

    public static void main(String[] args) throws Exception{
        BufferedReader in = new BufferedReader(
                   new InputStreamReader(
                              new FileInputStream("D:\\DATA\\test1.txt"), "UTF8"));
        
        String s = "";
        while ((s = in.readLine()) != null) {
            System.out.println(s);
            System.out.println(s.length());
            System.out.println(s.charAt(0));
            System.out.println(s.charAt(1));
            
            System.out.println((int)(s.charAt(0)));
            System.out.println((int)(s.charAt(1)));
            
            String z = org.apache.commons.lang.StringEscapeUtils.escapeXml(s);
            String z3 = org.apache.commons.lang3.StringEscapeUtils.escapeXml(s);
            
            System.out.println("======================================");
            System.out.println(z);
            System.out.println(z.length());
            System.out.println(z.charAt(0));
            
            System.out.println("======================================");
            System.out.println(z3);
            System.out.println(z3.length());
            System.out.println(z3.charAt(0));
            
            System.out.println("======================================");

        }

        in.close();
    }

}

【问题讨论】:

  • 不应该将字符集称为"UTF-8" 而不是"UTF8"
  • @f1sh 我认为两者都可以,但会尝试一下......是的......同样的事情。
  • 请注意,您根本不需要在 XML 中转义这些字符,您可以按原样编写它们,前提是您使用正确的编码并且接收方正确处理 XML .您必须转义的唯一字符是 XML 本身的语法使用的字符(甚至那些并非总是如此,例如 < 不需要在属性值中转义,但 &必须转义)。
  • @JoachimSauer 谢谢...是的,似乎这就是来自 Apache commons lang 3.11 的 StringEscapeUtils.escapeXml10 所做的。它根本无法逃脱它。我想我现在让它工作了。非常感谢!

标签: java unicode encoding java-8


【解决方案1】:

是的,正常,Unicode 符号是 2 个 UTF-16 字符(1 个字符是 2 个字节)。

int codePoint = s.codePointAt(0); // Your code point.
System.out.printf("U+%04X, chars: $d%n", codePoint, Character.charCount(cp));

U+F09F988A, chars: 2

cmets 之后

Java,使用流:

public static String escapeToAsciiHTML(String s) {
    StringBuilder sb = new StringBuilder();
    s.codePoints().forEach(cp -> {
        if (cp < 128) {
            sb.append((char) cp);
        } else{
            sb.append("&#").append(cp).append(";");
        }
    });
    return sb.toString();
}

【讨论】:

  • ..并且该字符在您的终端上无法打印,因此您将得到 ? 作为输出。
  • 好的,谢谢...然后当我使用System.out.println(org.apache.commons.lang.StringEscapeUtils.StringEscapeUtils.escapeXml(s)); 为XML 转义这个字符串s 时,我得到了这个&amp;#55357;&amp;#56842; 这是错的吧?我应该断定这是 StringEscapeUtils 中的错误吗?
  • @peter.petrov 那是StringEscapeUtils 没有做适当的工作。
  • 当最终的 XML/Html 文件以 UTF-8 编码时,根本不需要转义这些代码点。但你需要关心的是逃跑,&lt;&gt;&amp;"',这是escapeToAsciiHTML 方法无法处理的。另一方面,如果应该将生成的字符串传递给另一个进行正确编码的 XML 写入方法,那么转义代码点的理由就更少了。
  • XML 中也存在这样的数字实体,因此通过 XML 对象可能会得到“&#1111;` 或作为字符串,表情符号再次作为字符插入接收方。如@Holger 说最好的就是什么都不做。
【解决方案2】:

StringEscapeUtils 已损坏。不要使用它。试试NumericEntityEscaper

或者,更好的是,因为 apache 公共库往往是糟糕的 API** 并且无论如何都损坏了***,guava* 的 XmlEscapers

java 是 unicode,是的,但 'char' 是个谎言。 'char' 不代表字符;它代表一个单一的、无符号的 16 位数字。从j.l.String 对象中获取字符的实际方法不是charAt,这是用词不当;我是codepointAt,还有朋友们。

这(char 是假冒的)通常无关紧要;大多数实际字符适合 16 位 char 类型。但是当他们不这样做时,这很重要,并且那个表情符号不适合。在 java 使用的 unicode 模型和 char 类型中,您将获得 2 个 char 值(表示单个 unicode 字符)。这对称为“代理对”。

请注意,正确的方法往往适用于 int(毕竟,您需要 32 位来表示一个 unicode 符号)。

*) guava 有它自己的问题,因为它不积极地与自身向后兼容,它往往会导致依赖地狱。不幸的是,这是一种选择你的毒药的交易。

**) Utils-anything 通常是糟糕的 API 设计的标志; 'util' 作为一个术语几乎毫无意义,通常意味着您已经破坏了面向对象的模型。正确的模型当然是有一个对象来表示将数据以一种形式(例如,原始字符串)转换为另一种形式(例如,可以直接转储到 XML 文件中的字符串,转义并且很好)的过程 - 等等因此,事物将被称为“逃逸者”,并且可能存在于名为“逃逸者”或“文本”的包中。幸运的是,后来版本的 apache 库以及 guava 都“修复”了这个问题。

***) 正如这个示例所示,这些 API 通常不会按照您的意愿行事。注意 apache 是开源的;如果您希望这些 API 更好,它们会接受拉取请求 :)

【讨论】:

  • 非常感谢。似乎我们已经包含了番石榴,所以我想我会尝试一下。 xmlAttributeEscaper() 方法 - 我可以重用从那里得到的这个实例吗?还是我每次都需要创建一个新的?我正在处理的整个事情都存在于一个非常复杂的多线程后端应用程序中。
  • 我认为这个XmlEscapers.xmlAttributeEscaper() 也不起作用。
  • 或者可能是这样...我很困惑,我整天都在研究这个问题:) 似乎XmlEscapers.xmlAttributeEscaper().escape(s) 产生与org.apache.commons.lang3.StringEscapeUtils.escapeXml(s) 相同的字符串,我只是比较了产生两个字符串并且它们相等(我的意思是根据 equals 方法)。
  • @peter.petrov 来自XmlEscapers 的链接文档:“目前此类提供的转义符不会转义 ASCII 字符范围之外的任何字符。”跨度>
  • @Holger,是的,我后来注意到了。谢谢。 org.apache.commons.lang3.StringEscapeUtils.escapeXml10 似乎最适合我!从 3.11 jar 版本:mvnrepository.com/artifact/org.apache.commons/commons-lang3/…
猜你喜欢
  • 1970-01-01
  • 2020-09-19
  • 1970-01-01
  • 1970-01-01
  • 2014-08-06
  • 1970-01-01
  • 2015-06-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多