texto - iconv linux ejemplos
cómo detectar inválido utf8 unicode/binary en un archivo de texto (8)
Necesito detectar el archivo de texto dañado donde hay utf-8 no válido (no ASCII), Unicode o caracteres binarios.
�>t�ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½w�ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½o��������ï¿ï¿½_��������������������o����������������������￿����ß����������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½~�ï¿ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½}���������}w��׿��������������������������������������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½~������������������������������������_������������������������������������������������������������������������������^����ï¿ï¿½s�����������������������������?�������������ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½w�������������ï¿ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½}����������ï¿ï¿½ï¿½ï¿½ï¿½y����������������ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½ï¿½o�������������������������}��
lo que he intentado:
iconv -f utf-8 -t utf-8 -c file.csv
esto convierte un archivo de codificación utf-8 a codificación utf-8 y -c
es para omitir los caracteres no válidos utf-8. Sin embargo, al final esos caracteres ilegales todavía se imprimieron. ¿Hay alguna otra solución en Bash on Linux u otros lenguajes?
Este programa Perl debería eliminar todos los caracteres que no sean ASCII:
foreach $file (@ARGV) {
open(IN, $file);
open(OUT, "> super-temporary-utf8-replacement-file-which-should-never-be-used-EVER");
while (<IN>) {
s/[^[:ascii:]]//g;
print OUT "$_";
}
rename "super-temporary-utf8-replacement-file-which-should-never-be-used-EVER", $file;
}
Lo que hace es tomar los archivos como entrada en la línea de comandos, así:
perl fixutf8.pl foo bar baz
Luego, para cada línea, reemplaza cada instancia de un carácter no ASCII sin nada (eliminación).
A continuación, escribe esta línea modificada en super-temporary-utf8-replacement-file-which-should-never-be-used-EVER
(nombrado para que no modifique ningún otro archivo).
Luego, cambia el nombre del archivo temporal por el original.
Esto acepta TODOS los caracteres ASCII (incluidos DEL, NUL, CR, etc.), en caso de que tenga algún uso especial para ellos. Si solo desea caracteres imprimibles, simplemente reemplace :ascii:
con :print:
en s///
.
¡Espero que esto ayude! Por favor, avíseme si esto no era lo que estaba buscando.
Creo que este programa C debería hacer lo que se requiere. Cuando se le presente un archivo de entrada, imprimirá el desplazamiento del primer inválido (es decir, una secuencia de caracteres que no sea UTF-8) y saldrá con un código de error. Opcionalmente, también puede eliminar todas las secuencias que no sean UTF-8 del archivo de entrada e imprimir el resto.
Tenga en cuenta que solo funcionará en Linux y que debe compilarlo usted mismo, pero eso debería ser sencillo.
Lo que estás viendo está por definición corrompido. Aparentemente, está mostrando el archivo tal como se representa en Latin-1; los tres caracteres � representan los valores de tres bytes 0xEF 0xBF 0xBD. Pero esas son la codificación UTF-8 del CARACTER DE REEMPLAZO Unicode U + FFFD que es el resultado de intentar convertir bytes de una codificación desconocida o indefinida en UTF-8, y que se mostraría correctamente como (si tiene un navegador a partir de este siglo, debería ver algo así como un diamante negro con un signo de interrogación, pero esto también depende de la fuente que esté utilizando, etc.).
Entonces su pregunta sobre "cómo detectar" este fenómeno particular es fácil; el punto de código Unicode U + FFFD es un obsequio irrecuperable, y el único síntoma posible del proceso que está implicando.
Estos no son "Unicode inválido" o "UTF-8 no válido" en el sentido de que esta es una secuencia UTF-8 válida que codifica un punto de código Unicode válido; es solo que la semántica de este punto de código en particular es "este es un carácter de reemplazo para un personaje que no se puede representar correctamente", es decir, entrada no válida.
En cuanto a cómo prevenirlo en primer lugar, la respuesta es realmente simple, pero también bastante poco informativa: es necesario identificar cuándo y cómo se realizó la codificación incorrecta, y corregir el proceso que produjo este resultado no válido.
Para eliminar los caracteres U + FFFD, pruebe algo como
perl -CSD -pe ''s//x{FFFD}//g'' file
pero, una vez más, la solución adecuada es no generar estas salidas erróneas en primer lugar.
(No está revelando la codificación de sus datos de ejemplo. Es posible que tenga una corrupción adicional . Si lo que nos está mostrando es una copia / pegado de la representación UTF-8 de los datos, ha sido "doblemente codificado" "En otras palabras, alguien tomó, ya corrompido, según el texto UTF-8 anterior y le dijo a la computadora que lo convierta de Latin-1 a UTF-8. Deshacer eso es fácil, simplemente conviértalo" atrás " a Latin-1. Lo que obtienes debería ser el dato original de UTF-8 antes de la conversión incorrecta superflua.)
Una solución muy sucia en python 3
import sys
with open ("cur.txt","r",encoding="utf-8") as f:
for i in f:
for c in i:
if(ord(c)<128):
print(c,end="")
El resultado debería ser:
>two_o~}}w~_^s?w}yo}
Suponiendo que tiene su configuración regional en UTF-8, esto funciona bien para reconocer secuencias UTF-8 no válidas:
grep -axv ''.*'' file.txt
Probablemente estoy repitiendo lo que otros ya han dicho. Pero creo que sus caracteres inválidos aún se imprimen porque pueden ser válidos. El Juego de caracteres universales es el intento de hacer referencia a los personajes de uso frecuente en todo el mundo para poder escribir software robusto que no se basa en un juego de caracteres especial.
Así que creo que su problema puede ser uno de los siguientes, en la suposición de que su objetivo general es manejar esta entrada (maliciosa) desde archivos utf en general:
- Hay caracteres utf8 no válidos (mejor denominados secuencias de bytes inválidas , para esto me gustaría referirme al Artículo de Wikipedia correspondiente).
- No hay equivalentes ausentes en su fuente de visualización actual que se sustituyan por un símbolo especial o se muestren como su equivalente ASCII binario (por lo tanto, me gustaría referirme a los siguientes posts posteriores: los caracteres especiales UTF-8 no se muestran arriba ).
Entonces, en mi opinión, tienes dos formas posibles de manejar esto:
- Transformar todos los caracteres de utf8 en algo manejable - fe ASCII - esto se puede hacer fe con
iconv -f utf-8 -t ascii -o file_in_ascii.txt file_in_utf8.txt
. Pero tenga cuidado al transferir de uno el espacio de caracteres más amplio (utf) a uno más pequeño que podría causar la pérdida de datos. - Maneje utf (8) correctamente - así es como el mundo está escribiendo cosas. Si crees que deberías depender de ASCII-chars debido a cualquier paso de posprocesamiento limitado, detente y vuelve a pensar. En la mayoría de los casos, el postprocesador ya es compatible con utf, probablemente sea mejor descubrir cómo utilizarlo. Estás haciendo que tus cosas sean futuras y a prueba de balas.
El manejo de utf puede parecer complicado, los siguientes pasos pueden ayudarte a lograr la preparación para el uso personal:
- Ser capaz de mostrar utf correctamente o asegurarse de que su display-stack (sistema operativo, terminal, etc.) pueda mostrar un subconjunto adecuado de unicode (que, por supuesto, debe satisfacer sus necesidades), esto puede evitar la necesidad de un hex. -editor en muchos casos. Desafortunadamente utf es demasiado grande para venir en una sola fuente, pero un buen punto para comenzar es esto: post: https://.com/questions/586503/complete-monospaced-unicode-font
- Poder filtrar secuencias de bytes inválidas. Y hay muchas formas de lograrlo, este ul-post muestra una gran variedad de estas formas: Filtrado de utf8 no válido : quiero señalar especialmente la cuarta respuesta que sugiere usar
uconv
que le permite establecer un gestor de devolución de llamada no válido secuencias. - Lea un poco más acerca de Unicode.
Me grep
para caracteres no ASCII.
Con GNU grep con pcre (debido a -P
, no disponible siempre. En FreeBSD puede usar pcregrep en el paquete pcre2) que puede hacer:
grep -P "[/x80-/xFF]" file
Referencia en How do grep Para todos los caracteres no ASCII en UNIX . Por lo tanto, de hecho, si solo desea verificar si el archivo contiene caracteres que no son ASCII, simplemente puede decir:
if grep -qP "[/x80-/xFF]" file ; then echo "file contains ascii"; fi
# ^
# silent grep
Para eliminar estos caracteres, puede usar:
sed -i.bak ''s/[/d128-/d255]//g'' file
Esto creará un archivo file.bak
como copia de seguridad, mientras que el file
original tendrá sus caracteres no ASCII eliminados. Referencia en Eliminar caracteres no ascii de csv .
El siguiente programa C detecta caracteres utf8 no válidos. Fue probado y utilizado en un sistema Linux.
/*
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
void usage( void ) {
printf( "Usage: test_utf8 file .../n" );
return;
}
int line_number = 1;
int char_number = 1;
char *file_name = NULL;
void inv_char( void ) {
printf( "%s: line : %d - char %d/n", file_name, line_number, char_number );
return;
}
int main( int argc, char *argv[]) {
FILE *out = NULL;
FILE *fh = NULL;
// printf( "argc: %d/n", argc );
if( argc < 2 ) {
usage();
exit( 1 );
}
// printf( "File: %s/n", argv[1] );
file_name = argv[1];
fh = fopen( file_name, "rb" );
if( ! fh ) {
printf( "Could not open file ''%s''/n", file_name );
exit( 1 );
}
int utf8_type = 1;
int utf8_1 = 0;
int utf8_2 = 0;
int utf8_3 = 0;
int utf8_4 = 0;
int byte_count = 0;
int expected_byte_count = 0;
int cin = fgetc( fh );
while( ! feof( fh ) ) {
switch( utf8_type ) {
case 1:
if( (cin & 0x80) ) {
if( (cin & 0xe0) == 0xc0 ) {
utf8_1 = cin;
utf8_type = 2;
byte_count = 1;
expected_byte_count = 2;
break;
}
if( (cin & 0xf0) == 0xe0 ) {
utf8_1 = cin;
utf8_type = 2;
byte_count = 1;
expected_byte_count = 3;
break;
}
if( (cin & 0xf8) == 0xf0 ) {
utf8_1 = cin;
utf8_type = 2;
byte_count = 1;
expected_byte_count = 4;
break;
}
inv_char();
utf8_type = 1;
break;
}
break;
case 2:
case 3:
case 4:
// printf( "utf8_type - %d/n", utf8_type );
// printf( "%c - %02x/n", cin, cin );
if( (cin & 0xc0) == 0x80 ) {
if( utf8_type == expected_byte_count ) {
utf8_type = 1;
break;
}
byte_count = utf8_type;
utf8_type++;
if( utf8_type == 5 ) {
utf8_type = 1;
}
break;
}
inv_char();
utf8_type = 1;
break;
default:
inv_char();
utf8_type = 1;
break;
}
if( cin == ''/n'' ) {
line_number ++;
char_number = 0;
}
if( out != NULL ) {
fputc( cin, out );
}
// printf( "lno: %d/n", line_number );
cin = fgetc( fh );
char_number++;
}
fclose( fh );
return 0;
}