「java.nio.charset.MalformedInputException:入力長= 1」を回避するためのオールインクルーシブの文字セット?


93

私はJavaでディレクトリのテキストベースのファイルを読み取る単純なワードカウントプログラムを作成しています。

ただし、エラーが発生し続けます。

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

このコード行から:

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

Charsetテキストファイルに一部の文字が含まれていないaを使用したため、おそらく他の言語の文字が含まれているので、おそらくこれがわかるでしょう。しかし、私はそれらのキャラクターを含めたいです。

私は後でJavaDocsでこれCharsetがオプションであり、ファイルのより効率的な読み取りにのみ使用されることを学びましたそのため、コードを次のように変更しました。

BufferedReader reader = Files.newBufferedReader(file);

しかし、一部のファイルは依然としてをスローしMalformedInputExceptionます。理由はわかりません。

Charsetさまざまな種類の文字を含むテキストファイルを読み取ることができる包括的なものがあるかどうか疑問に思っていましたか?

ありがとう。

回答:


79

おそらく、サポートされているエンコーディングのリストが必要です。ファイルごとに、エンコーディングを順番に試してください。UTF-8で始まる可能性があります。をキャッチするたびにMalformedInputException、次のエンコーディングを試してください。


43
私が試したところISO-8859-1、うまくいきました。ヨーロッパのキャラクター向けだと思いますが、結構です。なぜUTF-16うまくいかないのか、まだわかりません。
Jonathan Lam

1
Notepad ++をお持ちの場合は、テキストファイルを開いてみると、メニューにファイルのエンコードが表示されます。その後、常に同じソースからファイルを取得する場合は、コードを適切に適応させることができます。
JGFMK

@JonathanLamまあ、それはでエンコードだ場合のでISO-8859-1、それはだではありません UTF-16。これらのエンコーディングは完全に異なります。ファイルを両方にすることはできません。
Dawood ibnカリーム

@DawoodsaysreinstateMonica私は、UTF-16が機能せず、ISO-8859-1のようなヨーロッパの文字のキャッチオールが機能しているように思われたので驚いたと思います。しかし、情報に感謝します(6年後であっても):P
ジョナサンラム

承知しました。UTF-16にはヨーロッパの文字がすべて含まれています。しかし、それらはISO-8859-1とは異なって表現されています。ISO-8859-1では、すべての文字が8ビットのみで表されるため、可能な文字数は256に制限されています。UTF-16では、ほとんどの文字が16ビットで表され、一部の文字は32ビットで表されます。そのため、UTF-16ではより多くの可能な文字がありますが、ISO-8859-1ファイルでは、同じデータがUTF-16で使用する場合の半分のスペースしか必要としません。
Dawood ibnカリーム

39

Files.newBufferedReaderからBufferedReaderを作成する

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

アプリケーションを実行すると、次の例外がスローされる場合があります。

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

だが

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

うまくいきます。

異なる点は、前者はCharsetDecoderのデフォルトアクションを使用することです。

不正な形式の入力およびマップできない文字エラーのデフォルトのアクションは、それらを報告することです。

後者はREPLACEアクションを使用します。

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

27

ISO-8859-1は、MalformedInputExceptionをスローしないことが保証されているという意味で、包括的な文字セットです。したがって、入力がこの文字セットでなくても、デバッグに適しています。そう:-

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

入力にいくつかの二重右引用符/二重左引用符文字があり、US-ASCIIとUTF-8の両方がMalformedInputExceptionをスローしましたが、ISO-8859-1は機能しました。


6

エラーメッセージでこの例外も発生しました。

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)

使用しようとすると奇妙なバグが発生することがわかりました

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

クラスのジェネリック型からキャストされた文字列「orazg 54」キャストを記述します。

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

この文字列は長さが9で、次のコードポイントを持つ文字が含まれています。

111 114 97 122103 9 53 52 10

ただし、クラスのBufferedWriterが次のように置き換えられた場合:

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

例外なくこの文字列を正常に書き込むことができます。また、同じ文字列を文字から作成しても、問題なく動作します。

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

以前は、最初のBufferedWriterを使用して文字列を書き込むときに、例外が発生することはありませんでした。java.nio.file.Files.newBufferedWriter(path、options)から作成されたBufferedWriterに発生する奇妙なバグです。


1
OPが書くことではなく、読むことについて話していたので、これは少し話題から外れています。BufferedWriter.write(int)が原因で同様の問題が発生しました。これは、そのintを文字として扱い、ストリームに直接書き込みます。これを回避するには、手動で文字列に変換してから書き込みます。
malaverdiere 2016年

これは残念ながら投票にかけられていない回答です。本当に素晴らしい作品です。トム。これがJavaの以降のバージョンで解決されているかどうか疑問に思っています。
リボフラビン


3

利用可能な文字セットに基づいて結果のリストを標準出力に出力するために、以下を書きました。また、問題の原因となっている文字をトラブルシューティングしている場合に備えて、0から始まる行番号からどの行が失敗するかがわかります。

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

これを試してください..私は同じ問題がありました、以下の実装は私のために働きました

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

次に、Readerを好きな場所で使用します。

前例:

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

さて、問題は次のFiles.newBufferedReader(Path path)ように実装されていることです:

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

したがって、基本的UTF-8に、コードで説明を必要としない限り、指定しても意味がありません。「より広い」文字セットを試したい場合は、で試すことができますがStandardCharsets.UTF_16、とにかくすべての可能な文字を100%確実に取得することはできません。


-1

あなたはこのようなものを試すことができます、または単に下の部分をコピーして貼り付けてください。

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

例外ハンドラーはwhile(exception)、配列内で機能する文字セットが見つからない場合、ループを永久に作成する可能性があります。配列の終わりに達し、機能する文字セットが見つからない場合、例外ハンドラは再スローする必要があります。また、これを書いている時点で、この回答は「-2」票でした。「-1」に投票しました。反対票になったのは、説明が足りないからだと思います。私はコードが何をするかを理解していますが、他の人はそうではないかもしれません。したがって、「このようなことを試すことができます」などのコメントは、一部の人には理解されない場合があります。
mvanle

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.