Usuń nieznany znak Unicode z plików tekstowych - sed, inne metody bash / shell

9

Muszę wyszukać i zastąpić wszystkie wystąpienia nieznanego znaku w niektórych plikach o tej samej nazwie.

Otwierając takie pliki za pomocą vi, czytam kod <91> dla tego znaku. Otwierając je nano, przeczytałem „znak zapytania” w rombie (czarny romb).

Chciałbym zastąpić taki nieznany znak cytatem ('). Próbuję na wiele sposobów bez powodzenia.

Próbowałem:

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



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

EDYCJA Więcej informacji o postaci:

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

Jeśli potrzebujesz więcej, zapytaj!

jaśmin
źródło
W jaki sposób robi sed -i "s/\x91/'/g"na tym filenie działać?
Stéphane Chazelas

Odpowiedzi:

3

Powinieneś spojrzeć za pomocą hexdump -Ci znaleźć bajty wokół niego. Zakładając, że UTF-8, to, co vipokazuje jako <91>(dziesiętnie 145, punkt unicode bez znaczenia w tekście), to dwa bajty 0xc2 i 0x91.

Sugeruje to, że twoje podstawienia wcale nie działały, ale jeśli to, co zrobiłeś, to po prostu zastąpiłeś 0x91 wartością 0x27, unieważniłeś UTF-8 (drugi bajt sekwencji dwóch bajtów zawsze ma ustawiony wysoki bit, tj. > = 0x80). Może to skomplikować twoją analizę, chociaż vipowinna to pokazać jako ?'.

Powiedziałem, przetestowałem to i działa:

#!/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;  

Jeśli $ARGV[0]istnieje, gdy <>się do niego odwołuje, perl usuwa go ze stosu argumentów i bierze go jako ścieżkę do pliku wejściowego (uważam, że krótkie skrypty są łatwiejsze do poprawiania i pracy z więcej niż jednym linerem, BTW). Gromadzi się to w pamięci (tak długo, jak pliki nie są ogromne), a perl -izmienia nazwę oryginalnego pliku, aby uniknąć warunków wyścigu edycji na miejscu (patrz perldoc perlrun).

Możesz więc użyć tego:

  find . -name "*.txt" -exec whatever.pl {} +
Złotowłosa
źródło
nie działa, pozostaje znak zapytania ...
jaśmin
Sprawdziłeś to, hexdump -Cco tam jest?
goldilocks
3

Jeśli rzeczywiście jest to znak U + 0091 (0xc2 0x91 w kodowaniu UTF-8), a nie bajt 0x91, to:

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

Przekształciłbym to w '.

Z GNU sed:

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

Edytować:

Jednak w twoim przypadku plik nie znajduje się w UTF-8. Znaki UTF-8 są jednobajtowe, tylko dla znaków ASCII (dla wartości od 0 do 0x7F). Pozostałe znaki są reprezentowane przez dwa lub więcej bajtów, których wartość jest większa niż 0x7F. Więc0x91 bajtu bez bajtu większego niż 0x7F nie można znaleźć w pliku utf-8.

Bardziej prawdopodobne jest, że plik ma jednobajtowy zestaw znaków, najprawdopodobniej taki Microsoft jak Windows-1252 .

W Windows-1252 0x91 to lewy pojedynczy cudzysłów. Odpowiednikiem Unicode jest U + 2018, który w UTF-8 jest zapisany 0xe2 0x80 0x98.

Jeśli chcesz przekonwertować plik na UTF-8, najlepiej jest użyć do tego dedykowanego narzędzia. Lubić:

recode windows-1252..utf8 < file

Lub:

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

Lub jeśli chcesz to zrobić dla każdego 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 {} +
Stéphane Chazelas
źródło
nie działa, pozostaje znak zapytania ...
jaśmin
@jasmines Więc to nie jest U+0091. Dodaj wynik LC_ALL=C sed -n l < filedo pytania.
Stéphane Chazelas
wydaje się, że jest \ 221
jaśmin
Nie mogę przekonwertować, ponieważ nie jest to pojedynczy plik ... Muszę grupować i rekurencyjnie wyszukiwać i zamieniać.
jaśmin