1. ホーム
  2. Java

Java学習5_Java NIOでjava.nio.charset.MalformedInputExceptionが報告されています。入力の長さ = 1 の例外

2022-02-22 10:32:40
Flumeを使用してファイルを読み込んでいるときに、以下のエラーが発生しました。
ERROR ({pool-4-thread-1} SpoolDirectorySource.java[run]:256) [2015-10-30 02:06:36,030] - FATAL: Spool Directory source r1: { spoolDir: /home/ hadoop/flumeSpool }: Uncaught exception in SpoolDirectorySource thread. restart or reconfigure Flume to continue processing.
java.nio.charset.MalformedInputException: Input length = 1
	at java.nio.charset.CoderResult.throwException(CoderResult.java:281)
	at org.apache.flume.serialization.ResettableFileInputStream.readChar(ResettableFileInputStream.java:195)

引き続き原因を探っていくと、読み込んでいるファイルに問題があることがわかりました。

Flumeの使用中に遭遇したわけではありませんが、このエラーについて説明したウェブ上の関連記事を紹介します。
結局、問題は解決しなかったので、今後も方法を模索する必要があります。


今日、Java NIOのChannelとBufferを使ってファイル操作をしているときに、次のような報告がありました。 java.nio.charset.MalformedInputException: 入力の長さ = 1 という例外が発生します。
  1. <スパン java.nio.charset.MalformedInputException: 入力の長さ =  1 <スパン
  2.     at java.nio.charset.CoderResult.throwException(CoderResult.java.CoderResult): 260 )  
  3.     at java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java: 781 )  
  4.     at cn.fuxi.nio.ReadFile.main(ReadFile.java: 37 )  

具体的なJavaのソースコードは以下の通りです。ReadFile.java
  1. <スパン 公開 クラス  ReadFile {  
  2. 公開 静的 ボイド  main(String[]アーギュメント) { {  
  3.         FileInputStream fis;  
  4. トライ  {  
  5.             fis =  新しい  FileInputStream( "a.txt" );  
  6.             FileChannel channel = fis.getChannel();  
  7. // データを繰り返し読み込むためのByteBufferを定義する。
  8.             ByteBuffer byteBuffe = ByteBuffer.allocate() 64 ); // 64バイトを一度に削除する
  9. // FileChannelからByteBufferにデータを取り込む
  10.  (channel.read(byteBuffe) ! = - 1 <スパン ) {  
  11. // ByteBufferの空白領域をロックする
  12.                 byteBuffe.flip()を実行します。  
  13. /* Charsetオブジェクトの作成 */
  14.                 Charset charset = Charset.forName()。 GBK" );  
  15. // デコーダを作成する
  16.                 CharsetDecoder charsetDecoder = charset.newDecoder()。  
  17. // ByteBufferの中身をトランスコードする
  18.                 CharBuffer charBuffer = charsetDecoder.decode(byteBuffe);  
  19. // CharBuffer charBuffer = charset.decode(byteBuffe);
  20.                 System.out.println(charBuffer)を実行します。  
  21. // ByteBufferを初期化し、次の読み込みに備えます。
  22.                 byteBuffe.clear()を実行します。  
  23.             }  
  24.         }  キャッチ  (例外e) {  
  25. // TODO: 例外を処理する
  26.             e.printStackTrace()を実行します。  
  27.         }  
  28.     }  
  29. }  

私が読みたいa.txtファイルの内容は、次のようなシンプルなものです。
  1. <スパン これは単なるテストです  のために  ファイルチャンネル  
  2. java.nio.charset.MalformedInputException という例外が報告されることに注意してください。入力長さ  1 一体何が起こっているのか見てみましょう。  


で公式のJava APIをチェックアウト。  MalformedInputExceptionは、以下のように記述されています。

入力バイト列が与えられた charset に対して不正である場合、または入力文字列が 16 ビット Unicode の不正な列である場合にスローされるチェックされた例外です。

<スパン 直訳すると、次のような意味です。 このチェック例外は、入力バイト列が与えられた charset に対して不正である場合、または入力文字列が 16-bit Unicode の正規の列でない場合にスローされます。


