【问题标题】:Remove weird invalid character in ruby删除 ruby​​ 中奇怪的无效字符
【发布时间】:2015-02-23 11:56:01
【问题描述】:

我有一些 XML 内容 (UTF-8),其中包含无效字符(当我尝试使用 Nokogiri::XML(content) 解析内容时,nokogiri 告诉我 Line 2190, SyntaxError: PCDATA invalid Char value 15)。

字符在 Sublime Text 编辑器中显示为“SI”:

当我尝试复制角色时,没有任何内容被复制,所以我什至无法查找。例如,当我在我的 Atom 编辑器中打开它时,不会显示“SI”。但是,当我用右键遍历字符时,我必须键入两次才能越过放置“SI”字符的位置。

首先,这是一个什么样的角色?第二:Ruby中有没有办法删除这些字符。我用content.chars.select{|i| i.valid_encoding?}.join 试过了,但它没有删除字符。

更新

我通过用 ruby​​ 读取原始文件找到了这个字符。字符为\u000F,"\u000F".ord 返回字符代码15。关于http://www.fileformat.info/info/unicode/char/000f/index.htm,这是一个SHIFT IN 字符。还有其他类似的角色吗?我可以使用str.split("\u000F").join 删除它们,但如果还有其他类似的字符,这似乎不是一个好方法。有什么想法吗?

【问题讨论】:

  • 如果您从可以影响或控制的来源接收 XML,那么最好的解决方法是在来源处正确转义(或删除,如果它根本不应该存在)字符 -如有必要,通过提交错误报告。解析器准确地抱怨。尝试猜测 XML 中的其他问题并预处理 XML 直到其格式正确是可能的,但会很丑陋并且仅适用于这一来源。
  • 很遗憾,我无法控制源。好的,也许我只是等到下一个错误发生,将此字符添加到列表中并再次等待;)
  • 一般解决方案的一个问题是,在 CDATA 块中,这些坏字符有时是可以的。如果它在数据中是有原因的(记录分隔符),那么您可能更愿意用正确转义的版本替换它。
  • 嗯,你是对的。你能举一个这样的坏角色的例子吗?

标签: ruby encoding character-encoding special-characters


【解决方案1】:

如果它是实际上对编码无效的字节序列(UTF-8),那么在 ruby​​ 2.1+ 中,您可以使用 String#scrub 方法。默认情况下,它会将无效字符替换为“unicode 替换字符”(通常在框中表示为问号),但您也可以使用它来完全删除它们。

但是,正如您所注意到的,您的“奇怪字节”实际上是有效的 UTF-8,表示 unicode 代码点“\u000F”,即SHIFT IN 控制字符。 (搞清楚所涉及的实际字节/字符做得很好,这是困难的部分!)

因此,如果我们想删除它们,我们必须清楚“这样的字符”是什么意思。像什么角色?

Nokogiri 抱怨它在 XML“PCDATA”(已解析字符数据)区域中无效。为什么它是合法的 unicode/UTF-8,但在 XML PCDATA 中无效?在 XML 字符数据中什么是合法的?我试图弄清楚,但它变得令人困惑,spec 显然说某些角色“不受欢迎”(什么?),并在我看来对其他事情做出了自相矛盾的陈述。

我不确定 Nokogiri 究竟会从 PCData 中禁止哪些字符,我们必须查看 Nokogiri 源(或更可能是 libxml 源),或者尝试向更了解 nokogiri/ 的人提问libxml 的源代码。

但是,“\u000F”是一个“控制字符”,您不太可能希望在 XML 字符数据中使用控制字符(除非您知道这样做),并且 XML 规范似乎不鼓励控制字符(显然 Nokogiri/libxml实际上不允许他们?)。所以解释“像这样的字符”的一种方法是“控制字符”。

您可以使用此正则表达式从字符串中删除所有控制字符,例如:

"Some string \u000F more".gsub(/[\u0001-\u001A]/ , '') # remove control chars, unicode codepoints from 0001 to 001A
   # => "Some string  more"

