Фильтрация недействительных utf8

У меня есть текстовый файл в неизвестном или смешанном кодировании. Я хочу видеть строки, содержащие последовательность байтов, которая недопустима UTF-8 (путем подачи текстового файла в какую-либо программу). Эквивалентно, я хочу отфильтровать строки, которые являются действительными UTF-8. Другими словами, я ищу grep [ notutf8 ] .

Идеальное решение было бы портативным, коротким и обобщаемым для других кодировок, но если вы считаете, что лучший способ – испечь в определении UTF-8 , продолжайте.

6 Solutions collect form web for “Фильтрация недействительных utf8”

Если вы хотите использовать grep , вы можете сделать:

 grep -axv '.*' file 

в локалях UTF-8, чтобы получить строки, имеющие хотя бы недействительную последовательность UTF-8 (это работает, по крайней мере, с GNU Grep).

Я думаю, вы, вероятно, хотите iconv . Он предназначен для преобразования между наборами кодов и поддерживает абсурдное количество форматов. Например, чтобы удалить все, что недействительно в UTF-8, вы можете использовать:

iconv -c -t UTF-8 < input.txt > output.txt

Без опции -c он будет сообщать о проблемах при преобразовании в stderr, поэтому с помощью направления процесса вы можете сохранить список из них. Другим способом было бы удалить материал, не относящийся к UTF8, а затем

diff input.txt output.txt

для списка изменений.

Изменить: я исправил опечатку в регулярном выражении. Для этого ему понадобилось «\ x80` не \ 80 .

Регулярное выражение для фильтрации недопустимых форм UTF-8 для строгого соблюдения UTF-8 выглядит следующим образом

 perl -l -ne '/ ^( ([\x00-\x7F]) # 1-byte pattern |([\xC2-\xDF][\x80-\xBF]) # 2-byte pattern |((([\xE0][\xA0-\xBF])|([\xED][\x80-\x9F])|([\xE1-\xEC\xEE-\xEF][\x80-\xBF]))([\x80-\xBF])) # 3-byte pattern |((([\xF0][\x90-\xBF])|([\xF1-\xF3][\x80-\xBF])|([\xF4][\x80-\x8F]))([\x80-\xBF]{2})) # 4-byte pattern )*$ /x or print' 

Выход (из ключевых строк. Из теста 1 ):

 Codepoint ========= 00001000 Test=1 mode=strict valid,invalid,fail=(1000,0,0) 0000E000 Test=1 mode=strict valid,invalid,fail=(D800,800,0) 0010FFFF mode=strict test-return=(0,0) valid,invalid,fail=(10F800,800,0) элемент Codepoint ========= 00001000 Test=1 mode=strict valid,invalid,fail=(1000,0,0) 0000E000 Test=1 mode=strict valid,invalid,fail=(D800,800,0) 0010FFFF mode=strict test-return=(0,0) valid,invalid,fail=(10F800,800,0) 

В. Как создать тестовые данные для проверки регулярного выражения, которое фильтрует недействительный Unicode?
A. Создайте свой собственный алгоритм тестирования UTF-8 и нарушите его правила …
Catch-22 .. Но тогда, как вы тогда тестируете свой тестовый алгоритм?

Регулярное выражение, выше, было протестировано (с использованием iconv в качестве ссылки) для каждого целочисленного значения от 0x00000 до 0x10FFFF .. Это верхнее значение является максимальным целым значением кодека Unicode

  • В ноябре 2003 года UTF-8 был ограничен RFC 3629 до четырех байтов, охватывающих только диапазон U + 0000 до U + 10FFFF, чтобы соответствовать ограничениям кодировки символов UTF-16. `

Согласно этой странице Википедии UTF-8 .

  • UTF-8 кодирует каждую из 1112 064 кодовых точек в наборе символов Unicode, используя от одного до четырех 8-битных байтов

Этот номер (1,112,064) соответствует диапазону от 0x000000 до 0x10F7FF , что на 0x10F7FF меньше фактического максимального целочисленного значения для наивысшего кодового 0x10FFFF Unicode: 0x10FFFF

