Conjunto de caracteres todo incluido para evitar "java.nio.charset.MalformedInputException: Input length = 1"?


97

Estoy creando un programa de conteo de palabras simple en Java que lee los archivos basados ​​en texto de un directorio.

Sin embargo, sigo recibiendo el error:

java.nio.charset.MalformedInputException: Input length = 1

de esta línea de código:

BufferedReader reader = Files.newBufferedReader(file,Charset.forName("UTF-8"));

Sé que probablemente entiendo esto porque utilicé un Charsetque no incluía algunos de los caracteres en los archivos de texto, algunos de los cuales incluían caracteres de otros idiomas. Pero quiero incluir esos personajes.

Más tarde supe en JavaDocs que Charsetes opcional y solo se usa para una lectura más eficiente de los archivos, así que cambié el código a:

BufferedReader reader = Files.newBufferedReader(file);

Pero algunos archivos todavía arrojan el MalformedInputException. No sé por qué.

Me preguntaba si hay un todo incluido Charsetque me permita leer archivos de texto con muchos tipos diferentes de caracteres .

Gracias.

Respuestas:


82

Probablemente desee tener una lista de codificaciones compatibles. Para cada archivo, pruebe cada codificación por turno, tal vez comenzando con UTF-8. Cada vez que detecte MalformedInputException, intente con la siguiente codificación.


45
Lo intenté ISO-8859-1y funciona bien. Creo que es para personajes europeos, lo cual está bien. Sin embargo, todavía no sé por qué UTF-16no funciona.
Jonathan Lam

1
Si tiene Notepad ++, puede intentar abrir un archivo de texto y le indicará la codificación del archivo en el Menú. Luego puede adaptar el código en consecuencia si siempre obtiene el archivo de la misma fuente.
JGFMK

@JonathanLam Bueno, porque si está codificado con ISO-8859-1, entonces es no UTF-16 . Estas codificaciones son completamente diferentes. Un archivo no puede ser ambos.
Dawood ibn Kareem

@DawoodsaysreinstateMonica Creo que quise decir que me sorprendió que UTF-16 no funcionara tan bien como un comodín para los caracteres europeos como parece hacerlo ISO-8859-1. Pero gracias por la información (incluso si seis años después): P
Jonathan Lam

Por supuesto. UTF-16 tiene todos los caracteres europeos. Pero están representados de manera diferente a ISO-8859-1. En ISO-8859-1, todos los caracteres se representan con solo 8 bits, por lo que está limitado a 256 caracteres posibles. En UTF-16, la mayoría de los caracteres se representan con 16 bits y algunos caracteres se representan con 32 bits. Por lo tanto, hay muchos más caracteres posibles en UTF-16, pero un archivo ISO-8859-1 solo requerirá la mitad de espacio del que usarían los mismos datos en UTF-16.
Dawood ibn Kareem

41

Creación de BufferedReader a partir de Files.newBufferedReader

Files.newBufferedReader(Paths.get("a.txt"), StandardCharsets.UTF_8);

al ejecutar la aplicación, puede producir la siguiente excepción:

java.nio.charset.MalformedInputException: Input length = 1

Pero

new BufferedReader(new InputStreamReader(new FileInputStream("a.txt"),"utf-8"));

funciona bien.

La diferencia es que, el primero usa la acción predeterminada CharsetDecoder.

La acción predeterminada para errores de caracteres no mapeados y de entrada con formato incorrecto es informarlos .

mientras que este último usa la acción REPLACE.

cs.newDecoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE)

29

ISO-8859-1 es un conjunto de caracteres con todo incluido, en el sentido de que está garantizado que no arrojará MalformedInputException. Por lo tanto, es bueno para la depuración, incluso si su entrada no está en este juego de caracteres. Entonces:-

req.setCharacterEncoding("ISO-8859-1");

Tenía algunos caracteres de comillas dobles a la derecha / comillas dobles a la izquierda en mi entrada, y tanto US-ASCII como UTF-8 arrojaron MalformedInputException en ellos, pero ISO-8859-1 funcionó.


7

También encontré esta excepción con un mensaje de error,

java.nio.charset.MalformedInputException: Input length = 1
at java.nio.charset.CoderResult.throwException(Unknown Source)
at sun.nio.cs.StreamEncoder.implWrite(Unknown Source)
at sun.nio.cs.StreamEncoder.write(Unknown Source)
at java.io.OutputStreamWriter.write(Unknown Source)
at java.io.BufferedWriter.flushBuffer(Unknown Source)
at java.io.BufferedWriter.write(Unknown Source)
at java.io.Writer.write(Unknown Source)

y descubrió que se produce un error extraño al intentar usar

BufferedWriter writer = Files.newBufferedWriter(Paths.get(filePath));

para escribir una cadena "orazg 54" de un tipo genérico en una clase.