MalformedInputExceptionが発生するのは、quot;half Chinese problem"のためです。上記の手順を分析すると、CharsetDecoderがByteBufferをデコードする際に、漢字に正常にデコードできることを保証できないため、もしかしたら半分の漢字が含まれている可能性があるからです。この例外は、中国語の文字が半分ある場合に発生します。
<スパン 例として、GBKでは文字が1byte、漢字が2byteなので。 文字列 "我ABC汉字d"が5バイトで傍受された場合は"我ABC"、8バイトで傍受された場合は"我ABC汉字"、ではなく "我ABC汉字?になるはずです。ここで、"? "は漢字の半分であり、これは前方傍受と理解することができます。そのため、例外が報告されます。    ( 注)文字コードをGBKからUTF-8に変更した場合、漢字1文字あたりの長さは3文字として計算されます  )

<スパン <スパン 最初の1枚の解答は
<スパン <スパン ByteBuffer byteBuffe = ByteBuffer.allocate(64); の行を ByteBuffer byteBuffe = ByteBuffer.allocate(1024) に置き換えます。
<スパン <スパン 読みたいa.txtファイルは大きくないので、1024バイトを一気に読むとa.txtファイルの合計サイズより大きくなるので、a.txtファイルを一気に読み込むことにします。だから、もう例外を報告することはない。
<スパン <スパン しかし、もし 1024バイトより大きなa.txtファイルを読みたいのですが、やはり例外が吹っ飛ぶ可能性があります。だから、この方法は正しくない。
<スパン <スパン
<スパン <スパン <スパン 私の第二の解答は
<スパン <スパン CharsetDecoder.decode()メソッドをアウトにして、直接Charset.decode()メソッドを使用します。
<スパン <スパン <スパン これから以下のコードを使用します。
<スパン <スパン
  1. <スパン /* Charsetオブジェクトの作成 */
  2.                 Charset charset = Charset.forName()。 GBK" );  
  3. // デコーダを作成する
  4.                 CharsetDecoder charsetDecoder = charset.newDecoder()。  
  5. // ByteBufferの中身をトランスコードする
  6. // CharBuffer charBuffer = charsetDecoder.decode(byteBuffe);
に変更する。
<スパン <スパン <スパン <スパン
  1. <スパン /* Charsetオブジェクトの作成 */
  2.                 Charset charset = Charset.forName()。 GBK" );  
  3.                 CharBuffer charBuffer = charset.decode(byteBuffe);  

しかし、このように変更すると、次のような面倒なコードの問題も発生するので、これも勧められない。
  1. <スパン これは単なるテストです  のために  ファイルチャンネル  
  2. java.nio.charset.MalformedInputException という例外が報告されることに注意してください。入力長さ  1 一体何が起こっているのか見てみましょう。  
  3. これは単なるテストです  のために  ファイルチャンネル  
  4. java.nio.charset.MalformedInputException という例外が報告されることに注意してください。入力長さ  1 底面が見えますか?  
  5. 杵柄ボアの鞘?  


<スパン 3つ目の解決策は、Bytebufferの最後の1バイトが合法かどうかを毎回判断することです。合法でない場合は、このバイトが2バイト漢字の一部であることを意味するので、デコード時にこのバイトを含めず、次のデコードの前にこのバイトをBytebufferに入れればよい。そうすれば、quot;could not be correct"のような例外を投げることはない。
どのようにメソッドの具体的な解決策コードを変更するには、今日の心は少し痛い、変更する時間がない、次の変更を掲載する予定です。(あなたは、見てみることができます  http://songjianyong.iteye.com/blog/1399241  をご覧ください)

<スパン 4つ目の方法です。FileChannel.map()メソッドを使用して、すべてのファイルの内容を一度にメモリにマッピングしますが、読み込むファイルが大きすぎる場合、パフォーマンス低下を招きます。コードは以下の通りです。
<スパン
<スパン
  1. <スパン 公開 クラス  FileChannelTest {  
  2. 公開 静的 ボイド  main(String[]アーギュメント) { {