如何在Scala或Java中读取带有混合编码的文本文件?

Dan*_*ler 52 java scala utf-8 character-encoding weka

我正在尝试解析CSV文件,理想情况下使用weka.core.converters.CSVLoader.但是我的文件不是有效的UTF-8文件.它主要是一个UTF-8文件,但是一些字段值采用不同的编码,因此没有整个文件有效的编码,但无论如何我需要解析它.除了使用像Weka这样的java库之外,我主要在Scala中工作.我甚至无法使用scala.io.Source读取文件:例如

Source.
  fromFile(filename)("UTF-8").
  foreach(print);
Run Code Online (Sandbox Code Playgroud)

抛出:

    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)
Run Code Online (Sandbox Code Playgroud)

我很高兴将所有无效字符丢弃或用一些虚拟替换它们.我将有很多这样的文本以各种方式处理,可能需要将数据传递给各种第三方库.理想的解决方案是某种全局设置会导致所有低级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)
Run Code Online (Sandbox Code Playgroud)

感谢+ Esailija指出我正确的方向.这引导我如何检测非法的UTF-8字节序列以在java输入流中替换它们? 它提供了核心java解决方案.在Scala中,我可以通过隐式编解码器使其成为默认行为.我想通过在包对象中加入隐式编解码器定义,我可以使它成为整个包的默认行为.

Esa*_*ija 25

这就是我设法用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);
Run Code Online (Sandbox Code Playgroud)

使用字节创建无效文件:

0x68, 0x80, 0x65, 0x6C, 0x6C, 0xC3, 0xB6, 0xFE, 0x20, 0x77, 0xC3, 0xB6, 0x9C, 0x72, 0x6C, 0x64, 0x94
Run Code Online (Sandbox Code Playgroud)

其中hellö wörld包含4个无效字节的UTF-8.

随着.REPLACE你看到标准的Unicode替换字符使用:

//"h?ellö? wö?rld?"
Run Code Online (Sandbox Code Playgroud)

使用.IGNORE,您会看到忽略无效字节:

//"hellö wörld"
Run Code Online (Sandbox Code Playgroud)

没有说明.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)
Run Code Online (Sandbox Code Playgroud)


rai*_*tin 14

scala源代码的解决方案(基于@Esailija答案):

def toSource(inputStream:InputStream): scala.io.BufferedSource = {
    import java.nio.charset.Charset
    import java.nio.charset.CodingErrorAction
    val decoder = Charset.forName("UTF-8").newDecoder()
    decoder.onMalformedInput(CodingErrorAction.IGNORE)
    scala.io.Source.fromInputStream(inputStream)(decoder)
}
Run Code Online (Sandbox Code Playgroud)


max*_*xmc 14

Scala的Codec有一个解码器字段,它返回一个java.nio.charset.CharsetDecoder:

val decoder = Codec.UTF8.decoder.onMalformedInput(CodingErrorAction.IGNORE)
Source.fromFile(filename)(decoder).getLines().toList
Run Code Online (Sandbox Code Playgroud)

  • 对.这是Esailija和raisercostin的答案的本质.这是最简洁的. (2认同)