Этот блок целых чисел отсутствует в спектре Unicode Codepoints из-за необходимости кодирования UTF-16 за пределы его первоначального намерения дизайна через систему, называемую суррогатными парами . Блок с целыми числами 0x0800 зарезервирован для использования UTF-16 .. Этот блок охватывает диапазон от 0x00D800 до 0x00DFFF . Ни один из этих параметров не является законным значением Unicode и поэтому является недопустимым значением UTF-8.

В тесте 1 regex было проверено на каждое число в диапазоне Unicode Codepoints, и оно точно соответствует результатам iconv .. т.е. 0x010F7FF допустимые значения и 0x000800 недопустимые значения.

Однако теперь возникает проблема: * Как регулярное выражение обрабатывает значение Out-Of-Range UTF-8 Value; выше 0x010FFFF (UTF-8 может расширяться до 6 байтов, с максимальным целочисленным значением 0x7FFFFFFF ?
Чтобы сгенерировать необходимые * байтовые значения UIF-8 без юникода , я использовал следующую команду:

  perl -C -e 'print chr 0x'$hexUTF32BE 

Чтобы проверить их достоверность (в некотором роде), я использовал регулярное выражение Gilles' UTF-8 …

  perl -l -ne '/ ^( [\000-\177] # 1-byte pattern |[\300-\337][\200-\277] # 2-byte pattern |[\340-\357][\200-\277]{2} # 3-byte pattern |[\360-\367][\200-\277]{3} # 4-byte pattern |[\370-\373][\200-\277]{4} # 5-byte pattern |[\374-\375][\200-\277]{5} # 6-byte pattern )*$ /x or print' 

Выходной файл 'perl's print chr' соответствует фильтрации регулярного выражения Gilles. Один из них подтверждает достоверность другого. Я не могу использовать iconv потому что он обрабатывает только допустимое Unicode Standard подмножество более широкой (оригинальной) UTF- 8 стандартных …

Уганды довольно большие, поэтому я тестировал верхний диапазон, нижний диапазон и несколько сканов, шаг за шагом, таких как 11111, 13579, 33333, 53441 … Результаты все совпадают, так что теперь все, что осталось, – проверить регулярное выражение на эти значения UTF-8 в соответствии с их пределами (недействительными для Unicode и, следовательно, также недействительными для самого строгого UTF-8).


Вот тестовые модули:

 [[ "$(locale charmap)" != "UTF-8" ]] && { echo "ERROR: locale must be UTF-8, but it is $(locale charmap)"; exit 1; } # Testing the UTF-8 regex # # Tests to check that the observed byte-ranges (above) have # been accurately observed and included in the test code and final regex. # ========================================================================= : 2 bytes; B2=0 # run-test=1 do-not-test=0 : 3 bytes; B3=0 # run-test=1 do-not-test=0 : 4 bytes; B4=0 # run-test=1 do-not-test=0 : regex; Rx=1 # run-test=1 do-not-test=0 ((strict=16)); mode[$strict]=strict # iconv -f UTF-16BE then iconv -f UTF-32BE beyond 0xFFFF) (( lax=32)); mode[$lax]=lax # iconv -f UTF-32BE only) # modebits=$strict # UTF-8, in relation to UTF-16 has invalid values # modebits=$strict automatically shifts to modebits=$lax # when the tested integer exceeds 0xFFFF # modebits=$lax # UTF-8, in relation to UTF-32, has no restrictione # Test 1 Sequentially tests a range of Big-Endian integers # * Unicode Codepoints are a subset ofBig-Endian integers # ( based on 'iconv' -f UTF-32BE -f UTF-8 ) # Note: strict UTF-8 has a few quirks because of UTF-16 # Set modebits=16 to "strictly" test the low range Test=1; modebits=$strict # Test=2; modebits=$lax # Test=3 mode3wlo=$(( 1*4)) # minimum chars * 4 ( '4' is for UTF-32BE ) mode3whi=$((10*4)) # minimum chars * 4 ( '4' is for UTF-32BE ) ######################################################################### # 1 byte UTF-8 values: Nothing to do; no complexities. ######################################################################### # 2 Byte UTF-8 values: Verifying that I've got the right range values. if ((B2==1)) ; then echo "# Test 2 bytes for Valid UTF-8 values: ie. values which are in range" # ========================================================================= time \ for d1 in {194..223} ;do # bin oct hex dec # lo 11000010 302 C2 194 # hi 11011111 337 DF 223 B2b1=$(printf "%0.2X" $d1) # for d2 in {128..191} ;do # bin oct hex dec # lo 10000000 200 80 128 # hi 10111111 277 BF 191 B2b2=$(printf "%0.2X" $d2) # echo -n "${B2b1}${B2b2}" | xxd -p -u -r | iconv -f UTF-8 >/dev/null || { echo "ERROR: Invalid UTF-8 found: ${B2b1}${B2b2}"; exit 20; } # done done echo # Now do a negated test.. This takes longer, because there are more values. echo "# Test 2 bytes for Invalid values: ie. values which are out of range" # ========================================================================= # Note: 'iconv' will treat a leading \x00-\x7F as a valid leading single, # so this negated test primes the first UTF-8 byte with values starting at \x80 time \ for d1 in {128..193} {224..255} ;do #for d1 in {128..194} {224..255} ;do # force a valid UTF-8 (needs $B2b2) B2b1=$(printf "%0.2X" $d1) # for d2 in {0..127} {192..255} ;do #for d2 in {0..128} {192..255} ;do # force a valid UTF-8 (needs $B2b1) B2b2=$(printf "%0.2X" $d2) # echo -n "${B2b1}${B2b2}" | xxd -p -u -r | iconv -f UTF-8 2>/dev/null && { echo "ERROR: VALID UTF-8 found: ${B2b1}${B2b2}"; exit 21; } # done done echo fi ######################################################################### # 3 Byte UTF-8 values: Verifying that I've got the right range values. if ((B3==1)) ; then echo "# Test 3 bytes for Valid UTF-8 values: ie. values which are in range" # ======================================================================== time \ for d1 in {224..239} ;do # bin oct hex dec # lo 11100000 340 E0 224 # hi 11101111 357 EF 239 B3b1=$(printf "%0.2X" $d1) # if [[ $B3b1 == "E0" ]] ; then B3b2range="$(echo {160..191})" # bin oct hex dec # lo 10100000 240 A0 160 # hi 10111111 277 BF 191 elif [[ $B3b1 == "ED" ]] ; then B3b2range="$(echo {128..159})" # bin oct hex dec # lo 10000000 200 80 128 # hi 10011111 237 9F 159 else B3b2range="$(echo {128..191})" # bin oct hex dec # lo 10000000 200 80 128 # hi 10111111 277 BF 191 fi # for d2 in $B3b2range ;do B3b2=$(printf "%0.2X" $d2) echo "${B3b1} ${B3b2} xx" # for d3 in {128..191} ;do # bin oct hex dec # lo 10000000 200 80 128 # hi 10111111 277 BF 191 B3b3=$(printf "%0.2X" $d3) # echo -n "${B3b1}${B3b2}${B3b3}" | xxd -p -u -r | iconv -f UTF-8 >/dev/null || { echo "ERROR: Invalid UTF-8 found: ${B3b1}${B3b2}${B3b3}"; exit 30; } # done done done echo # Now do a negated test.. This takes longer, because there are more values. echo "# Test 3 bytes for Invalid values: ie. values which are out of range" # ========================================================================= # Note: 'iconv' will treat a leading \x00-\x7F as a valid leading single, # so this negated test primes the first UTF-8 byte with values starting at \x80 # # real 26m28.462s \ # user 27m12.526s | stepping by 2 # sys 13m11.193s / # # real 239m00.836s \ # user 225m11.108s | stepping by 1 # sys 120m00.538s / # time \ for d1 in {128..223..1} {240..255..1} ;do #for d1 in {128..224..1} {239..255..1} ;do # force a valid UTF-8 (needs $B2b2,$B3b3) B3b1=$(printf "%0.2X" $d1) # if [[ $B3b1 == "E0" ]] ; then B3b2range="$(echo {0..159..1} {192..255..1})" #B3b2range="$(> {192..255..1})" # force a valid UTF-8 (needs $B3b1,$B3b3) elif [[ $B3b1 == "ED" ]] ; then B3b2range="$(echo {0..127..1} {160..255..1})" #B3b2range="$(echo {0..128..1} {160..255..1})" # force a valid UTF-8 (needs $B3b1,$B3b3) else B3b2range="$(echo {0..127..1} {192..255..1})" #B3b2range="$(echo {0..128..1} {192..255..1})" # force a valid UTF-8 (needs $B3b1,$B3b3) fi for d2 in $B3b2range ;do B3b2=$(printf "%0.2X" $d2) echo "${B3b1} ${B3b2} xx" # for d3 in {0..127..1} {192..255..1} ;do #for d3 in {0..128..1} {192..255..1} ;do # force a valid UTF-8 (needs $B2b1) B3b3=$(printf "%0.2X" $d3) # echo -n "${B3b1}${B3b2}${B3b3}" | xxd -p -u -r | iconv -f UTF-8 2>/dev/null && { echo "ERROR: VALID UTF-8 found: ${B3b1}${B3b2}${B3b3}"; exit 31; } # done done done echo fi ######################################################################### # Brute force testing in the Astral Plane will take a VERY LONG time.. # Perhaps selective testing is more appropriate, now that the previous tests # have panned out okay... # # 4 Byte UTF-8 values: if ((B4==1)) ; then echo "# Test 4 bytes for Valid UTF-8 values: ie. values which are in range" # ================================================================== # real 58m18.531s \ # user 56m44.317s | # sys 27m29.867s / time \ for d1 in {240..244} ;do # bin oct hex dec # lo 11110000 360 F0 240 # hi 11110100 364 F4 244 -- F4 encodes some values greater than 0x10FFFF; # such a sequence is invalid. B4b1=$(printf "%0.2X" $d1) # if [[ $B4b1 == "F0" ]] ; then B4b2range="$(echo {144..191})" ## f0 90 80 80 to f0 bf bf bf # bin oct hex dec 010000 -- 03FFFF # lo 10010000 220 90 144 # hi 10111111 277 BF 191 # elif [[ $B4b1 == "F4" ]] ; then B4b2range="$(echo {128..143})" ## f4 80 80 80 to f4 8f bf bf # bin oct hex dec 100000 -- 10FFFF # lo 10000000 200 80 128 # hi 10001111 217 8F 143 -- F4 encodes some values greater than 0x10FFFF; # such a sequence is invalid. else B4b2range="$(echo {128..191})" ## fx 80 80 80 to f3 bf bf bf # bin oct hex dec 0C0000 -- 0FFFFF # lo 10000000 200 80 128 0A0000 # hi 10111111 277 BF 191 fi # for d2 in $B4b2range ;do B4b2=$(printf "%0.2X" $d2) # for d3 in {128..191} ;do # bin oct hex dec # lo 10000000 200 80 128 # hi 10111111 277 BF 191 B4b3=$(printf "%0.2X" $d3) echo "${B4b1} ${B4b2} ${B4b3} xx" # for d4 in {128..191} ;do # bin oct hex dec # lo 10000000 200 80 128 # hi 10111111 277 BF 191 B4b4=$(printf "%0.2X" $d4) # echo -n "${B4b1}${B4b2}${B4b3}${B4b4}" | xxd -p -u -r | iconv -f UTF-8 >/dev/null || { echo "ERROR: Invalid UTF-8 found: ${B4b1}${B4b2}${B4b3}${B4b4}"; exit 40; } # done done done done echo "# Test 4 bytes for Valid UTF-8 values: END" echo fi ######################################################################## # There is no test (yet) for negated range values in the astral plane. # # (all negated range values must be invalid) # # I won't bother; This was mainly for me to ge the general feel of # # the tests, and the final test below should flush anything out.. # # Traversing the intire UTF-8 range takes quite a while... # # so no need to do it twice (albeit in a slightly different manner) # ######################################################################## ################################ ### The construction of: #### ### The Regular Expression #### ### (de-construction?) #### ################################ # BYTE 1 BYTE 2 BYTE 3 BYTE 4 # 1: [\x00-\x7F] # =========== # ([\x00-\x7F]) # # 2: [\xC2-\xDF] [\x80-\xBF] # ================================= # ([\xC2-\xDF][\x80-\xBF]) # # 3: [\xE0] [\xA0-\xBF] [\x80-\xBF] # [\xED] [\x80-\x9F] [\x80-\xBF] # [\xE1-\xEC\xEE-\xEF] [\x80-\xBF] [\x80-\xBF] # ============================================== # ((([\xE0][\xA0-\xBF])|([\xED][\x80-\x9F])|([\xE1-\xEC\xEE-\xEF][\x80-\xBF]))([\x80-\xBF])) # # 4 [\xF0] [\x90-\xBF] [\x80-\xBF] [\x80-\xBF] # [\xF1-\xF3] [\x80-\xBF] [\x80-\xBF] [\x80-\xBF] # [\xF4] [\x80-\x8F] [\x80-\xBF] [\x80-\xBF] # =========================================================== # ((([\xF0][\x90-\xBF])|([\xF1-\xF3][\x80-\xBF])|([\xF4][\x80-\x8F]))([\x80-\xBF]{2})) # # The final regex # =============== # 1-4: (([\x00-\x7F])|([\xC2-\xDF][\x80-\xBF])|((([\xE0][\xA0-\xBF])|([\xED][\x80-\x9F])|([\xE1-\xEC\xEE-\xEF][\x80-\xBF]))([\x80-\xBF]))|((([\xF0][\x90-\xBF])|([\xF1-\xF3][\x80-\xBF])|([\xF4][\x80-\x8F]))([\x80-\xBF]{2}))) # 4-1: (((([\xF0][\x90-\xBF])|([\xF1-\xF3][\x80-\xBF])|([\xF4][\x80-\x8F]))([\x80-\xBF]{2}))|((([\xE0][\xA0-\xBF])|([\xED][\x80-\x9F])|([\xE1-\xEC\xEE-\xEF][\x80-\xBF]))([\x80-\xBF]))|([\xC2-\xDF][\x80-\xBF])|([\x00-\x7F])) ####################################################################### # The final Test; for a single character (multi chars to follow) # # Compare the return code of 'iconv' against the 'regex' # # for the full range of 0x000000 to 0x10FFFF # # # # Note; this script has 3 modes: # # Run this test TWICE, set each mode Manually! # # # # 1. Sequentially test every value from 0x000000 to 0x10FFFF # # 2. Throw a spanner into the works! Force random byte patterns # # 2. Throw a spanner into the works! Force random longer strings # # ============================== # # # # Note: The purpose of this routine is to determine if there is any # # difference how 'iconv' and 'regex' handle the same data # # # ####################################################################### if ((Rx==1)) ; then # real 191m34.826s # user 158m24.114s # sys 83m10.676s time { invalCt=0 validCt=0 failCt=0 decBeg=$((0x00110000)) # incement by decimal integer decMax=$((0x7FFFFFFF)) # incement by decimal integer # for ((CPDec=decBeg;CPDec<=decMax;CPDec+=13247)) ;do ((D==1)) && echo "==========================================================" # # Convert decimal integer '$CPDec' to Hex-digits; 6-long (dec2hex) hexUTF32BE=$(printf '%0.8X\n' $CPDec) # hexUTF32BE # progress count if (((CPDec%$((0x1000)))==0)) ;then ((Test>2)) && echo echo "$hexUTF32BE Test=$Test mode=${mode[$modebits]} " fi if ((Test==1 || Test==2 )) then # Test 1. Sequentially test every value from 0x000000 to 0x10FFFF # if ((Test==2)) ; then bits=32 UTF8="$( perl -C -e 'print chr 0x'$hexUTF32BE | perl -l -ne '/^( [\000-\177] | [\300-\337][\200-\277] | [\340-\357][\200-\277]{2} | [\360-\367][\200-\277]{3} | [\370-\373][\200-\277]{4} | [\374-\375][\200-\277]{5} )*$/x and print' |xxd -p )" UTF8="${UTF8%0a}" [[ -n "$UTF8" ]] \ && rcIco32=0 || rcIco32=1 rcIco16= elif ((modebits==strict && CPDec<=$((0xFFFF)))) ;then bits=16 UTF8="$( echo -n "${hexUTF32BE:4}" | xxd -p -u -r | iconv -f UTF-16BE -t UTF-8 2>/dev/null)" \ && rcIco16=0 || rcIco16=1 rcIco32= else bits=32 UTF8="$( echo -n "$hexUTF32BE" | xxd -p -u -r | iconv -f UTF-32BE -t UTF-8 2>/dev/null)" \ && rcIco32=0 || rcIco32=1 rcIco16= fi # echo "1 mode=${mode[$modebits]}-$bits rcIconv: (${rcIco16},${rcIco32}) $hexUTF32BE " # # # if ((${rcIco16}${rcIco32}!=0)) ;then # 'iconv -f UTF-16BE' failed produce a reliable UTF-8 if ((bits==16)) ;then ((D==1)) && echo "bits-$bits rcIconv: error $hexUTF32BE .. 'strict' failed, now trying 'lax'" # iconv failed to create a 'srict' UTF-8 so # try UTF-32BE to get a 'lax' UTF-8 pattern UTF8="$( echo -n "$hexUTF32BE" | xxd -p -u -r | iconv -f UTF-32BE -t UTF-8 2>/dev/null)" \ && rcIco32=0 || rcIco32=1 #echo "2 mode=${mode[$modebits]}-$bits rcIconv: (${rcIco16},${rcIco32}) $hexUTF32BE " if ((rcIco32!=0)) ;then ((D==1)) && echo -n "bits-$bits rcIconv: Cannot gen UTF-8 for: $hexUTF32BE" rcIco32=1 fi fi fi # echo "3 mode=${mode[$modebits]}-$bits rcIconv: (${rcIco16},${rcIco32}) $hexUTF32BE " # # # if ((rcIco16==0 || rcIco32==0)) ;then # 'strict(16)' OR 'lax(32)'... 'iconv' managed to generate a UTF-8 pattern ((D==1)) && echo -n "bits-$bits rcIconv: pattern* $hexUTF32BE" ((D==1)) && if [[ $bits == "16" && $rcIco32 == "0" ]] ;then echo " .. 'lax' UTF-8 produced a pattern" else echo fi # regex test if ((modebits==strict)) ;then #rxOut="$(echo -n "$UTF8" |perl -l -ne '/^(([\x00-\x7F])|([\xC2-\xDF][\x80-\xBF])|((([\xE0][\xA0-\xBF])|([\xED][\x80-\x9F])|([\xE1-\xEC\xEE-\xEF][\x80-\xBF]))([\x80-\xBF]))|((([\xF0][\x90-\xBF])|([\xF1-\xF3][\x80-\xBF])|([\xF4][\x80-\x8F]))([\x80-\xBF]{2})))*$/ or print' )" rxOut="$(echo -n "$UTF8" | perl -l -ne '/^( ([\x00-\x7F]) # 1-byte pattern |([\xC2-\xDF][\x80-\xBF]) # 2-byte pattern |((([\xE0][\xA0-\xBF])|([\xED][\x80-\x9F])|([\xE1-\xEC\xEE-\xEF][\x80-\xBF]))([\x80-\xBF])) # 3-byte pattern |((([\xF0][\x90-\xBF])|([\xF1-\xF3][\x80-\xBF])|([\xF4][\x80-\x8F]))([\x80-\xBF]{2})) # 4-byte pattern )*$ /x or print' )" else if ((Test==2)) ;then rx="$(echo -n "$UTF8" |perl -l -ne '/^([\000-\177]|[\300-\337][\200-\277]|[\340-\357][\200-\277]{2}|[\360-\367][\200-\277]{3}|[\370-\373][\200-\277]{4}|[\374-\375][\200-\277]{5})*$/ and print')" [[ "$UTF8" != "$rx" ]] && rxOut="$UTF8" || rxOut= rx="$(echo -n "$rx" |sed -e "s/\(..\)/\1 /g")" else rxOut="$(echo -n "$UTF8" |perl -l -ne '/^([\000-\177]|[\300-\337][\200-\277]|[\340-\357][\200-\277]{2}|[\360-\367][\200-\277]{3}|[\370-\373][\200-\277]{4}|[\374-\375][\200-\277]{5})*$/ or print' )" fi fi if [[ "$rxOut" == "" ]] ;then ((D==1)) && echo " rcRegex: ok" rcRegex=0 else ((D==1)) && echo -n "bits-$bits rcRegex: error $hexUTF32BE .. 'strict' failed," ((D==1)) && if [[ "12" == *$Test* ]] ;then echo # " (codepoint) Test $Test" else echo fi rcRegex=1 fi fi # elif [[ $Test == 2 ]] then # Test 2. Throw a randomizing spanner into the works! # Then test the arbitary bytes ASIS # hexLineRand="$(echo -n "$hexUTF32BE" | sed -re "s/(.)(.)(.)(.)(.)(.)(.)(.)/\1\n\2\n\3\n\4\n\5\n\6\n\7\n\8/" | sort -R | tr -d '\n')" # elif [[ $Test == 3 ]] then # Test 3. Test single UTF-16BE bytes in the range 0x00000000 to 0x7FFFFFFF # echo "Test 3 is not properly implemented yet.. Exiting" exit 99 else echo "ERROR: Invalid mode" exit fi # # if ((Test==1 || Test=2)) ;then if ((modebits==strict && CPDec<=$((0xFFFF)))) ;then ((rcIconv=rcIco16)) else ((rcIconv=rcIco32)) fi if ((rcRegex!=rcIconv)) ;then [[ $Test != 1 ]] && echo if ((rcRegex==1)) ;then echo "ERROR: 'regex' ok, but NOT 'iconv': ${hexUTF32BE} " else echo "ERROR: 'iconv' ok, but NOT 'regex': ${hexUTF32BE} " fi ((failCt++)); elif ((rcRegex!=0)) ;then # ((invalCt++)); echo -ne "$hexUTF32BE exit-codes $${rcIco16}${rcIco32}=,$rcRegex\t: $(printf "%0.8X\n" $invalCt)\t$hexLine$(printf "%$(((mode3whi*2)-${#hexLine}))s")\r" ((invalCt++)) else ((validCt++)) fi if ((Test==1)) ;then echo -ne "$hexUTF32BE " "mode=${mode[$modebits]} test-return=($rcIconv,$rcRegex) valid,invalid,fail=($(printf "%X" $validCt),$(printf "%X" $invalCt),$(printf "%X" $failCt)) \r" else echo -ne "$hexUTF32BE $rx mode=${mode[$modebits]} test-return=($rcIconv,$rcRegex) val,inval,fail=($(printf "%X" $validCt),$(printf "%X" $invalCt),$(printf "%X" $failCt))\r" fi fi done } # End time fi exit 

Я нахожу uconv (в пакете icu-devtools в Debian) полезно проверять данные UTF-8:

 $ print '\\xE9 \xe9 \u20ac \ud800\udc00 \U110000' | uconv --callback escape-c -t us \xE9 \xE9 \u20ac \xED\xA0\x80\xED\xB0\x80 \xF4\x90\x80\x80 

( \x s помогает определить недопустимые символы (за исключением ложных положительных, введенных добровольно с буквальным \xE9 выше)).

(много других приятных привычек).

С версии 2.0 у Python была встроенная функция unicode .

 #!/usr/bin/env python2 import sys for line in sys.stdin: try: unicode(line, 'utf-8') except UnicodeDecodeError: sys.stdout.write(line) 

В Python 3 unicode был сложен на str . Он должен быть передан байтовым объектом , здесь базовые объекты buffer для стандартных дескрипторов .

 #!/usr/bin/env python3 import sys for line in sys.stdin.buffer: try: str(line, 'utf-8') except UnicodeDecodeError: sys.stdout.buffer.write(line) 

Я столкнулся с подобной проблемой (подробно в разделе «Контекст») и пришел со следующим решением ftfy_line_by_line.py :

 #!/usr/bin/env python3 import ftfy, sys with open(sys.argv[1], mode='rt', encoding='utf8', errors='replace') as f: for line in f: sys.stdout.buffer.write(ftfy.fix_text(line).encode('utf8', 'replace')) #print(ftfy.fix_text(line).rstrip().decode(encoding="utf-8", errors="replace")) 

Использование encode + replace + ftfy для автоматического исправления Mojibake и других исправлений.

контекст

Я собрал> 10GiB CSV базовых метаданных файловой системы, используя следующий скрипт gen_basic_files_metadata.csv.sh , выполняющий по существу:

 find "${path}" -type f -exec stat --format="%i,%Y,%s,${hostname},%m,%n" "{}" \; 

Проблема, с которой я столкнулся, заключалась в несогласованном кодировании имен файлов в файловых системах, что UnicodeDecodeError к UnicodeDecodeError при дальнейшей обработке приложений python (более конкретно, csvsql ).

Поэтому я применил выше сценарий ftfy, и потребовалось

Обратите внимание, что ftfy довольно медленный, обработка этих> 10GiB взяла:

 real 147m35.182s user 146m14.329s sys 2m8.713s 

в то время как sha256sum для сравнения:

 real 6m28.897s user 1m9.273s sys 0m6.210s 

на Intel (R) Core (TM) i7-3520M CPU @ 2,90 ГГц + 16 ГБ RAM (и данные на внешнем диске)

  • Как установить языковой стандарт cs_CZ.ISO8859-2 на Ubuntu 12.04
  • Как я могу печатать таблицы utf-8 и unicode из терминала?
  • shell script / utility заменить 2 байта unicode в двоичном файле
  • Можно ли отображать математические символы в терминале?
  • Как стереть символы Unicode в терминале?
  • Извлечение только китайских символов
  • Как преобразовать справочную страницу troff с символами UTF-8 (чешский, если быть точным) в PDF
  • Текстовый редактор с выбором размера шрифта для определенных человеческих языков (например, Unicode Block), например, Devanagari
  • GraphViz: как получить UTF-8 и внешние процедуры PostScript?
  • Как изменить кодировку символов терминала
  • Получить ширину отображения строки символов
  • Interesting Posts

    Функция второго grep в `ps | grep -v | grep`

    Как захватить сообщение об ошибке из выполненной команды?

    vsftpd – как разрешить пользователю удалять файлы, добавленные другим пользователем / группой

    Не удается запустить приложение, которое зависит от X как службы systemd

    Что именно отличает пользователя root от каждого другого пользователя?

    Почему я получаю незаконный номер: {1..3}

    Как я могу гарантировать, что настраиваемая переменная среды будет установлена ​​до того, как cron выполнит задачу?

    Необъяснимые логические прерывания и возможные упавшие пакеты в WPA2-Personal LAN

    бесконечность шрифтов и эмуляция шрифтов Windows

    Что отсутствует в моей установке SELinux?

    В сценарии bash, используя условное выражение «или» в выражении «if»

    Вращение файлов журналов во время работы

    Xrdp на Debian 9 различные настройки отображения для удаленного сеанса

    Программирование Asterisk IVR внутри проблемы freepbx, продолжающей программу, когда зависание клерка

    Приложение Vuescan

    Linux и Unix - лучшая ОС в мире.