Удалите неизвестный символ Юникода из текстовых файлов - sed, другие методы bash / shell

9

Мне нужно найти и заменить все вхождения неизвестного символа в некоторых файлах с одинаковыми именами.

Открывая такие файлы с помощью vi, я прочитал код <91> для этого символа. Открыв их с помощью nano, я прочитал «вопросительный знак» в ромбе (черный грохот).

Я хотел бы заменить такой неизвестный символ кавычкой ('). Я пытаюсь много способов без удачи.

Я старался:

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, точка Unicode не имеет смысла в тексте), будет два байта, 0xc2 и 0x91.

Это подразумевает, что ваши замены вообще не работали, но если вы просто заменили 0x91 на 0x27, вы лишите законной силы UTF-8 (у второго байта двухбайтовой последовательности всегда установлен старший бит, т.е. > = 0x80). Это может усложнить ваш анализ, хотя 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 извлекает это из стека аргументов и принимает его как путь к файлу, чтобы использовать для ввода (я считаю, что короткие сценарии легче настраивать и работать с ними, чем с одним вкладышем, кстати). Это накапливается в памяти (хорошо, пока файлы не массивны), тогда perl -iкак исходный файл переименовывается, чтобы избежать условий гонки по месту (см. perldoc perlrun).

Таким образом, вы можете использовать это:

  find . -name "*.txt" -exec whatever.pl {} +
лютик золотистый
источник
это не работает, знак вопроса остается ...
Жасмин
Вы зарегистрировались, hexdump -Cчтобы увидеть, что на самом деле там?
Златовласка
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 - это символ левой кавычки. Эквивалентом в Юникоде является 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 {} +
Стефан Шазелас
источник
это не работает, знак вопроса остается ...
Жасмин
@jasmines Тогда это не так U+0091. Пожалуйста, добавьте вывод LC_ALL=C sed -n l < fileк вопросу.
Стефан Шазелас
кажется, что это \ 221
жасмин
Я не могу конвертировать, потому что это не один файл ... Мне нужно пакетно и рекурсивно искать и заменять.
Жасмин