如何在Perl中从字符串中去除无效的XML字符?
我正在寻找一种标准的、经认可的、健壮的方法,在将字符串写入XML文件之前,从字符串中删除无效字符。我在这里说的是包含退格(^H)和换行符等的文本块 必须有一个标准的库/模块函数来执行此操作,但我找不到它如何在Perl中从字符串中去除无效的XML字符?,xml,perl,Xml,Perl,我正在寻找一种标准的、经认可的、健壮的方法,在将字符串写入XML文件之前,从字符串中删除无效字符。我在这里说的是包含退格(^H)和换行符等的文本块 必须有一个标准的库/模块函数来执行此操作,但我找不到它 我正在使用它来构建DOM树,然后将其序列化到磁盘。如果您使用XML库来构建XML(而不是字符串连接、简单模板等),那么它应该为您解决这一问题。重新发明轮子是没有意义的 等 您可以使用正则表达式删除控制字符,例如\cH将与\cL或\x08和\x0C分别匹配backspace和Formfe
我正在使用它来构建DOM树,然后将其序列化到磁盘。如果您使用XML库来构建XML(而不是字符串连接、简单模板等),那么它应该为您解决这一问题。重新发明轮子是没有意义的
- 等
# Replace all control characters with a space
$text =~ s/[[:cntrl:]]/ /g;
# or remove them
$text =~ s/[[:cntrl:]]//g;
几乎所有人都说过,使用正则表达式。老实说,它不够复杂,不值得添加到库中。使用替换对文本进行预处理 您对上述换行符的评论表明,格式对您来说非常重要,因此您可能需要准确地决定要用什么替换某些字符 XML规范中明确定义了无效字符列表(例如,这里)。不允许使用的字符是ASCII控制字符,包括回车、换行和制表符。因此,您将看到一个29个字符的正则表达式字符类。那当然不算太糟 比如:
$text =~ s/[\x00-\x08 \x0B \x0C \x0E-\x19]//g;
use HTML::Entities "encode_entities_numeric";
$encoded_string = encode_entities_numeric( $string, "\x00-\x08\x0B\x0C\x0E-\x19");
应该这样做。Translate比regex替换快得多。特别是如果你想删除所有字符。使用牛顿集:
$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;
}
};
是的:
Rate substitute translate
substitute 287770/s -- -86%
translate 2040816/s 609% --
我需要删除的字符越多,tr就越快。我以前没有对包含“无效”字符的XML做过很多工作,但是 在我看来,这里有两个完全不同的问题 首先,数据中有一些您可能不需要的字符。您应该独立于任何XML限制来决定它们是什么以及如何删除/替换它们。例如,您可能有类似于
x^H\u y^H\u z^H\u的内容,您决定同时删除退格和以下字符。或者,事实上,您可能不想调整数据,但由于需要用XML表示数据而感到不得不这样做
更新:我为后代保留了以下段落,但它们基于一个误解:我认为只要编码正确,就可以在XML数据中包含任何字符,但似乎有些字符是完全多余的,
甚至编码?LibXML除去了这些字符(至少当前版本是这样),但nul字符除外,它将nul字符视为字符串的结尾,并丢弃它和以下任何字符:(
第二,您的数据中可能有需要用XML编码的字符。理想情况下,您使用的任何XML模块都可以为您做到这一点,但如果没有,您应该能够手动完成,例如:
$text =~ s/[\x00-\x08 \x0B \x0C \x0E-\x19]//g;
use HTML::Entities "encode_entities_numeric";
$encoded_string = encode_entities_numeric( $string, "\x00-\x08\x0B\x0C\x0E-\x19");
但这实际上只是权宜之计。使用适当的XML模块;例如,请参见。好的,这似乎已经得到了回答,但这又有什么关系呢。如果你想创作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;
!/usr/bin/perl
严格使用;
使用XML::LibXML;
我的$doc=XML::LibXML::Document->createDocument('1.0');
$doc->setURI($doc)http://example.com/myuri');
$doc->setDocumentElement($doc->createElement('root-node'));
$doc->documentElement->appendTextChild('text-node',用于删除无效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;
我找到了一个解决方案,但它使用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
$perl-e'print“\n\x{A0}\x{A0}”>无效的.xml
$perl-e“使用XML::Simple;XMLin(“invalid.XML”)”
无效。xml:2:解析器错误:输入不正确UTF-8,请指示编码!
字节:0xA0 0xA0 0x3C 0x2F
$perl-ne的/[^\x09\x0A\x0D\x20-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]///go;打印'invalid.xml>valid.xml
$perl-e“使用XML::Simple;XMLin(“valid.XML”)”
无效。xml:2:解析器错误:输入不正确UTF-8,请指示编码!
字节:0xA0 0xA0 0x3C 0x2F
事实上,这两个文件invalid.xml
和valid.xml
是相同的
问题是范围“\x20-\x{D7FF}”匹配这些unicode字符的有效表示,但不匹配无效字符序列“\x{A0}\x{A0}”。Axeman关于使用tr的权利,但他和newt在反转XML规范的合法字符范围时犯了一个小错误
由于\x20
之前的十六进制数是\x1F
(而不是\x19
!),您应该使用
$string_to_clean =~ tr/\x00-\x08\x0B\x0C\x0E-\x1F//d;
@David:这些库只是从传入字符串中剥离控制字符吗?据我所知,XML::LibXML除了在包含无效字符时拒绝外,对文本节点内容没有任何作用。如果其他库也这样做了,我会感到惊讶。newt,这就是首先使用XML库的意义。当然rse是的,但他问如何确保文本内容不包含无效字符,从而避免出现此问题。@newt:我不完全确定“此问题”是什么意思。我看到XML::LibXML去掉了“非法”字符,但nul除外,它将nul视为数据的结尾:(…这也会删除换行符-所以不是很有用:)哎哟,我没想到换行符。纽特的答案似乎对你要做的事情没问题。是的,这是