我有一些XML内容(UTF-8),其中包含无效字符(当我尝试使用Line 2190, SyntaxError: PCDATA invalid Char value 15
解析内容时,nokogiri告诉我Nokogiri::XML(content)
。
该字符在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
删除它们,但如果还有其他这样的字符,这似乎不是一个好方法。有什么想法吗?
答案 0 :(得分:12)
如果字节序列实际上对编码无效(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拒绝允许的任何字符#34;我害怕我实际上没有回答那个问题,因为我和#39;我不确定,也无法轻易搞清楚。但是对于很多情况下,删除控制字符,甚至更好地删除不匹配[:print]
的字符可能会很好,除非你有理由想要控制字符和类似字符(如果你认识的话)例如,他们需要它们作为记录分隔符。
如果不是删除,你想用unicode替换字符替换它们,这通常用于表示我们无法处理的字节序列":
"Shift in: \u000F".gsub(/[^[:print:]]/, "\uFFFD")
# => "Shift in: �"
如果不是删除它们而是想以某种方式逃避它们,可以在XML解析后重建它们......再次问这个并且我会弄明白,但我现在还没有。 :)
欢迎处理字符编码问题,有时确实会让人感到困惑。
答案 1 :(得分:0)
以UTF-8文本删除控制字符但不包含空格的方法。 Iconv将首先将字符串转换为UTF-8编码。编码行允许您指定如何处理无效字符,但不删除控制字符。 gsub负责删除控制字符,但留下空白区域。如果由于正则表达式约束而使用“NOT(NOT Control OR is Whitespace)”代替替换if(Is Control和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)
答案 2 :(得分:0)
同样的事情发生在我使用Roo gem从xlsx文件中读取电子邮件时。
我从来不知道我的字符串中究竟出现了哪些字节/字符,但是因为我知道我会接受哪些字符,所以我删除了那些不匹配的字符,如下所示:
email_chars = 'a-z0-9\.\-_@'
clean_email = email.gsub(/[^#{email_chars}]/, '')