Как я могу удалить недопустимые символы XML из строк в Perl?
Я ищу, какой стандартный, одобренный и надежный способ удаления недопустимых символов из строк перед записью их в файл XML. Я говорю здесь о блоках текста, содержащих backspace (^ H) и символы формы и т.д.
Для этого должна быть стандартная функция библиотеки/модуля, но я не могу ее найти.
Я использую XML:: LibXML для создания дерева DOM, которое затем сериализую на диск.
Ответы
Ответ 1
Полное регулярное выражение для удаления недопустимых символов 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;
Ответ 2
Как почти все остальные сказали, используйте регулярное выражение. Это честно недостаточно сложно, чтобы добавить в библиотеку. Предварительно обрабатывайте текст с помощью замены.
Ваш комментарий к вышеперечисленным строкам предполагает, что форматирование имеет для вас какое-то значение, поэтому вам, возможно, придется точно определить, что вы хотите заменить некоторыми символами.
Список недопустимых символов четко определен в спецификации XML (здесь - http://www.w3.org/TR/REC-xml/#charsets - например). Запрещенными символами являются возвращаемые символы каретки символов ASCII, строка перевода и вкладка. Итак, вы смотрите на характерный характер символьного символа 29 символов. Это не так уж плохо.
Что-то вроде:
$text =~ s/[\x00-\x08 \x0B \x0C \x0E-\x19]//g;
должен это сделать.
Ответ 3
Я нашел решение, но вместо него он использует команду iconv
.
$ 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}".
Ответ 4
Перевод выполняется намного быстрее, чем замена регулярных выражений. Особенно, если вы хотите удалить символы. Использование набора тэгов:
$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 получил в связи.
Ответ 5
Если вы используете XML-библиотеку для создания своего XML (в отличие от конкатенации строк, простых шаблонов и т.д.), тогда он должен позаботиться об этом для вас. Нет смысла изобретать колесо.
Ответ 6
Хорошо, это, похоже, уже ответили, но что эй. Если вы хотите создавать 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. Это должно сделать трюк.
Ответ 7
Вы можете использовать регулярное выражение для удаления управляющих символов, например, \cH будет соответствовать \cL или\x08 и \x0C соответственно совпадают с backspace и Formfeed.
Ответ 8
Вы можете использовать простой regex, чтобы находить и заменять все управляющие символы в вашем куске текста, заменяя их пробелом или удаляя их в целом -
# Replace all control characters with a space
$text =~ s/[[:cntrl:]]/ /g;
# or remove them
$text =~ s/[[:cntrl:]]//g;
Ответ 9
Я не выполнял большую работу с 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-модуль; см., например, этот ответ.
Ответ 10
Axeman прямо об использовании tr, но он и ньют сделали небольшую ошибку, обращая диапазон спецификаций 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;