55

理想的には weka.core.converters.CSVLoader を使用して、CSV ファイルを解析しようとしています。しかし、私が持っているファイルは有効な UTF-8 ファイルではありません。ほとんどがUTF-8ファイルですが、フィールド値の一部は異なるエンコーディングであるため、ファイル全体が有効なエンコーディングはありませんが、とにかく解析する必要があります. Weka などの Java ライブラリを使用する以外は、主に Scala で作業しています。scala.io.Source を使用してファイルを読み取ることさえできません。たとえば、

Source.
  fromFile(filename)("UTF-8").
  foreach(print);

スロー:

    java.nio.charset.MalformedInputException: Input length = 1
at java.nio.charset.CoderResult.throwException(CoderResult.java:277)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:337)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:176)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:153)
at java.io.BufferedReader.read(BufferedReader.java:174)
at scala.io.BufferedSource$$anonfun$iter$1$$anonfun$apply$mcI$sp$1.apply$mcI$sp(BufferedSource.scala:38)
at scala.io.Codec.wrap(Codec.scala:64)
at scala.io.BufferedSource$$anonfun$iter$1.apply(BufferedSource.scala:38)
at scala.io.BufferedSource$$anonfun$iter$1.apply(BufferedSource.scala:38)
at scala.collection.Iterator$$anon$14.next(Iterator.scala:150)
at scala.collection.Iterator$$anon$25.hasNext(Iterator.scala:562)
at scala.collection.Iterator$$anon$19.hasNext(Iterator.scala:400)
at scala.io.Source.hasNext(Source.scala:238)
at scala.collection.Iterator$class.foreach(Iterator.scala:772)
at scala.io.Source.foreach(Source.scala:181)

無効な文字をすべて破棄するか、ダミーに置き換えることができれば幸いです。さまざまな方法で処理するために、このような多くのテキストを用意する予定であり、データをさまざまなサードパーティ ライブラリに渡す必要がある場合があります。理想的な解決策は、すべての低レベルの Java ライブラリがテキスト内の無効なバイトを無視するようなグローバル設定であるため、このデータを変更せずにサードパーティのライブラリを呼び出すことができます。

解決:

import java.nio.charset.CodingErrorAction
import scala.io.Codec

implicit val codec = Codec("UTF-8")
codec.onMalformedInput(CodingErrorAction.REPLACE)
codec.onUnmappableCharacter(CodingErrorAction.REPLACE)

val src = Source.
  fromFile(filename).
  foreach(print)

私を正しい方向に向けてくれた +Esailija に感謝します。これにより、不正なUTF-8バイトシーケンスを検出してJava入力ストリームでそれらを置き換える方法は? コア Java ソリューションを提供します。Scala では、コーデックを暗黙的にすることで、これをデフォルトの動作にすることができます。パッケージ オブジェクトに暗黙的なコーデック定義を配置することで、パッケージ全体の既定の動作にすることができると思います。

4

7 に答える 7

29

