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.

Jonathan Lam
fonte

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.

Dawood ibn Kareem
fonte
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)
Xin Wang
fonte
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.

Tim Cooper
fonte
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)

Tom
fonte
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
5

ISO_8859_1 Funcionou para mim! Eu estava lendo um arquivo de texto com valores separados por vírgula

Shahid Hussain Abbasi
fonte
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);
    }
}
EngineerWithJava54321
fonte
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();
    }
Vin
fonte
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.

Francesco Foresti
fonte
-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 ++;
    }
}
Pengxiang
fonte
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

Adriano
fonte