我正在寻找在将字符串写入XML文件之前从字符串中删除无效字符的标准,批准和强大的方法。我在这里谈论的是包含退格(^ H)和换页字符等的文本块。
有作为标准库/模块函数来执行此操作,但我找不到它。
我正在使用XML::LibXML构建一个DOM树,然后我将其序列化为磁盘。
答案 0 :(得分:7)
删除无效xml-1.0字符的完整正则表达式为:
# #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
$str =~ s/[^\x09\x0A\x0D\x20-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]//go;
对于xml-1.1来说是:
# allowed: [#x1-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
$str =~ s/[^\x01-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]//go;
# restricted:[#x1-#x8][#xB-#xC][#xE-#x1F][#x7F-#x84][#x86-#x9F]
$str =~ s/[\x01-\x08\x0B-\x0C\x0E-\x1F\x7F-\x84\x86-\x9F]//go;
答案 1 :(得分:6)
正如几乎所有人都说的那样,使用正则表达式。说实话,它不够复杂,不值得添加到库中。使用替换预处理文本。
您对上述换行符的评论表明,格式化对您来说非常重要,因此您可能必须准确确定要替换某些字符的内容。
XML规范中明确定义了无效字符列表(例如,此处为http://www.w3.org/TR/REC-xml/#charsets)。不允许的字符是ASCII控制字符栏回车,换行和制表符。所以,你正在看一个29个字符的正则表达式字符类。那肯定不是太糟糕。
类似的东西:
$text =~ s/[\x00-\x08 \x0B \x0C \x0E-\x19]//g;
应该这样做。
答案 2 :(得分:5)
我找到了一个解决方案,但它使用iconv
命令而不是perl。
$ iconv -c -f UTF-8 -t UTF-8 invalid.utf8 > valid.utf8
上面基于正则表达式给出的解决方案不起作用!! ,请考虑以下示例:
$ perl -e 'print "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<root>\x{A0}\x{A0}</root>"' > invalid.xml
$ perl -e 'use XML::Simple; XMLin("invalid.xml")'
invalid.xml:2: parser error : Input is not proper UTF-8, indicate encoding !
Bytes: 0xA0 0xA0 0x3C 0x2F
$ perl -ne 's/[^\x09\x0A\x0D\x20-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]//go; print' invalid.xml > valid.xml
$ perl -e 'use XML::Simple; XMLin("valid.xml")'
invalid.xml:2: parser error : Input is not proper UTF-8, indicate encoding !
Bytes: 0xA0 0xA0 0x3C 0x2F
事实上,两个文件invalid.xml
和valid.xml
相同。
问题是“\ x20- \ x {D7FF}”范围匹配那些unicode字符的有效表示,但不是无效的字符序列“\ x {A0} \ x {A0}”。
答案 3 :(得分:4)
翻译比正则表达式替换更快批次。特别是如果您想要删除所有字符。使用newt的集合:
$string_to_clean =~ tr/\x00-\x08\x0B\x0C\x0E-\x19//d;
这样的测试:
cmpthese 1_000_000
, { translate => sub {
my $copy = $text;
$copy =~ tr/\x00-\x08\x0B\x0C\x0E-\x19//d;
}
, substitute => sub {
my $copy = $text;
$copy =~ s/[\x00-\x08\x0B\x0C\x0E-\x19]//g;
}
};
yeilded:
Rate substitute translate
substitute 287770/s -- -86%
translate 2040816/s 609% --
我需要删除更快的tr所需的字符越多。
答案 4 :(得分:3)
如果您使用XML库来构建XML(而不是字符串连接,简单模板等),那么它应该为您处理。重新发明轮子毫无意义。
答案 5 :(得分:3)
好的,这似乎已经得到了解答,但是嘿。如果要创作XML文档,必须使用XML库。
#!/usr/bin/perl
use strict;
use XML::LibXML;
my $doc = XML::LibXML::Document->createDocument('1.0');
$doc->setURI('http://example.com/myuri');
$doc->setDocumentElement($doc->createElement('root-node'));
$doc->documentElement->appendTextChild('text-node',<<EOT);
This node contains &, ñ, á, <, >...
EOT
print $doc->toString;
这会产生以下结果:
$ perl test.pl
<?xml version="1.0"?>
<root-node><text-node> This node contains &, 񬠡, <, >...
</text-node></root-node>
编辑:我现在看到您已经在使用XML :: LibXML。这应该可以解决问题。
答案 6 :(得分:0)
您可以使用正则表达式删除控制字符,例如\ cH将匹配\ cL或\ x08和\ x0C分别匹配退格和Formfeed。
答案 7 :(得分:0)
你可以使用一个简单的regex来查找和替换你的文本块中的所有控制字符,用空格替换它们或者完全删除它们 -
# Replace all control characters with a space
$text =~ s/[[:cntrl:]]/ /g;
# or remove them
$text =~ s/[[:cntrl:]]//g;
答案 8 :(得分:0)
我之前没有对包含“无效”字符的XML做过很多工作,但是 在我看来,你在这里有两个完全不同的问题。
首先,您可能不需要数据中的字符。您应该决定它们是什么以及如何删除/替换它们,而不受任何XML限制的影响。例如,您可能有x^H_y^H_z^H_
之类的内容,您决定要删除退格和后续字符。或者你可能实际上不想调整你的数据,但是由于需要用XML表示它而感到被迫。
更新:我为后代保留了以下段落,但它们基于一个误解:我认为你可以在XML数据中包含任何字符,只要你正确编码它,但似乎有一些字符是彻头彻尾的verboten, 甚至编码? XML :: LibXML剥离它们(至少当前版本是这样做的),除了nul字符,它将其视为字符串的结尾,丢弃它以及随后的任何内容:(
其次,您可能在数据中包含需要使用XML编码的字符。理想情况下,您使用的任何XML模块都可以为您执行此操作,但如果不是,则应该能够手动执行此操作,例如:
use HTML::Entities "encode_entities_numeric";
$encoded_string = encode_entities_numeric( $string, "\x00-\x08\x0B\x0C\x0E-\x19");
但那真的只是权宜之计。使用适当的XML模块;例如,见this answer。
答案 9 :(得分:0)
Axeman关于使用tr的权利,但是他和newt在反转XML规范的法律字符方面犯了一点错误。 http://www.w3.org/TR/REC-xml/#charsets给出了
Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD] | [#x10000-#x10FFFF]
由于\x20
之前的十六进制数是\x1F
(不是\x19
!),您应该使用
$string_to_clean =~ tr/\x00-\x08\x0B\x0C\x0E-\x1F//d;