//key is of generic type <Key extends Comparable<Key>>
writer.write(item.getKey() + "\t" + item.getValue() + "\n");

Esta cadena tiene una longitud de 9 y contiene caracteres con los siguientes puntos de código:

111114 97122103 9 53 52 10

Sin embargo, si el BufferedWriter de la clase se reemplaza por:

FileOutputStream outputStream = new FileOutputStream(filePath);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));

puede escribir correctamente esta cadena sin excepciones. Además, si escribo la misma cadena creada a partir de los caracteres, todavía funciona bien.

String string = new String(new char[] {111, 114, 97, 122, 103, 9, 53, 52, 10});
BufferedWriter writer = Files.newBufferedWriter(Paths.get("a.txt"));
writer.write(string);
writer.close();

Anteriormente, nunca había encontrado ninguna excepción al usar el primer BufferedWriter para escribir cadenas. Es un error extraño que le ocurre a BufferedWriter creado a partir de java.nio.file.Files.newBufferedWriter (ruta, opciones)


1
Esto es algo fuera de tema, ya que el OP hablaba de leer, en lugar de escribir. Tuve un problema similar debido a BufferedWriter.write (int), que trata ese int como un carácter y lo escribe directamente en la transmisión. La solución es convertirlo manualmente en una cadena y luego escribir.
malaverdiere

Esta es una respuesta lamentablemente poco votada, Muy buen trabajo, Tom. Me pregunto si esto se ha resuelto en versiones posteriores de Java.
Ryboflavin


4

prueba esto ... tuve el mismo problema, la implementación a continuación funcionó para mí

Reader reader = Files.newBufferedReader(Paths.get(<yourfilewithpath>), StandardCharsets.ISO_8859_1);

luego use Reader donde quiera.

foreg:

CsvToBean<anyPojo> csvToBean = null;
    try {
        Reader reader = Files.newBufferedReader(Paths.get(csvFilePath), 
                        StandardCharsets.ISO_8859_1);
        csvToBean = new CsvToBeanBuilder(reader)
                .withType(anyPojo.class)
                .withIgnoreLeadingWhiteSpace(true)
                .withSkipLines(1)
                .build();

    } catch (IOException e) {
        e.printStackTrace();
    }

3

Escribí lo siguiente para imprimir una lista de resultados a la salida estándar basada en los juegos de caracteres disponibles. Tenga en cuenta que también le indica qué línea falla de un número de línea basado en 0 en caso de que esté solucionando el carácter que está causando problemas.

public static void testCharset(String fileName) {
    SortedMap<String, Charset> charsets = Charset.availableCharsets();
    for (String k : charsets.keySet()) {
        int line = 0;
        boolean success = true;
        try (BufferedReader b = Files.newBufferedReader(Paths.get(fileName),charsets.get(k))) {
            while (b.ready()) {
                b.readLine();
                line++;
            }
        } catch (IOException e) {
            success = false;
            System.out.println(k+" failed on line "+line);
        }
        if (success) 
            System.out.println("*************************  Successs "+k);
    }
}

0

Bueno, el problema es que Files.newBufferedReader(Path path)se implementa así:

public static BufferedReader newBufferedReader(Path path) throws IOException {
    return newBufferedReader(path, StandardCharsets.UTF_8);
}

así que básicamente no tiene sentido especificar a UTF-8menos que desee ser descriptivo en su código. Si quieres probar un juego de caracteres "más amplio", puedes intentarlo StandardCharsets.UTF_16, pero no puedes estar 100% seguro de obtener todos los caracteres posibles de todos modos.


-1

puede probar algo como esto, o simplemente copiar y pegar la siguiente pieza.

boolean exception = true;
Charset charset = Charset.defaultCharset(); //Try the default one first.        
int index = 0;

while(exception) {
    try {
        lines = Files.readAllLines(f.toPath(),charset);
          for (String line: lines) {
              line= line.trim();
              if(line.contains(keyword))
                  values.add(line);
              }           
        //No exception, just returns
        exception = false; 
    } catch (IOException e) {
        exception = true;
        //Try the next charset
        if(index<Charset.availableCharsets().values().size())
            charset = (Charset) Charset.availableCharsets().values().toArray()[index];
        index ++;
    }
}

El controlador de excepciones puede potencialmente hacer el while(exception)bucle para siempre si nunca encuentra un juego de caracteres que funcione en la matriz. El controlador de excepciones debería volver a lanzar si se alcanza el final de la matriz y no se encuentra ningún juego de caracteres que funcione. Además, en el momento de redactar este informe, esta respuesta tenía "-2" votos. Lo he votado a "-1". Creo que la razón por la que obtuvo votos negativos es porque no hay una explicación suficiente. Si bien entiendo lo que hace el código, es posible que otras personas no. Por lo tanto, es posible que algunas personas no aprecien un comentario como "puedes probar algo como esto".
mvanle

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.