Charset com tudo incluído para evitar “java.nio.charset.MalformedInputException: Input length = 1”?


93

Estou criando um programa simples de contagem de palavras em Java que lê os arquivos baseados em texto de um diretório.

No entanto, continuo recebendo o erro:

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

desta linha de código:

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

Sei que provavelmente entendi isso porque usei um Charsetque não incluía alguns dos caracteres nos arquivos de texto, alguns dos quais incluíam caracteres de outros idiomas. Mas eu quero incluir esses personagens.

Posteriormente, aprendi nos JavaDocs que o Charseté opcional e usado apenas para uma leitura mais eficiente dos arquivos, então mudei o código para:

BufferedReader reader = Files.newBufferedReader(file);

Mas alguns arquivos ainda jogar a MalformedInputException. Não sei por quê.

Eu queria saber se existe um all-inclusive Charsetque me permitirá ler arquivos de texto com muitos tipos diferentes de caracteres ?

Obrigado.

Respostas:


79

Você provavelmente deseja ter uma lista de codificações com suporte. Para cada arquivo, tente cada codificação por vez, talvez começando com UTF-8. Cada vez que você pegar o MalformedInputException, tente a próxima codificação.


43
Eu tentei ISO-8859-1e funciona bem. Acho que é para personagens europeus, o que é bom. Ainda não sei por UTF-16que não funciona, no entanto.
Jonathan Lam,

1
Se você tiver o Notepad ++, você pode tentar abrir o arquivo de texto e ele lhe dirá a codificação do arquivo no Menu. Você pode então adaptar o código corretamente se sempre obtiver o arquivo da mesma fonte.
JGFMK

@JonathanLam Bem, porque se ele está codificado com ISO-8859-1, então é não UTF-16 . Essas codificações são completamente diferentes. Um arquivo não pode ser ambos.
Dawood ibn Kareem

@DawoodsaysreinstateMonica Acredito que quis dizer que fiquei surpreso que o UTF-16 não funcionou tão bem como um pega-tudo para personagens europeus como ISO-8859-1 parece funcionar. Mas obrigado pela informação (mesmo que seis anos depois): P
Jonathan Lam

Certo. UTF-16 contém todos os personagens europeus. Mas eles são representados de forma diferente do ISO-8859-1. No ISO-8859-1, todos os caracteres são representados com apenas 8 bits, portanto, você está limitado a 256 caracteres possíveis. No UTF-16, a maioria dos caracteres são representados com 16 bits e alguns caracteres são representados com 32 bits. Portanto, há muito mais caracteres possíveis em UTF-16, mas um arquivo ISO-8859-1 exigirá apenas metade do espaço que os mesmos dados usariam em UTF-16.
Dawood ibn Kareem

39

Criando BufferedReader de Files.newBufferedReader

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

ao executar o aplicativo, ele pode lançar a seguinte exceção:

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

Mas

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

funciona bem.

A diferença é que o primeiro usa a ação padrão CharsetDecoder.

A ação padrão para erros de entrada malformados e caracteres não mapeáveis ​​é relatar los.

enquanto o último usa a ação REPLACE.

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

27

ISO-8859-1 é um conjunto de caracteres completo, no sentido de que é garantido que não lançará MalformedInputException. Portanto, é bom para depuração, mesmo se sua entrada não estiver neste conjunto de caracteres. Assim:-

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

Eu tinha alguns caracteres de aspas duplas à direita / aspas duplas à esquerda em minha entrada e tanto US-ASCII quanto UTF-8 exibiram MalformedInputException neles, mas ISO-8859-1 funcionou.


6

Eu também encontrei essa exceção com mensagem de erro,

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)

e descobri que algum bug estranho ocorre ao tentar usar

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

para escrever uma string "orazg 54" convertida de um tipo genérico em uma classe.

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

Esta string tem comprimento 9 contendo caracteres com os seguintes pontos de código:

111 114 97 122 103 9 53 52 10

No entanto, se BufferedWriter na classe for substituído por:

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

ele pode escrever com sucesso esta String sem exceções. Além disso, se eu escrever a mesma string criada a partir dos caracteres, ainda funcionará bem.

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 encontrei nenhuma exceção ao usar o primeiro BufferedWriter para escrever quaisquer Strings. É um bug estranho que ocorre no BufferedWriter criado a partir de java.nio.file.Files.newBufferedWriter (caminho, opções)


1
Isso é um pouco fora do tópico, já que o OP falava de ler, em vez de escrever. Tive um problema semelhante devido ao BufferedWriter.write (int) - que trata esse int como um caractere e o grava diretamente no fluxo. A solução alternativa é convertê-lo manualmente em string e depois escrever.
malaverdiere

Esta é uma resposta tristemente votada, Realmente bom trabalho, Tom. Estou me perguntando se isso foi resolvido em versões posteriores do Java.
Ryboflavin


3

Escrevi o seguinte para imprimir uma lista de resultados para padronizar com base nos conjuntos de caracteres disponíveis. Observe que também informa qual linha falha a partir de um número de linha com base em 0, caso você esteja solucionando o caractere 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);
    }
}

3

tente isso .. eu tive o mesmo problema, a implementação funcionou para mim

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

em seguida, use o Reader onde quiser.

anterior:

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();
    }

0

Bem, o problema é que Files.newBufferedReader(Path path)é implementado assim:

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

então, basicamente, não há nenhum ponto em especificar, a UTF-8menos que você queira ser descritivo em seu código. Se você quiser tentar um conjunto de caracteres "mais amplo", pode tentar StandardCharsets.UTF_16, mas não pode ter 100% de certeza de obter todos os caracteres possíveis de qualquer maneira.


-1

você pode tentar algo assim, ou apenas copiar e colar a parte abaixo.

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 ++;
    }
}

O manipulador de exceções pode potencialmente fazer o while(exception)loop para sempre se nunca encontrar um conjunto de caracteres de trabalho na matriz. O manipulador de exceção deve relançar se o final da matriz for alcançado e nenhum conjunto de caracteres de trabalho for encontrado. Além disso, no momento em que escrevo, essa resposta tinha "-2" votos. Eu votei positivamente para "-1". Acho que a razão pela qual obteve votos negativos é porque não há explicação suficiente. Embora eu entenda o que o código faz, outras pessoas podem não entender. Portanto, um comentário como "você pode tentar algo assim" pode não ser apreciado por algumas pessoas.
mvanle

-1

UTF-8 funciona para mim com caracteres poloneses

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.