복붙노트

[SCALA] 어떻게 스칼라 또는 Java에서 혼합 된 인코딩 된 텍스트 파일을 읽을 수?

SCALA

어떻게 스칼라 또는 Java에서 혼합 된 인코딩 된 텍스트 파일을 읽을 수?

내가 이상적으로 weka.core.converters.CSVLoader를 사용하여 CSV 파일을 구문 분석하는 것을 시도하고있다. 그러나 내가 가지고있는 파일이 올바른 UTF-8 파일이 아닙니다. 그것은 대부분 UTF-8 파일이지만 필드 값의 일부가 다른 인코딩에, 그래서 전체 파일이 유효한에서 어떤 인코딩이 없다, 하지만 어쨌든 그것을 구문 분석 할 필요가있다. 그렇다 웨카 같은 자바 라이브러리를 사용에서, 나는 주로 스칼라하고 있어요. 나는 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)

나는 멀리 모든 잘못된 문자를 던지거나 일부 더미로 대체 완벽하게 행복하다. 나는 다양한 방법으로 프로세스에이 같은 텍스트를 많이해야 할 것입니다 다양한 타사 라이브러리에 데이터를 전달해야 할 수도 있습니다. 이상적인 솔루션은 것 전역 설정 어떤 종류의 것 모든 낮은 수준의 자바 라이브러리 텍스트에 잘못된 바이트를 무시하게, 그래서 수정없이이 데이터에 타사 라이브러리를 호출 할 수있다.

해결책:

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 감사합니다. 방법이 리드 나 자바의 InputStream에 장착하는 것은 불법 UTF-8 바이트 시퀀스를 감지? 이는 코어 자바 솔루션을 제공한다. 스칼라에서 나는이 코덱 암시를함으로써 기본 동작 할 수 있습니다. 나는 그것을 패키지 오브젝트에 그것을 암시 코덱의 정의를 넣어 전체 패키지의 기본 동작 할 수 있다고 생각합니다.

해결법

  1. ==============================

    1.이것은 내가 자바와 함께 할 관리하는 방법입니다 :

    이것은 내가 자바와 함께 할 관리하는 방법입니다 :

        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
    

    어떤 인사가 혼합 4 잘못된 바이트 UTF-8로 세계입니다.

    .REPLACE하면 표준 유니 코드 대체 문자가 사용되는 것을 볼 :

    //"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)
    
  2. ==============================

    2.스칼라의 소스에 대한 해결책은 (@Esailija의 답변에 따라) :

    스칼라의 소스에 대한 해결책은 (@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)
    }
    
  3. ==============================

    3.스칼라의 코덱은 java.nio.charset.CharsetDecoder를 반환하는 디코더 필드가 있습니다 :

    스칼라의 코덱은 java.nio.charset.CharsetDecoder를 반환하는 디코더 필드가 있습니다 :

    val decoder = Codec.UTF8.decoder.onMalformedInput(CodingErrorAction.IGNORE)
    Source.fromFile(filename)(decoder).getLines().toList
    
  4. ==============================

    4.그들이 다시 유효있을 때 무시하고 잘못된 바이트의 문제는 결정된다. UTF-8 바이트가 무효 그렇다면, 문자의 가변 길이 바이트 인코딩을 할 수 있습니다, 당신은 다시 유효한 문자 스트림을 얻을에서 읽기 시작 바이트를 이해할 필요가있다.

    그들이 다시 유효있을 때 무시하고 잘못된 바이트의 문제는 결정된다. UTF-8 바이트가 무효 그렇다면, 문자의 가변 길이 바이트 인코딩을 할 수 있습니다, 당신은 다시 유효한 문자 스트림을 얻을에서 읽기 시작 바이트를 이해할 필요가있다.

    즉, 나는 당신이 읽 할 수있는 '올바른'라이브러리를 찾을 수 있다고 생각하지 않습니다. 나는 훨씬 더 생산적인 접근 방식은 그 데이터를 백업 첫번째 시도하고 청소하는 것입니다 생각합니다.

  5. ==============================

    5.하나가 실패하면 나는 다른 코덱으로 전환하고있다.

    하나가 실패하면 나는 다른 코덱으로 전환하고있다.

    패턴을 구현하기 위해, 나는이 다른 유래 질문에서 영감을 얻었다.

    나는 코덱의 기본 목록을 사용하고 반복적으로 그들을 통해 이동합니다. 모두가 실패하면, 나는 무서운 비트를 출력 :

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

    코드가 최적이되지 않을 수 있습니다 난 그냥 스칼라를 배우는 중이에요.

  6. ==============================

    6.간단한 솔루션은 ASCII로 데이터 스트림을 해석하는 모든 텍스트가 아닌 문자를 무시하는 것입니다. 그러나, 당신도 유효한 인코딩 UTF8-문자를 잃을 것입니다. 그런 당신을 위해 허용 알고하지 마십시오.

    간단한 솔루션은 ASCII로 데이터 스트림을 해석하는 모든 텍스트가 아닌 문자를 무시하는 것입니다. 그러나, 당신도 유효한 인코딩 UTF8-문자를 잃을 것입니다. 그런 당신을 위해 허용 알고하지 마십시오.

    편집 : 유효한 UTF-8입니다 열을 미리 알고 있다면, 당신은 어떤 전략이 무엇인지 컬럼에 사용하도록 구성 할 수 있습니다 자신의 CSV 파서를 작성할 수 있습니다.

  7. ==============================

    7.사용 ISO-8859-1 인코더와 같은; 이것은 당신이 문자열로 포장 값을 바이트 줄 것이다. 이것은 대부분의 인코딩을위한 CSV를 구문 분석에 충분하다. (당신이 혼합 된 8 비트 및 16 비트 블록이있는 경우에, 당신은 문제에있어, 당신은 여전히 ​​ISO-8859-1의 라인을 읽을 수 있습니다,하지만 당신은 블록으로 라인을 구문 분석 할 수 없습니다.)

    사용 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
    
  8. from https://stackoverflow.com/questions/13625024/how-to-read-a-text-file-with-mixed-encodings-in-scala-or-java by cc-by-sa and MIT license