概要
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ディレクティブを利用しました