如果我们将“像这样的字符”解释为任何不打印的字符 - 比“控制字符”更广泛的类别,并且将包括一些 nokogiri 完全没有问题的字符。我们可以尝试通过使用 ruby​​ 对正则表达式中的 unicode 字符类的支持来删除更多的控制字符:

some_string.gsub(/[^[:print:]]/ , '')

[:print] 被相当模糊地记录为“排除控制字符和类似字符”,因此这与我们对我们想要做什么的模糊规范相匹配。 :)

所以这真的取决于我们所说的“像这样的字符”是什么意思。真的,对于您的情况,“这样的字符”可能意味着“Nokogiri/libxml 将拒绝允许的任何字符”,恐怕我还没有真正回答 那个 问题,因为我不确定,也无法轻易弄清楚。但在许多情况下,删除控制字符,甚至更好地删除与[:print] 不匹配的字符可能会很好,除非您有理由希望保留控制字符和类似字符(如果您知道需要它们作为记录)分隔符,例如)。

如果您想用 unicode 替换字符替换它们而不是删除它们,该字符通常用于表示“我们无法处理的字节序列”:

"Shift in: \u000F".gsub(/[^[:print:]]/, "\uFFFD")
   # => "Shift in: �"

如果您不想删除它们,而是想以某种方式转义它们,那么它们可以在 XML 解析后重新构建......再问一遍,我会弄明白的,但我现在还没有。 :)

欢迎处理字符编码问题,它有时确实会让人感到困惑。

【讨论】:

  • 根据您的链接,XML 的合法字符范围是 #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF] - 这显然不包括 #xF,因此对于 Nokogiri 或查找 Nokogiri 接受哪些字符不是问题,如所述在您的其余答案中。 OP 问题中的 XML 格式不正确。
  • 我不确定我是这样阅读规范的,但无论如何,使用 gsub 从源 XML 文档中删除那些无效字符是很容易的。是的,XML 格式不正确,当然。
【解决方案2】:

一种在 UTF-8 文本中删除控制字符但不包括空格的方法。 Iconv 将首先将字符串转换为 UTF-8 编码。编码行允许您指定如何处理无效字符,但不会删除控制字符。 gsub 负责删除控制字符,但留下空白。如果由于正则表达式约束,使用“NOT ( NOT Control OR is Whitespace)”代替替代 if (Is Control and NOT whitespace)。这适用于 ruby​​ 1.9.x 向前,不适用于 1.8.7 REE。

require 'iconv'

def only_valid_chars(text)
  return "" unless text
  text = Iconv.conv('UTF-8//IGNORE', 'UTF-8', text)
  text.encode('UTF-8', 'UTF-8', {:invalid => :replace, :undef => :replace, :replace => ""})
  #remove control characters, keep white space and line endings
  text = text.gsub(/[^ [^[:cntrl:]] | [\s] ]/,'')
  return text
end
#text = "08-10-06 –"
#text = "08-10-06 â\u0080\u0093 Appr \n \r  \r\n ABC"
#only_valid_chars(text)

【讨论】:

  • iconv 已从 ruby​​ 2.0+ 中移除
  • 另外,该正则表达式会输出有关字符类具有重复范围的警告。
【解决方案3】:

同样的事情发生在我使用 Roo gem 从 xlsx 文件中读取电子邮件时。

我从来不知道我的字符串中到底是哪个字节/字符,但是因为我知道我会接受哪些字符,所以我只是删除了那些不匹配的字符,如下所示:

email_chars = 'a-z0-9\.\-_@'
clean_email = email.gsub(/[^#{email_chars}]/, '')

【讨论】:

    猜你喜欢
    • 2014-10-04
    • 1970-01-01
    • 2013-04-14
    • 1970-01-01
    • 1970-01-01
    • 2019-01-09
    • 2018-03-28
    • 2020-05-04
    • 1970-01-01
    相关资源
    最近更新 更多