これは私がJavaでそれを行う方法です:

    FileInputStream input;
    String result = null;
    try {
        input = new FileInputStream(new File("invalid.txt"));
        CharsetDecoder decoder = Charset.forName("UTF-8").newDecoder();
        decoder.onMalformedInput(CodingErrorAction.IGNORE);
        InputStreamReader reader = new InputStreamReader(input, decoder);
        BufferedReader bufferedReader = new BufferedReader( reader );
        StringBuilder sb = new StringBuilder();
        String line = bufferedReader.readLine();
        while( line != null ) {
            sb.append( line );
            line = bufferedReader.readLine();
        }
        bufferedReader.close();
        result = sb.toString();

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

    System.out.println(result);

無効なファイルは次のバイトで作成されます:

0x68, 0x80, 0x65, 0x6C, 0x6C, 0xC3, 0xB6, 0xFE, 0x20, 0x77, 0xC3, 0xB6, 0x9C, 0x72, 0x6C, 0x64, 0x94

これはhellö wörld、4 つの無効なバイトが混在する UTF-8 です。

.REPLACE標準の Unicode 置換文字が使用されていることがわかります。

//"h�ellö� wö�rld�"

では.IGNORE、無効なバイトが無視されていることがわかります。

//"hellö wörld"

を指定.onMalformedInputしないと、

java.nio.charset.MalformedInputException: Input length = 1
    at java.nio.charset.CoderResult.throwException(Unknown Source)
    at sun.nio.cs.StreamDecoder.implRead(Unknown Source)
    at sun.nio.cs.StreamDecoder.read(Unknown Source)
    at java.io.InputStreamReader.read(Unknown Source)
    at java.io.BufferedReader.fill(Unknown Source)
    at java.io.BufferedReader.readLine(Unknown Source)
    at java.io.BufferedReader.readLine(Unknown Source)
于 2012-11-29T13:04:42.300 に答える
17

Scala のコーデックには、次を返すデコーダー フィールドがありますjava.nio.charset.CharsetDecoder

val decoder = Codec.UTF8.decoder.onMalformedInput(CodingErrorAction.IGNORE)
Source.fromFile(filename)(decoder).getLines().toList
于 2015-08-31T14:40:40.460 に答える
2

失敗した場合は、別のコーデックに切り替えています。

パターンを実装するために、この他のスタックオーバーフローの質問からインスピレーションを得ました。

デフォルトのコーデック リストを使用し、それらを再帰的に調べます。それらがすべて失敗した場合、恐ろしいビットを出力します。

private val defaultCodecs = List(
  io.Codec("UTF-8"),
  io.Codec("ISO-8859-1")
)

def listLines(file: java.io.File, codecs:Iterable[io.Codec] = defaultCodecs): Iterable[String] = {
  val codec = codecs.head
  val fileHandle = scala.io.Source.fromFile(file)(codec)
  try {
    val txtArray = fileHandle.getLines().toList
    txtArray
  } catch {
    case ex: Exception => {
      if (codecs.tail.isEmpty) {
        println("Exception:  " + ex)
        println("Skipping file:  " + file.getPath)
        List()
      } else {
        listLines(file, codecs.tail)
      }
    }
  } finally {
    fileHandle.close()
  }
}

私は Scala を学習しているだけなので、コードが最適ではない可能性があります。

于 2014-10-31T13:21:16.563 に答える
2

無効なバイトを無視することの問題は、それらが再び有効になる時期を決定することです。UTF-8 では文字の可変長バイト エンコーディングが許可されているため、バイトが無効な場合、有効な文字ストリームを再度取得するには、どのバイトから読み取りを開始するかを理解する必要があります。

要するに、読み取り時に「修正」できるライブラリは見つからないと思います。はるかに生産的なアプローチは、最初にそのデータをクリーンアップすることだと思います.

于 2012-11-29T11:46:41.950 に答える
0

ISO-8859-1エンコーダーとして使用します。これにより、文字列にパックされたバイト値が得られます。これは、ほとんどのエンコーディングのCSVを解析するのに十分です。(8ビットブロックと16ビットブロックが混在している場合は、問題が発生します。ISO-8859-1の行を読み取ることはできますが、行をブロックとして解析できない場合があります。)

個々のフィールドを個別の文字列として取得したら、次のことを試すことができます

new String(oldstring.getBytes("ISO-8859-1"), "UTF-8")

適切なエンコーディングで文字列を生成します(わかっている場合は、フィールドごとに適切なエンコーディング名を使用します)。

編集:java.nio.charset.Charset.CharsetDecoderエラーを検出する場合はを使用する必要があります。この方法でUTF-8にマッピングすると、エラーが発生したときに文字列に0xFFFFが含まれるようになります。

val decoder = java.nio.charset.Charset.forName("UTF-8").newDecoder

// By default will throw a MalformedInputException if encoding fails
decoder.decode( java.nio.ByteBuffer.wrap(oldstring.getBytes("ISO-8859-1")) ).toString
于 2012-11-29T12:10:17.493 に答える
0

簡単な解決策は、データ ストリームを ASCII として解釈し、テキスト以外のすべての文字を無視することです。ただし、有効なエンコードされた UTF8 文字でさえも失われます。それがあなたに受け入れられるかどうかはわかりません。

編集:どの列が有効な UTF-8 であるかが事前にわかっている場合は、どの列でどの戦略を使用するかを構成できる独自の CSV パーサーを作成できます。

于 2012-11-29T11:51:11.133 に答える