Вопрос по xml, perl – Как я могу удалить недопустимые символы XML из строк в Perl?

10

Я ищу стандартный, одобренный и надежный способ удаления недопустимых символов из строк перед записью их в файл XML. Я говорю здесь о блоках текста, содержащих символ возврата влево (^ H), символы в форме подачи и т. Д.

Тамhas быть стандартной библиотечной / модульной функцией для этого, но я не могу ее найти.

Я используюXML :: LibXML построить дерево DOM, которое я затем сериализовать на диск.

Ваш Ответ

10   ответов
5

iconv команда вместо перл.

$ iconv -c -f UTF-8 -t UTF-8 invalid.utf8 > valid.utf8

Решения, приведенные выше, основаны наregular expressions do not work!!рассмотрим следующий пример:

$ 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 являютсяidentical.

Дело в том, что диапазон & quot; \ x20- \ x {D7FF} & quot; Матчиvalid представления этих символов Юникода, но не, например. неверная последовательность символов & quot; \ x {A0} \ x {A0} & quot ;.

проблема в том, что существуют кодовые точки, которые являются допустимыми UTF-8, которые являются недопустимыми в XML
К сожалению, это не работает для удаления недопустимых управляющих символов между тегами, такими как & lt; D & gt; [015] & lt; / D & gt; где [015] - недопустимый символ, попавший в строку.
3

это, кажется, уже ответили, но что за эй? Если вы хотите создавать документы XML, выmust использовать библиотеку 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 &amp;, &#x6C821;, &lt;, &gt;...
</text-node></root-node>

Edit: Теперь я вижу, что вы уже используете XML :: LibXML. Это должно сделать свое дело.

хммм ... только что сталкивался с этим ... XML :: LibXML не обрабатывает это, если вы используете $ node- & gt; appendText ($ str) ... но делает, если вы используете $ parent- & gt; appendTextChild (& apos; node & apos) ;, $ str) ... странность
Конечно, это так. Но первоначальный вопрос заключался в удалении символов, из-за которых XML :: LibXML отклоняет содержимое (символы ниже пробела ASCII, пробелы). Это не совсем то же самое.
Спасибо за пример; Я был немного шокирован комментарием, в котором утверждалось, что XML :: LibXML не справился с этим для вас.
& quot; использовать строгий & quot; это хорошо, но предупреждения еще важнее. Не забывайте -w или & quot; используйте предупреждения & quot ;!
0

неверный & quot; персонажи раньше, но мне кажется, у вас есть две совершенно разные проблемы здесь.

Во-первых, в ваших данных есть символы, которые вам могут не понадобиться. Вы должны решить, что это такое и как вы хотите удалить / заменить их независимо от каких-либо ограничений XML. Например, у вас могут быть такие вещи, какx^H_y^H_z^H_ где вы решите, что хотите убрать как backspace, так и следующий символ. Или же возможно, что вы на самом деле не хотите корректировать свои данные, но чувствуете необходимость в представлении их в XML.

Обновление: я сохранил следующие абзацы для потомков, но они основаны на недоразумении: я думал, что вы можете включить любой символ в данные XML, если вы его правильно закодировали, но, похоже, есть некоторые символы, которые являются полностью глагольными, даже закодированы? XML :: LibXML удаляет их (по крайней мере, в текущей версии), за исключением символа nul, который он обрабатывает как конец строки, отбрасывая его и все, что следует за ним :(

Во-вторых, в ваших данных могут быть символы, которые вы сохранили и которые нуждаются в кодировании в XML. В идеале, какой бы модуль XML вы не использовали, он бы сделал это для вас, но если это не так, вы должны быть в состоянии сделать это вручную, например:

use HTML::Entities "encode_entities_numeric";
$encoded_string = encode_entities_numeric( $string, "\x00-\x08\x0B\x0C\x0E-\x19");

Но это на самом деле просто временная мера. Используйте правильный модуль XML; см. напримерэтот ответ.

6

используйте регулярное выражение. Это, честно говоря, недостаточно сложный процесс, чтобы его можно было добавить в библиотеку. Предварительно обработайте ваш текст с заменой.

Ваш комментарий о переводах строки выше предполагает, что форматирование имеет для вас какое-то значение, поэтому вам, возможно, придется решить, на что именно вы хотите заменить некоторые символы.

Список недопустимых символов четко определен в спецификации XML (здесь -http://www.w3.org/TR/REC-xml/#charsets - например). Запрещенные символы - это возврат каретки строки управления ASCII, перевод строки и табуляция. Итак, вы смотрите на класс символов из 29 регулярных выражений. Это не так уж и плохо.

Что-то вроде:

$text =~ s/[\x00-\x08 \x0B \x0C \x0E-\x19]//g;

должен сделать это.

Ага. Это в значительной степени то, что я в итоге сделал. AndrewR
Я должен признать, что отправлял сообщения только после того, как искал CPAN, потому что был убежден, что RE должен быть где-то в Regexp :: Common!
7

# #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;
Это решение, основанное на регулярных выражениях, не работает. Увидетьmy answer ниже.
3

от конкатенации строк, простых шаблонов и т. Д.), То вам следует позаботиться об этом. Нет смысла изобретать велосипед.

XML::LibXML XML::Twig XML::Smart XML::Simple etc
@ Дэвид: эти библиотеки просто убирают управляющие символы из входящей строки?
@newt: я не совсем уверен, что вы имеете в виду под «этой проблемой». Я вижу, что XML :: LibXML удаляет & quot; незаконно & quot; символы, кроме nul, который он рассматривает как конец данных :(
newt, это смысл использования библиотеки XML в первую очередь.
Насколько мне известно, XML :: LibXML ничего не делает для текстового содержимого узла, кроме как отклоняет его, если он содержит недопустимые символы. Я был бы удивлен, если бы другие библиотеки что-нибудь сделали.
Конечно, это так, но он спрашивал о том, как обеспечить, чтобы он не получил эту проблему, гарантируя, что текстовое содержимое не содержит недопустимых символов.
0

регулярное выражение найти и заменить все управляющие символы в вашем фрагменте текста, заменяя их пробелом или удаляя их вообще

# Replace all control characters with a space
$text =~ s/[[:cntrl:]]/ /g;

# or remove them
$text =~ s/[[:cntrl:]]//g;
... который также удаляет переводы строки - так что не очень полезно :) AndrewR
Ой, не думал о переводе строки. Тогда ответ newt кажется нормальным для того, что вы пытаетесь сделать.
0

имволов, например, \ cH будет совпадать с \ cL или \ x08 и \ x0C, и совпадать с backspace и Formfeed соответственно.

4

Перевести этоlot быстрее, чем замена регулярных выражений. Особенно, если все, что вы хотите сделать, удалить символы. Используя набор 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 //, потому что он настолько ограничен, но это, безусловно, подходящее применение.
Да, это намного быстрее, но 287770 / с - это достаточно быстро.
Я тоже. У меня практически никогда нет необходимости в урезанных способностях тр. Но если меня не волнует, где находится персонаж, я буду использовать его отныне, хотя я не уверен, насколько вероятно, что я столкнусь с этим случаем.
0

но он и Ньют сделали небольшую ошибку, перевернув диапазон допустимых символов спецификации 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;

Похожие вопросы