【问题标题】:Why is non-breaking space not a whitespace character in Java?为什么不间断空格不是Java中的空白字符?
【发布时间】:2023-04-06 22:24:01
【问题描述】:

在寻找从解析的 HTML 中修剪非中断空间的正确方法时,我首先偶然发现了 Java 的 String.trim() 的斯巴达式定义,它至少有正确的文档记录。我想避免明确列出符合修剪条件的字符,所以我假设在 Character 类上使用 Unicode 支持的方法可以为我完成这项工作。

那时我发现Character.isWhitespace(char) 明确排除了不间断空格:

它是 Unicode 空格字符(SPACE_SEPARATORLINE_SEPARATORPARAGRAPH_SEPARATOR但也不是不间断空格'\u00A0''\u2007'、@987654329 @)。

这是为什么呢?

corresponding .NET equivalent 的实现区别不大。

【问题讨论】:

    标签: java unicode


    【解决方案1】:

    Character.isWhitespace(char) 是旧的。真的老了。 Java 早期所做的许多事情都遵循 C 的约定和实现。

    现在,十多年后,这些事情似乎是错误的。即使在 Java 的最初几天和 .NET 的最初几天之间,它也证明了事情已经发展到何种程度。

    Java 力求 100% 向后兼容。因此,即使 Java 团队认为修复他们最初的错误并为从 Character.isWhitespace(char) 返回 true 的字符集添加不间断空格会很好,但他们不能,因为几乎可以肯定存在这样的软件依赖于当前的实现完全按照它的方式工作。

    【讨论】:

    • 关于向后兼容性:我同意,但没有理由不添加,比如 Character.isWhitespaceNew(char) 来捕捉当前情况。
    • 而在另一条路上,Java。一种为后来者开辟道路的语言(他们从错误中吸取了教训),但如果有人有其他选择,为什么有人会自愿使用它,这超出了我的理解。
    • 由于向后兼容,它仍然在语言中,但它没有解释为什么它最初是这样的。
    • @Jirka 好吧,他们确实添加了它,除了它叫做 Character.isSpaceChar(char);但它不包括换行符
    【解决方案2】:

    从 Java 5 开始,还有一个 isSpaceChar(int) 方法。这不是你想要的吗?

    确定指定字符(Unicode 代码点)是否为 Unicode 空格字符。当且仅当 Unicode 标准将字符指定为空格字符时,该字符才被视为空格字符。如果角色的一般类别类型是以下任何一种,则此方法返回 true:...

    【讨论】:

    • OP 寻找的并不是这种方法的存在;而是一个trim-type 函数,它使用该方法来确定要剥离的内容。
    • 注意还有isSpaceChar(char)方法
    • isSpaceChar() 方法不包含拉丁空格(例如制表符)。
    【解决方案3】:

    如上所述,isSpaceChar(int) 将为 OP 提供答案。它似乎相当谨慎地记录下来,但这种方法实际上是useable with regexes。 所以:

        "X\u00A0X X".replaceAll("\\p{javaSpaceChar}", "_");
    

    将产生一个“X_X_X”字符串。留给读者练习使用正则表达式来修剪字符串。 (带有一些标志的模式应该可以解决问题。)

    【讨论】:

    【解决方案4】:

    我认为Java 的实现比.NET 的更正确。不间断空格本质上是一个看起来像一个的非空白字符。也就是说,如果你有字符串“foo”和“bar”,并在它们之间放置任何传统的空白字符,你会得到一个分词。然而,一个不间断的空间不会将两者分开。

    【讨论】:

    • 不间断空格仍然是单词边界。 “不间断空格”中的“中断”是指应如何解释它以用于-中断,而不是分词。
    【解决方案5】:

    唯一应该对不间断空格进行特殊处理的情况是使用旨在执行文本自动换行的代码。

    对于所有其他用途,包括字数统计、修剪和沿字边界的通用拆分,不间断空格仍然是空格

    任何关于不间断空格“看起来像”空格但不是空格的论点都与 Unicode 的全部观点相冲突,Unicode 是根据其含义而不是显示方式来表示字符。

    因此,恕我直言,String.trim() 的 Java 实现没有按预期执行,底层的 Character.isWhitespace() 函数有问题。

    我的猜测是,Java 实现者编写 isWhitespace() 是基于在控件中执行文本换行的需要。他们应该将此函数命名为 isWordWrappingBoundary() 或更清晰的名称,并对 trim() 使用限制较少的空白测试。

    【讨论】:

    • String.trim() 比这更糟糕。它只是修剪 ASCII 控制字符,根本没有 Unicode 空白,不管是否中断。
    【解决方案6】:

    看起来方法名称(isWhitespace)与其功能(检测分隔符)不一致。如果您查看引用的 Javadoc 页面中的完整字符列表,“分隔符”功能就相当清楚了:

    * It is a Unicode space character (SPACE_SEPARATOR, LINE_SEPARATOR, or PARAGRAPH_SEPARATOR) but is not also a non-breaking space ('\u00A0', '\u2007', '\u202F').
    * It is '\u0009', HORIZONTAL TABULATION.
    * It is '\u000A', LINE FEED.
    * It is '\u000B', VERTICAL TABULATION.
    * It is '\u000C', FORM FEED.
    * It is '\u000D', CARRIAGE RETURN.
    * It is '\u001C', FILE SEPARATOR.
    * It is '\u001D', GROUP SEPARATOR.
    * It is '\u001E', RECORD SEPARATOR.
    * It is '\u001F', UNIT SEPARATOR. 
    

    不间断空格的功能应该是单词之间没有被连字符算法分隔的视觉空间。

    【讨论】:

      【解决方案7】:

      在使用Apache Commons 函数StringUtils.isBlank()(和相关函数)时也要小心,它具有同样奇怪的isWhitespace 行为,即不间断的空格被认为是非空白的。

      【讨论】: