Eliminar caracteres Unicode desconocidos de archivos de texto - sed, otros métodos bash / shell

9

Necesito buscar y reemplazar todas las ocurrencias de un personaje desconocido en algunos archivos que tienen el mismo nombre.

Al abrir dichos archivos con vi, leo el código <91> para ese personaje. Al abrirlos con nano, leí un "signo de interrogación" en un diamante (estruendo negro).

Me gustaría reemplazar un personaje tan desconocido con una cita ('). Estoy intentando muchas formas sin suerte.

Lo intenté:

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



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

EDITAR Más información sobre el personaje:

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

¡Si necesitas más, pregunta!

jazmines
fuente
¿De qué manera no funciona sed -i "s/\x91/'/g"eso file?
Stéphane Chazelas

Respuestas:

3

Debería echar un vistazo usando hexdump -Cy encontrar los bytes a su alrededor. Suponiendo UTF-8, lo que se vimuestra como <91>(decimal 145, un punto unicode sin sentido en el texto) sería dos bytes, 0xc2 y 0x91.

Está implícito que sus sustituciones no funcionaron en absoluto, pero si lo que hizo fue reemplazar 0x91 con 0x27, habrá invalidado el UTF-8 (el segundo byte de una secuencia de dos bytes siempre tiene el bit alto establecido, es decir, es > = 0x80). Esto podría complicar su análisis, aunque videbería mostrarlo como ?'.

Dicho esto, probé esto y funciona:

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

Si $ARGV[0]existe cuando <>se hace referencia, perl saca esto de la pila de argumentos y lo toma como una ruta de archivo para usar como entrada (encuentro scripts cortos más fáciles de ajustar y trabajar con un liners, por cierto). Esto se acumula en la memoria (bien siempre y cuando los archivos no sean masivos), mientras que perl -icambia el nombre del archivo original para evitar condiciones de carrera de edición en el lugar (ver perldoc perlrun).

Entonces podrías usar eso:

  find . -name "*.txt" -exec whatever.pl {} +
encerrada dorada
fuente
no funciona, el signo de interrogación permanece ...
jasmines
¿Lo revisaste hexdump -Cpara ver qué hay realmente allí?
Ricitos
3

Si de hecho es el carácter U + 0091 (0xc2 0x91 en la codificación UTF-8) y no el byte 0x91, entonces:

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

Lo convertiría a '.

Con GNU sed:

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

Editar:

Sin embargo, en su caso, el archivo no está en UTF-8. Los caracteres UTF-8 son de un byte, solo para caracteres ASCII (para valores de 0 a 0x7F). Los otros caracteres están representados por dos o más bytes cuyo valor es mayor que 0x7F. Por lo tanto 0x91, no se puede encontrar un byte, sin un byte mayor que 0x7F en un archivo utf-8.

Lo más probable es que su archivo esté en un conjunto de caracteres de un solo byte, muy probablemente algunos de Microsoft como Windows-1252 .

En windows-1252, 0x91 es el carácter de comillas simples a la izquierda. El equivalente unicode es U + 2018, que en UTF-8 está escrito 0xe2 0x80 0x98.

Si desea convertir su archivo a UTF-8, lo mejor es probablemente usar una herramienta dedicada para eso. Me gusta:

recode windows-1252..utf8 < file

O:

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

O si quieres hacerlo por cada 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
fuente
no funciona, el signo de interrogación permanece ...
jasmines
@jasmines Entonces no es un U+0091. Agregue el resultado de LC_ALL=C sed -n l < filea la pregunta.
Stéphane Chazelas
parece ser \ 221
jasmines
No puedo convertir porque no es un solo archivo ... Necesito procesar por lotes y buscar y reemplazar recursivamente.
Jasmines