Hapus karakter unicode yang tidak dikenal dari file teks - sed, metode bash / shell lainnya

9

Saya perlu mencari dan mengganti semua kemunculan karakter yang tidak dikenal di beberapa file dengan nama yang sama.

Membuka file seperti itu dengan vi, saya membaca kode <91> untuk karakter itu. Membuka mereka dengan nano, saya membaca "tanda tanya" dalam berlian (black rhumble).

Saya ingin mengganti karakter yang tidak dikenal tersebut dengan kutipan ('). Saya mencoba banyak cara tanpa hasil.

Saya mencoba:

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



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

Sunting Informasi lebih lanjut tentang karakter:

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

Jika Anda membutuhkan lebih banyak, tanyakan!

melati
sumber
Dengan cara apa sed -i "s/\x91/'/g"itu filetidak berhasil?
Stéphane Chazelas

Jawaban:

3

Anda harus melihat menggunakan hexdump -Cdan menemukan byte di sekitarnya. Menganggap UTF-8, yang vimenunjukkan sebagai <91>(desimal 145, titik unicode yang tidak berarti dalam teks) akan menjadi dua byte, 0xc2 dan 0x91.

Tersirat bahwa substitusi Anda tidak berfungsi sama sekali, tetapi jika yang Anda lakukan hanya mengganti 0x91 dengan 0x27, Anda akan membatalkan UTF-8 (byte kedua dari urutan dua byte selalu memiliki set bit tinggi, yaitu > = 0x80). Ini mungkin mempersulit analisis Anda, meskipun vikemudian harus menunjukkannya sebagai ?'.

Yang mengatakan, saya menguji ini dan berhasil:

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

Jika $ARGV[0]ada saat <>direferensikan, perl mengeluarkan ini dari tumpukan argumen dan menjadikannya sebagai filepath untuk digunakan untuk input (saya menemukan skrip pendek lebih mudah untuk men-tweak dan bekerja dengan dari satu liners, BTW). Ini terakumulasi dalam memori (baik selama file tidak besar), sedangkan perl -imengganti nama file asli untuk menghindari kondisi balapan sunting di tempat (lihat perldoc perlrun).

Jadi Anda bisa menggunakannya:

  find . -name "*.txt" -exec whatever.pl {} +
goldilocks
sumber
tidak bekerja, tanda tanya tetap ...
jasmines
Apakah Anda memeriksanya hexdump -Cuntuk melihat apa yang sebenarnya ada di sana?
goldilocks
3

Jika memang karakter U + 0091 (0xc2 0x91 dalam pengkodean UTF-8) dan bukan byte 0x91, maka:

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

Akan mengubahnya menjadi '.

Dengan GNU sed:

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

Edit:

Namun, dalam kasus Anda, file tersebut tidak dalam UTF-8. Karakter UTF-8 adalah satu byte, hanya untuk karakter ASCII (untuk nilai 0 hingga 0x7F). Karakter lain diwakili oleh dua atau lebih byte yang nilainya lebih besar dari 0x7F. Jadi satu 0x91byte, tanpa byte lebih besar dari 0x7F di sekitarnya tidak dapat ditemukan dalam file utf-8.

Kemungkinan besar, file Anda dalam satu set karakter byte tunggal, kemungkinan besar beberapa Microsoft seperti windows-1252 .

Di windows-1252, 0x91 adalah karakter kutipan tunggal kiri. Setara unicode adalah U + 2018 yang ditulis dalam UTF-8 0xe2 0x80 0x98.

Jika Anda ingin mengonversi file Anda ke UTF-8, yang terbaik mungkin adalah menggunakan alat khusus untuk itu. Suka:

recode windows-1252..utf8 < file

Atau:

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

Atau jika Anda ingin melakukannya untuk setiap 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
sumber
tidak bekerja, tanda tanya tetap ...
jasmines
@ jasmines Maka itu bukan U+0091. Silakan tambahkan output LC_ALL=C sed -n l < fileke pertanyaan.
Stéphane Chazelas
tampaknya \ 221
jasmines
Saya tidak dapat mengonversi karena bukan satu file ... Saya perlu mengelompokkan dan mencari dan mengganti secara rekursif.
jasmines