備忘録的に。
JuliaでCのcharをStringに変換したい
JuliaでSDL2(SimpleDirectMediaLayer.jl)の文字入力を扱う機会があったのですが、文字の出力が、text=(-29, -127, -103, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -127, -82, 0, 127)
みたいにNTuple{32, Cchar}
型を取るために詰まってしまいました。
Ccharというのは、緩く言えば、1文字を数値の表現で表したunsigned int
の8bitのデータで、JuliaにおいてもCと同じく数値表現として扱われます。
SimpleDirectMediaLayer.jlでは要するにccall
などで得られるC言語の出力をCchar
のタプルとして返しているのですが、これをそのままprintln
ったり、TTF_RenderUTF8_Blended
ったりすると、「それ数字じゃね」ってJuliaは解釈する訳です。
解決までのロードマップ
僕はまずこの表現がもしかしたらJuliaだと良くあることなのかと思って、このタプル表現の文字列を直接String
に突っ込んでみて反応を見てみる訳です。
text = Cchar.((-29, -127, -103, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -127, -82, 0, 127))
String(text)
しかし、結果は残念ながら失敗。そんな関数は知らぬとJuliaは返す訳です。
次に、C言語のcharの扱いを思い出そうと努力しますが……高専の頃の、もう随分前の知識で、思い出しても極めてあやふやです。
観念していろいろと調べてみて、やっとまともに動作するっぽいものが完成しました。同じ処理を万が一別の場所で呼び出すときに困らないようにBase.String
にdispatchしてます。(本当は別の型名をつけてやるべきなんだと思いますが、現在の実装範囲では困らなそうなので、適当にdispatchしてます)
function Base.String(vec::NTuple{32, Cchar})
ret = ""
f = x -> (x < 0) ? 256 + x : x
str = String(collect(UInt8.(f.(vec))))
i = 1
while (i <= 32) & isprint(str[i]) & isvalid(str[i])
ret *= str[i]
i = nextind(str, i)
end
return ret
end
text = Cchar.((-29, -127, -103, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -126, -126, -29, -127, -82, 0, 127))
String(text) # "すももももももももの"
以上です。