
本記事は、UTF-8の文字列を指定されたバイト数で文字を切り取る方法と、一般的な文字の切り取り方法を合わせて解説します。
はじめに
文字コードUTF-8で、CSVを出力する話になります。
そのCSVのとある列では、文字列を60バイト以内に収める仕様でした。
ご存じの通り、UTF-8では1文字あたり1~4バイトのふり幅があります。
日本語は、だいたい3バイトの文字幅ですよね。
この3バイトや4バイトの文字が存在する事で、60バイト幅以内で文字を切り取る方法に躓く事になりました。
今回は、反省と備忘を兼ねて記事にしたいと思います。
基本の振り返り
本題に入る前に、ObjectScriptで文字を切り取る方法を確認したいと思います。
$extract 単独使用
文字を切り取るコマンドとしては「$extract」になると思います。
// 文字列の切り出し
s val = $e(str, from, to)
// 文字列の置換 ※今回は割愛します
s $e(str, from ,to) = val
文字の切り取り方はこんな感じ。
toを省略したら、1文字のみの切り出しになります。
「*」は文字列の最後を示します。
s str = "abcあいう" // 全体で6文字
w $e(str) // a -- 最初の文字
w $e(str, 2) // b -- 2文字目
w $e(str, *) // う -- 最後の文字
w $e(str, *-3) // c -- (6-3)文字目
w $e(str, 2, 4) // bcあ -- 2文字目から4文字目
w $e(str, 4, *) // あいう -- 4文字目から最後まで
w $e(str, *-2, *) // あいう -- (6-2)文字目から最後まで
$extractは、バイト数ではなく「文字数をカウント」して切り取ります。
$wextract
$extractは、サロゲート・ペアを認識しません。
そのためサロゲート・ペアが含まれている場合は、$wextractを使用する必要があります。
※「w」が付きます。
使用感は$extractと変わりません。
サロゲート・ペアに関しては下記記事を参照してください。
$extract(str, from, $zposition(str, field, pitch))
$extractの「to」に$zpositionを設定する事で、指定のフィールド幅に収まるように切り取ります。
$zpositionのコマンドは、「field」に収める事ができる「str」の「文字数」を返します。
pitchの初期値=2
s num = $zposition(str, field, pitch)
$zpositionは、バイト数をカウントするのでは無く、「フィールド幅に合わせた文字数をカウント」して返します。
【文字の幅】
下記の様に、半角「a」はピッチ=1, 全角「あ」はピッチ=2になります。

下記例では、出力するフィールド幅が「5」の場合、2.5を返します。
s str = "あいうえお"
s filed = 5
w $zposition(str, filed) // 2.5 が返る
$extractと組み合わせると、フィールド幅=5に収まる文字列を取得することが出来ます。
※ from, toも整数しか処理しない
s str = "あいうえお"
s filed = 5
s num = $zposition(str, filed) // 2.5 が返る
w $e(str, 1, num) // あい
$zpositionと$extractの組み合わせは、指定の「フィールド幅に合わせた文字列」を返すので、本題である「バイト数」での切り取りとは異なります。
残念ながら、この手法は使えません。
$zwidth
$zpositionとセットで使用されるコマンドを、併せて紹介します。
$zwidthは、文字の合計幅を返します。
s str = "あいうえお"
w $zwidth(str) // 10 が返る
s str = "aあbいcう"
w $zwidth(str) // 9 が返る
本題
文字数、フィールド幅と振り返ってきましたが、目的はバイト数での切り取り方法になるため、今までの方法は使えません。
そこで今回使用するコマンドは、「$zconvert ($zcvt)」になります。
■サンプルPG
s str = "あいうえおかきくけこさしすせそたちつてとなに" // utf-8 66バイト
s cnvStr = $zcvt(str, "O", "UTF8")
w $l(cnvStr) // 66が返る
s cnvStr = $e(cnvStr, 1, 60) // 60文字での切り出し
w $zcvt(cnvStr, "I", "UTF8", handle) // あいうえおかきくけこさしすせそたちつてと
入力エンコードを実行した時、第4引数(handle)を指定しないと、末尾に変換しきれないゴミが付与される事になります。
s str = "あいうえお"
s cnvStr = $zcvt(str, "O", "UTF8")
s cnvStr = $e(cnvStr, 1, 7) // 「う」の途中で切り取る事になる
//---------------------------------------------
// handle未指定
w $zcvt(cnvStr, "I", "UTF8") // あい? > 中途半端なゴミ「?」が付く
// handle指定
w $zcvt(cnvStr, "I", "UTF8", handle) // あい
w handle // ã > 「う」の残骸
成程、$zconvertですかーーーーー!
盲点でした。
まだまだ精進が足りていません。
おわりに
いかがだったでしょうか。
今回の対応は、文字コードUTF-8にて「指定バイト数内に収めて出力する」という、一見シンプルながら奥の深い課題でした。
今回の対応を通じて、さまざまな学びがありました。
これらの文字列操作は、今後の開発でも役に立つ知見になりそうです。
この記事が、同様の問題に直面している方々の参考となり、少しでもお役に立てれば幸いです。
最後までお読みいただき、ありがとうございました。