Xóa ký tự unicode không xác định khỏi textfiles - sed, các phương thức bash / shell khác


9

Tôi cần tìm kiếm và thay thế tất cả các lần xuất hiện của một ký tự không xác định trong một số tệp có cùng tên.

Mở các tệp như vậy bằng vi, tôi đọc mã <91> cho ký tự đó. Mở chúng bằng nano, tôi đọc một "dấu hỏi" trong một viên kim cương (vần đen).

Tôi muốn thay thế ký tự chưa biết như vậy bằng một trích dẫn ('). Tôi đang thử nhiều cách mà không gặp may.

Tôi đã thử:

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



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

EDIT Thêm thông tin về nhân vật:

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

Nếu bạn cần biết thêm xin!


Theo cách nào sed -i "s/\x91/'/g"trên đó filekhông hoạt động?
Stéphane Chazelas

Câu trả lời:


3

Bạn nên có một cái nhìn bằng cách sử dụng hexdump -Cvà tìm các byte xung quanh nó. Giả sử UTF-8, những gì vihiển thị dưới dạng <91>(thập phân 145, một điểm unicode vô nghĩa trong văn bản) sẽ là hai byte, 0xc2 và 0x91.

Điều đó ngụ ý rằng sự thay thế của bạn hoàn toàn không hoạt động, nhưng nếu những gì bạn đã làm chỉ là thay thế 0x91 bằng 0x27, bạn sẽ vô hiệu hóa UTF-8 (byte thứ hai của chuỗi hai byte luôn có tập bit cao, nghĩa là > = 0x80). Điều này có thể làm phức tạp phân tích của bạn, mặc dù visau đó sẽ hiển thị nó như là ?'.

Điều đó nói rằng, tôi đã thử nghiệm điều này và nó hoạt động:

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

Nếu $ARGV[0]tồn tại khi <>được tham chiếu, perl sẽ bật cái này ra khỏi ngăn xếp đối số và lấy nó làm filepath để sử dụng cho đầu vào (Tôi thấy các tập lệnh ngắn dễ điều chỉnh và làm việc hơn một lớp lót, BTW). Điều này tích lũy trong bộ nhớ (miễn là các tệp không lớn), trong khi perl -iđổi tên tệp gốc để tránh các điều kiện cuộc đua chỉnh sửa tại chỗ (xem perldoc perlrun).

Vì vậy, bạn có thể sử dụng:

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

nó không hoạt động, dấu hỏi vẫn còn ...
jasmines

Bạn đã kiểm tra nó hexdump -Cđể xem những gì thực sự ở đó?
goldilocks

3

Nếu đó thực sự là ký tự U + 0091 (0xc2 0x91 trong mã hóa UTF-8) chứ không phải byte 0x91, thì:

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

Sẽ chuyển đổi nó thành '.

Với GNU sed:

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

Biên tập:

Tuy nhiên, trong trường hợp của bạn, tệp không nằm trong UTF-8. Các ký tự UTF-8 là một byte, chỉ dành cho các ký tự ASCII (cho các giá trị 0 đến 0x7F). Các ký tự khác được biểu thị bằng hai hoặc nhiều byte có giá trị lớn hơn 0x7F. Vì vậy, một 0x91byte, không có byte lớn hơn 0x7F xung quanh nó không thể được tìm thấy trong tệp utf-8.

Nhiều khả năng, tệp của bạn nằm trong một bộ ký tự một byte, rất có thể là một số Microsoft như windows-1252 .

Trong windows-1252, 0x91 là ký tự trích dẫn đơn bên trái. Tương đương unicode là U + 2018 mà trong UTF-8 được viết 0xe2 0x80 0x98.

Nếu bạn muốn chuyển đổi tệp của mình thành UTF-8, tốt nhất có lẽ là sử dụng một công cụ chuyên dụng cho việc đó. Giống:

recode windows-1252..utf8 < file

Hoặc là:

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

Hoặc nếu bạn muốn làm điều đó cho mọi 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 {} +

nó không hoạt động, dấu hỏi vẫn còn ...
jasmines

@jasmines Thì không phải là a U+0091. Vui lòng thêm đầu ra của LC_ALL=C sed -n l < filecâu hỏi.
Stéphane Chazelas

nó dường như là \ 221
jasmines

Tôi không thể chuyển đổi vì không phải là một tệp duy nhất ... Tôi cần phải bó và tìm kiếm đệ quy và thay thế.
jasmines
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.