LoginSignup
4
1

More than 5 years have passed since last update.

Nimでファイルの文字コードを(外部ライブラリを利用して)判定する

Last updated at Posted at 2016-12-25

概要

Nimのadventカレンダーが空いていたので、穴埋め的な感じで。

Nimでlibguessを利用して、ファイルのエンコーディング判定を行ってみます。

libguessはダウンロードするとsoを作成するMakefileが入っていますが、こちらは利用せずにNimからコンパイルする方法を取ります。

ダウンロード後の解凍状態

.
├── Makefile
├── guess.c
├── guess.scm
├── guess_tab.c
├── libguess.h
└── test.c

c2nimで、ヘッダからnimファイルを生成

$ c2nim libguess.h                                                                                                                                                                             Hint: operation successful (48 lines compiled; 0 sec total; 516KiB; ) [SuccessX]

$ cat libguess.nim

## 
##  This code is derivative of guess.c of Gauche-0.8.3.
##  The following is the original copyright notice.
## 

.. 省略 ..

##  prototypes

proc guess_jp*(buf: cstring; buflen: cint): cstring
proc guess_tw*(buf: cstring; buflen: cint): cstring
proc guess_cn*(buf: cstring; buflen: cint): cstring
proc guess_kr*(buf: cstring; buflen: cint): cstring                                                                                                                                                                                          

compile および importc ディレクティブを追加する

今回は、Nimでcソースをコンパイルするので、compileとimportcディレクティブを利用します。

libguess.nim
{.compile: "guess.c".}

proc guess_jp*(buf: cstring; buflen: cint): cstring {.importc.}
proc guess_tw*(buf: cstring; buflen: cint): cstring {.importc.}
proc guess_cn*(buf: cstring; buflen: cint): cstring {.importc.}
proc guess_kr*(buf: cstring; buflen: cint): cstring {.importc.}

テストコードを作成

libguess.nimをインポートし、guess_jpを呼び出すコードを作成します。
guess_jpの第2引数cintに渡す値をcast関数を使って、変換しています。

test.nim
import libguess

if isMainModule:
  let text = "あいうえお"
  let textLen:cint = cast[cint](text.len)
  echo guess_jp(text,textLen)

実行結果は、こちら

Hint: used config file '~/nim/nim-0.15.2/config/nim.cfg' [Conf]
Hint: system [Processing]
Hint: test [Processing]
Hint: libguess [Processing]
CC: test
Hint:  [Link]
Hint: operation successful (11608 lines compiled; 0.320 sec total; 17.004MiB; Debug Build) [SuccessX]
Hint: ./debug/test.nim.exe [Exec]
UTF-8

ファイルの文字コードを判定する

Nimでファイルを読み出した文字列(というかバイト列)を、guess_jp関数に渡して文字コードを判定します。
バイト配列のポインタを、FileStreamのreadに渡すやり方がわからず、いろいろとネットを駆けずり回ったのは良い思い出。

test.nim

import libguess
import streams
import parseopt2
import os

# バッファ(8K)を用意します
var buff : array[8192,byte]
# ポインタを取得
var buffPtr = addr buff

proc detect(fileName:string) : string =
  # ファイルをオープンします  
  var fs = newFileStream(fileName,fmRead)
  defer:
    # 終了時にクローズ
    fs.close

  # ファイルをバッファへ読み込む
  let readLength = fs.readData(buffPtr, buff.len)
  # 結果を格納
  result = $guess_jp(cast[cstring](buffPtr),cast[cint](readLength))

# 引数なしの場合のテストコード
proc test(text:string) : string =
  let textLen:cint = cast[cint](text.len)
  result = $guess_jp(text,textLen)

# 開始
if isMainModule:
  if os.paramCount() == 0 :
    echo test("あいうえお")
  else :
    for kind,key,val in getopt() : 
      case kind
      of cmdArgument:
        echo key," = ",detect(key)
      of cmdLongOption,cmdShortOPtion,cmdEnd:
        discard

テストしてみる

iconvでtest.nimをShift_JISおよびEUC-JPに変換しておき、3つのファイルでテストしてみます。

# 変換
$ iconv -f UTF-8 -t SHIFT_JIS test.nim > test.nim.shift_jis   
$ iconv -f UTF-8 -t EUC-JP test.nim > test.nim.euc_jp   

# Let's 判定!!
$ test.nim.exe test.nim test.nim.shift_jis test.nim.euc_jp
test.nim = UTF-8
test.nim.shift_jis = SJIS
test.nim.euc_jp = EUC-JP

ちゃんと識別できているようです。

バイナリファイルを渡してみる

んーと・・・まあいっか

$ test.nim.exe /bin/ls /bin/mkdir
/bin/ls = SJIS
/bin/mkdir = SJIS

まとめ

  • nimでCのソースをコンパイルし、定義されている関数を呼び出すことができました
  • compileおよびimportcディレクティブを利用しました
4
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
1