Видаліть невідомий символ Unicode з текстових файлів - sed, інші методи bash / shell


9

Мені потрібно шукати і замінювати всі випадки невідомого символу в деяких файлах з однаковою назвою.

Відкриваючи такі файли за допомогою vi, я прочитав <91> код цього символу. Відкриваючи їх нано, я прочитав «знак питання» в ромбі (чорний ремінь).

Я хотів би замінити такого невідомого персонажа цитатою ('). Я намагаюся безліч удач.

Я намагався:

find ./ -name filename.txt -exec perl -i~ -pe "s/\x91/'/" {} \;



find ./ -name filename.txt -exec sed -i "s/\x91/'/g" {} \;

РЕДАКЦІЯ Детальніше про персонажа:

Hexadecimal: 91 68 74 74
Decimal: 145 104 116 116
Octal: 221 150 164 164
Binary: 10010001 01101000 01110100 01110100

LC_ALL=C sed -n l < file

\221

Якщо вам потрібно більше, запитайте!


Яким чином робить sed -i "s/\x91/'/g"на цьому fileне працює?
Стефан Шазелас

Відповіді:


3

Ви повинні ознайомитись hexdump -Cі знайти байти навколо нього. Якщо припустити, що UTF-8 viпоказує, що <91>(десятковий 145, точка без единого коду, безглузда в тексті), буде два байти, 0xc2 та 0x91.

Це означає, що ваші заміни взагалі не спрацювали, але якщо ви зробили лише заміну 0x91 на 0x27, ви скасували UTF-8 (другий байт двобайтової послідовності завжди має високий набір бітів, тобто > = 0х80). Це може ускладнити ваш аналіз, хоча viпотім має показати його як ?'.

Однак, я перевірив це, і це працює:

#!/usr/bin/perl
use strict;
use warnings FATAL => qw(all);

my $data = "";
my $file = $ARGV[0];

while (<>) {
    s/\xc2\x91/'/g;
    $data .= $_;
}

open my $out, '>', $file || die "Could not write $file.";
print $out $data;
close $out;  

Якщо $ARGV[0]існує, коли на <>нього посилається, perl вискакує цей стек аргументів і приймає його як файловий шлях для використання для введення даних (я вважаю, що короткі сценарії простіше налаштувати і працювати з більш ніж одними лайнерами, BTW). Це накопичується в пам'яті (прекрасно, поки файли не є масовими), тоді як perl -iперейменовується оригінальний файл, щоб уникнути перегонових умов на місці редагування (див. perldoc perlrun).

Отже, ви можете використовувати це:

  find . -name "*.txt" -exec whatever.pl {} +

це не працює, знак питання залишається ...
жасмини

Ви це зареєстрували, hexdump -Cщоб побачити, що насправді є?
goldilocks

3

Якщо це дійсно символ U + 0091 (0xc2 0x91 в кодуванні UTF-8), а не байт 0x91, то:

PERLIO=:utf8 perl -pi -e "s/\N{U+0091}/'/g" file

Перетворив би його в '.

З GNU sed:

sed -i "s/\xc2\x91/'/" file

Редагувати:

Однак у вашому випадку файл не в UTF-8. Символи UTF-8 - один байт, лише для символів ASCII (для значень від 0 до 0x7F). Інші символи представлені двома або більше байтами, значення яких перевищує 0x7F. Отже, 0x91байт, який не має байта більше 0x7F навколо нього, не можна знайти у файлі utf-8.

Швидше за все, ваш файл має однобайтовий набір символів, швидше за все, такий, як Microsoft, як Windows-1252 .

У Windows-1252 0x91 - лівий символ одиничної цитати. Еквівалент unicode - це U + 2018, який написано в UTF-8 0xe2 0x80 0x98.

Якщо ви хочете конвертувати свій файл в UTF-8, найкраще, мабуть, використовувати для цього спеціальний інструмент. Подібно до:

recode windows-1252..utf8 < file

Або:

iconv -f windows-1252 -t utf-8 < file

Або якщо ви хочете зробити це для кожного filename.txt:

find . -type f -name filename.txt -exec sh -Cc '
  for file do
    mv "$file" "$file~" &&
      iconv -f windows-1252 -t utf-8 < "$file~" > "$file"
  done' sh {} +

це не працює, знак питання залишається ...
жасмини

@jasmine Тоді це не а U+0091. Будь ласка, додайте висновок LC_ALL=C sed -n l < fileдо питання.
Стефан Шазелас

здається, це \ 221
жасмини

Я не можу конвертувати, оскільки це не один файл ... Мені потрібно пакетно та рекурсивно шукати та замінювати.
жасмини
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.