2017-02-07 14 views
3

Моя проблема заключается в том, что у меня есть файл данных, содержащий UTF-8, большая часть которого действительна и должна храниться, но некоторые из них имеют случайный «мусор», UTF-8, а именно в диапазоне 0xf0 - 0xff. Пример шестнадцатеричного для плохих данных можно увидеть нижеСкрипт для полосы диапазонов символов UTF-8 из файла

f4 80 80 ab f4 80 80 b6 f4 80 80 
a5 f4 80 80 a6 f4 80 80 83 f4 80 80 b6 f4 80 81 
84 f4 80 81 98 f4 80 81 87 f4 80 81 8c f4 

Я пытаюсь написать PERL скрипт, который будет выполнять поиск и замену для символов, которые первый байт находится в диапазоне 0xf0 - 0xff. На странице this website кодовая страница указана как личное использование.

Моих существующих попытки либо не делать ничего, или только были в состоянии удалить первые байты многобайтового символа а, например, как perl -CSD -pi.orig -e 's/[\x{f4}-\x{ff}]/?/g' Запуск PERL v5.12.5

Я не очень эксперт Perl, ни эксперт utf-8. Я также открыт для этого в ruby ​​/ python/C++ (98)/независимо от того, насколько он относительно переносится в Linux-окне.

Вот ссылка на фрагмент данных мусора. http://pastebin.com/LR0StPHu

+0

Теперь мой вопрос: как мне получить демо-данные в свой код? : D – simbabque

+0

@simbabque Pastbin для вас http://pastebin.com/LR0StPHu –

ответ

5

Хорошо, давайте не будем смешивать несколько вещей.

UTF-8 символы, первый байт которых 0xf0 имеют длину в четыре байта, что вам больше всего нужно, чтобы закодировать юридический символ Юникода. Поскольку для более чем 94% возможного диапазона Unicode требуется этот четвертый байт, 0xf0 не привязывается к какой-либо одной кодовой странице и, конечно же, не относится к частным областям.

Такие символы являются за пределами Basic Multilingual Plane. Но это отличается от недействительности или частного использования; это просто означает, что их кодовые точки больше U + FFFF (десятичное значение 65 535).

Если вы хотите, чтобы исключить все символы вне BMP, вы должны искать для тех, соответствующих данного регулярного выражения:

[\x{10000}-\x{10FFFF}] 

, который использует \x{ ... } интерполяции синтаксис Perl, чтобы включать в себя символы их шестнадцатеричного код точечное значение. Если вы на самом деле используете Perl, то для удобства использования вы можете поместить регулярное выражение в переменную (используя конструкцию quote-regex qr( ... ), так как голой слэш немедленно попытается сопоставить регулярное выражение с $_ при назначении время):

my $not_bmp = qr([\x{10000}-\x{10FFFF}]); 

Но, опять же, удаление символов, соответствующих этому регулярное выражение устраняет более 94% от возможных символов Юникода, поэтому убедитесь, что это то, что вы хотите.

Если вы действительно хотите исключить персональные символы, некоторые из которых являются внутри BMP - просто исключите эти диапазоны. С Perl или Python или любым другим языком, поддерживающим UTF-8, вам не нужно беспокоиться о байтах; просто проверьте коды.

Как Wikipedia скажет вам, три частного использования Области в этих диапазонах кодовых точек:

  • U + E000..U + F8FF
  • U + F0000..U + FFFFF
  • U + 100000..U + 10FFFF

Таким образом, соответствующий Perl регулярное выражение выглядит следующим образом:

my $pua = qr([\x{e000}-\x{f8ff}\x{f0000}-\x{fffff}\x{100000}-\x{10ffff}]); 

Многие другие языки имеют аналогичную поддержку Unicode (сопоставление с символами UTF-8, включая символы в строке по кодовой точке и т. Д.). Например, вот Руби, которая в основном отличается использованием \u{...} вместо \x{...} для интерполяции:

not_bmp = %r([\u{10000}-\u{10FFFF}]) 
pua = %r([\u{e000}-\u{f8ff}\u{f0000}-\u{fffff}\u{100000}-\u{10ffff}]) 

Python \u ускользает работают только с ровно четыре шестнадцатеричных цифр, но если у вас есть Python3 - или python2 скомпилирован в широкий режим - вы можете использовать капитал \U, который занимает ровно восемь (нет поддержки переменной длины через { ...}, как Perl и Руби есть):

not_bmp = re.compile(u'[\U00010000-\U0010ffff]') 
pua = re.compile(u'[\ue000-\uf8ff\U000f0000-\U000fffff\U00100000-\U0010ffff]') 
+0

Я провел некоторое исследование по BMP, и, похоже, именно то, о чем я забочусь о полученных данных. Любые данные за пределами этого, неправильные или иные, являются данными, которые мы не хотим хранить. –

+0

Чтобы дать немного контекста, мы сталкиваемся с проблемами с данными при отправке этих данных из файла на SQL Server, поскольку SQL-сервер делает преобразование в UTF-16 для всех данных Unicode, а наш конкретный драйвер имеет некоторые фатальные ошибки, когда он не может правильно настроить кодировку. Эти данные идут с UTF-16 машин Windows -> файлы UTF-8 -> UTF-16 SQL Server -> файлы UTf-16 -> файлы UTF-8 -> UTF-16 SQL Server. Это чертовски беспорядок. Спасибо за исправление моего слабого понимания UTF, это именно то, что я искал. –

+2

Похоже, что-то по пути на самом деле не поддерживает UTF-16, а только UCS-2. В любом случае, рад, что я мог бы помочь. –

3

Вам необходимо работать с символами, а не с байтами.

Если у вас есть данные внутри вашего кода, и вы используете прагму use utf8, чтобы сообщить Perl, что исходный код вашей программы находится в utf8. Мы делаем это для примера, чтобы вы могли скопировать/вставить мой код.

Вы можете выполнить замену строки с помощью \x{} escape sequence в классе символов []. Они могут использоваться как в диапазонах, так и отдельно.

use utf8; 

my $foo = "asfd ☃ Բարեւ ສະບາຍດີ"; 
$foo =~ s/[\x{10002b}\x{100036}]//g; 
CORE::say $foo; 

Это Выведет:

asfd ☃ Բարեւ ສະບາຍດີ 

(Там же широкий характер в печати предупреждения, но давайте игнорировать, что это потому, что мой STDOUT не открывается должным образом).

Два символа, которые я заменил \x{10002b}\x{100036}, являются первыми двумя символами в ваших данных примера. Шрифт, который я использую в своей IDE, показывает ординалы символов, для которых у него нет глифов, поэтому мне легко сказать, что представляют собой эти символы.

my font shows character ordinals

Эти символы из Supplementary Private Use Area-B. (Wikipedia)

16 НСД-B U + 100000..U + 10FFFF Дополнительное Частное использование Area-B 65536 65534 Неизвестный

Таким образом, мы можем также сделать выбор.

my $foo = "asfd ☃ Բարեւ ສະບາຍດີ"; 
$foo =~ s/[\x{100000}-\x{10ffff}]//g; 
CORE::say $foo; 

Выход:

asfd ☃ Բարեւ ສະບາຍດີ 

Чтобы получить все частного пользования Areas, вам нужно включить три диапазона, которые перечислены here.

/[\x{E000}-\x{F8FF}\x{F0_000}-\x{FF_FFD}\x{100_000}-\x{10f_fff}]//g; 
1

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

s/\ р {Private_Use} // г

perluniprops является файл стручок, который дает все свойства Unicode. Если вы хотите использовать только те области использования, которые находятся выше BMP, вы можете проконсультироваться с ним (grepping для Private), чтобы найти, как их сопоставить.

 Смежные вопросы

  • Нет связанных вопросов^_^