Magnolia Tech

いつもコードのことばかり考えている人のために。

フォークされたjuniversalchardetの挙動が変わって、US-ASCIIを判定するように変わっていた

長い人生、生きていれば文字コードの推論をやらねばならない場面が一度や二度は有るものだ(実際にはUTF-8統一時代になって、あまり発生しなくなったけど……)。

そんな時、JVM系言語であれば、juniversalchardetが候補に上がることもあるかもしれない。

長らくバージョンアップされていないな、と思っていたら、実はフォークされたバージョンが割とこまめにメンテナンスされていた。

https://github.com/albfernandez/juniversalchardet

使い方はREADMEを読めば分かるので割愛するけど、一つだけ気を付ける点が有る。元のjuniversalchardetUS-ASCIIをサポートしていない。つまり、US-ASCIIに相当する文字データを渡すと、判定不能で返却された(nullが返る)。

しかし、以下のコミットでUS-ASCIIが判定できるように変更が加えられている。

https://github.com/albfernandez/juniversalchardet/commit/fcf1898ab7bae3f7308d991e225c3016e460b352

つまり、US-ASCIIという文字列が返ってくる。返却値がnullの場合は、US-ASCIIと解釈し、UTF-8で上書きするようなコードを書いていると、挙動が変わってしまうので、注意。

以下のコードで確かめた。

package example

import org.mozilla.universalchardet.UniversalDetector

object Detector {
  def main(argv: Array[String]): Unit = {
    val detector = new UniversalDetector(null)

    val str = "This is Love"
    var bytes = str.getBytes()
      detector.handleData(bytes, 0, bytes.length)


    detector.dataEnd()

    val encoding = detector.getDetectedCharset()
    if (encoding != null) {
      println("Detected encoding = " + encoding);
    } else {
      println("No encoding detected.");
    }

    detector.reset()
  }
}
ThisBuild / scalaVersion     := "2.13.4"
ThisBuild / version          := "0.1.0-SNAPSHOT"
ThisBuild / organization     := "com.example"
ThisBuild / organizationName := "example"

lazy val root = (project in file("."))
  .settings(
    name := "Scala Seed Project",
    libraryDependencies ++= Seq(
      "com.googlecode.juniversalchardet" % "juniversalchardet"  % "1.0.3",
//      "com.github.albfernandez" %  "juniversalchardet"          % "2.4.0",
    )
  )

フォークされたバージョンが必ずしも元のバージョンと同じ挙動とは限らない点は、注意しないと(そもそも挙動を変える必要がなければフォークもしないと思うけど)