文字列操作ライブラリ
文字列操作用にいくつかの関数を追加してみました。
使い方
下記のスクリプトをstring.luaを名前で保存する。
それを、system.luaの中から読み出す。
NL_dofile("string.lua")
そうすると、stringに色々と新しい関数が追加されて便利になる。
string.lua
-- string.lua -- 文字列操作について、いささかの増強をする。 do -- 文字コードを入れると、 -- それが半角文字であればtrueを、そうでなければfalseを返す。 local code_decision = function(num) if num < 127 then return true end if num < 161 then return false end if num < 224 then return true end return false end -- 文字コードを入れると、 -- Shift-JISの漢字であればtrueを、そうでなければfalseを返す。 local kanji_decision = function(num) -- そもそも半角じゃ漢字じゃない。 if code_decision(num) then return false end return num > 136 end -- 文字列の先頭の一文字を返す。 string.initial = function(str) if str:len()==0 then return "" end if str:len()==1 then return str end if code_decision(str:byte()) then return str:sub(1, 1) end return str:sub(1, 2) end -- 文字列の先頭が漢字かどうかを判別する。 string.is_kanji = function(str) if str:len() == 0 then return nil end local test = str if test:len() > 2 then test = str:initial() end return kanji_decision(test:byte()) end -- 文字列を文字単位に分解する。返り値はテーブル。 string.separate_table = function(str) if str:len()==0 then return {} end -- 空文字列は何もしない。 local res = {} -- 回答用 local byte = {str:byte(1, str:len())} -- コードの配列にする。 local code while 0 < #byte do code = table.remove(byte, 1) -- 一文字取ってくる。 if code_decision(code) then -- 半角文字だった。 res[1+#res] = string.char(code) else -- 全角文字だった。 res[1+#res] = string.char(code, table.remove(byte, 1)) end end return res end -- 文字列を文字単位に分解する。返り値はバラで。 string.separate = function(str) return unpack(str:separate_table()) end -- 文字列の先頭に指定された文字を追加していく。 -- 引数は必要な桁数と、埋めるのに使う文字 string.fill = function(str, digit, letter) -- digitは数値である必要がある。 if type(digit)~="number" then return str end -- 数値でなければ何もしない。 -- 後何文字必要かを計算する。 local last = digit - str:len() if last < 1 then return str end -- 一文字未満であれば、何もしない。 -- letterは文字である必要がある。 if type(letter)~="string" then letter = " " end -- 文字にする。 if letter:len()==0 then letter = " " end -- 必ず一文字以上にする。 letter = letter:initial() local count = 0 -- repの実行回数。 while last > 0 do last = last - letter:len() count = count + 1 end return letter:rep(count)..str end end do -- 変換テーブル local han2zen_table = { [" "]=" ", ["!"]="!", ["\""]="”", ["#"]="#", ["$"]="$", ["%"]="%", ["&"]="&", ["'"]="’", ["("]="(", [")"]=")", ["*"]="*", ["+"]="+", [","]=",", ["-"]="−", ["."]=".", ["/"]="/", ["0"]="0", ["1"]="1", ["2"]="2", ["3"]="3", ["4"]="4", ["5"]="5", ["6"]="6", ["7"]="7", ["8"]="8", ["9"]="9", [":"]=":", [";"]=";", ["<"]="<", ["]="]="=", [">"]=">", ["?"]="?", ["@"]="@", ["A"]="A", ["B"]="B", ["C"]="C", ["D"]="D", ["E"]="E", ["F"]="F", ["G"]="G", ["H"]="H", ["I"]="I", ["J"]="J", ["K"]="K", ["L"]="L", ["M"]="M", ["N"]="N", ["O"]="O", ["P"]="P", ["Q"]="Q", ["R"]="R", ["S"]="S", ["T"]="T", ["U"]="U", ["V"]="V", ["W"]="W", ["X"]="X", ["Y"]="Y", ["Z"]="Z", ["["]="[", ["\\"]="¥", ["]"]="]", ["^"]="^", ["_"]="_", ["`"]="‘", ["a"]="a", ["b"]="b", ["c"]="c", ["d"]="d", ["e"]="e", ["f"]="f", ["g"]="g", ["h"]="h", ["i"]="i", ["j"]="j", ["k"]="k", ["l"]="l", ["m"]="m", ["n"]="n", ["o"]="o", ["p"]="p", ["q"]="q", ["r"]="r", ["s"]="s", ["t"]="t", ["u"]="u", ["v"]="v", ["w"]="w", ["x"]="x", ["y"]="y", ["z"]="z", ["{"]="{", ["|"]="|", ["}"]="}", ["~"]="〜", ["。"]="。", ["「"]="「", ["」"]="」", ["、"]="、", ["・"]="・", ["ヲ"]="ヲ", ["ァ"]="ァ", ["ィ"]="ィ", ["ゥ"]="ゥ", ["ェ"]="ェ", ["ォ"]="ォ", ["ャ"]="ャ", ["ュ"]="ュ", ["ョ"]="ョ", ["ッ"]="ッ", ["ー"]="ー", ["ア"]="ア", ["イ"]="イ", ["ウ"]="ウ", ["エ"]="エ", ["オ"]="オ", ["カ"]="カ", ["キ"]="キ", ["ク"]="ク", ["ケ"]="ケ", ["コ"]="コ", ["サ"]="サ", ["シ"]="シ", ["ス"]="ス", ["セ"]="セ", ["ソ"]="ソ", ["タ"]="タ", ["チ"]="チ", ["ツ"]="ツ", ["テ"]="テ", ["ト"]="ト", ["ナ"]="ナ", ["ニ"]="ニ", ["ヌ"]="ヌ", ["ネ"]="ネ", ["ノ"]="ノ", ["ハ"]="ハ", ["ヒ"]="ヒ", ["フ"]="フ", ["ヘ"]="ヘ", ["ホ"]="ホ", ["マ"]="マ", ["ミ"]="ミ", ["ム"]="ム", ["メ"]="メ", ["モ"]="モ", ["ヤ"]="ヤ", ["ユ"]="ユ", ["ヨ"]="ヨ", ["ラ"]="ラ", ["リ"]="リ", ["ル"]="ル", ["レ"]="レ", ["ロ"]="ロ", ["ワ"]="ワ", ["ン"]="ン", ["゙"]="゛", ["゚"]="゜", ["ガ"]="ガ", ["ギ"]="ギ", ["グ"]="グ", ["ゲ"]="ゲ", ["ゴ"]="ゴ", ["ザ"]="ザ", ["ジ"]="ジ", ["ズ"]="ズ", ["ゼ"]="ゼ", ["ゾ"]="ゾ", ["ダ"]="ダ", ["ヂ"]="ヂ", ["ヅ"]="ヅ", ["デ"]="デ", ["ド"]="ド", ["バ"]="バ", ["パ"]="パ", ["ビ"]="ビ", ["ピ"]="ピ", ["ブ"]="ブ", ["プ"]="プ", ["ベ"]="ベ", ["ペ"]="ペ", ["ボ"]="ボ", ["ポ"]="ポ" } -- 半角文字に変換。テーブル化 string.han2zen_table = function(str) if str:len()==0 then return {} end local t = str:separate_table() -- 文字毎に分割 local res = {} local letter1 = nil local letter2 = nil while 0<#t do letter1 = table.remove(t, 1) -- 先頭の文字を取得 if 0<#t then letter2 = letter1..t[1] else letter2 = "" end if han2zen_table[letter2] then res[1+#res] = han2zen_table[letter2] table.remove(t, 1) elseif han2zen_table[letter1] then res[1+#res] = han2zen_table[letter1] else res[1+#res] = letter1 end end return res end -- 半角文字を全角文字に変換 string.han2zen = function(str) return table.concat(str:han2zen_table()) end local zen2han_table = {} for k, v in pairs(han2zen_table) do zen2han_table[v] = k end -- 全角文字を半角に変換 string.zen2han_table = function(str) local t = str:separate_table() local res = {} local letter while 0<#t do letter = table.remove(t, 1) res[1+#res] = zen2han_table[letter] or letter end return res end -- 全角を半角に変換 string.zen2han = function(str) return table.concat(str:zen2han_table()) end -- 数値の構成要素として許可できる文字 local digit_list = { ["0"] = true, ["1"] = true, ["2"] = true, ["3"] = true, ["4"] = true, ["5"] = true, ["6"] = true, ["7"] = true, ["8"] = true, ["9"] = true, ["."] = true, ["-"] = true } -- 全角混じり文字列を数値に変換 string.tonumber = function(str) local t = str:zen2han_table() local res = {} for i, v in ipairs(t) do if digit_list[v] then res[1+#res] = v end end return tonumber(table.concat(res)) end -- 文字列を単純に特定の文字で区切る。返り値はテーブル -- csvのパースには使えませんよ? -- 区切り文字は一文字である必要がある。そうでなければ、先頭の一文字が使われる。 string.split_table_infinity = function(str, separater) if str:len()==0 then return {""} end -- ショートカット local t = str:separate_table() -- まずは文字単位に分割 if separater:len()==0 then return t end local res = {""} -- 回答用テーブル local letter = nil -- 一時変数 while 0<#t do -- tが存在し続ける限り回す。 letter = table.remove(t, 1) -- tの最初の一つを取得 if letter == separater then res[1+#res] = "" -- 文字列を追加 else res[#res] = res[#res]..letter -- 最後の文字列に追加 end end return res end string.split_table = function(str, separater, max) -- セパレータのデフォルト設定 if type(separater)~="string" then separater="" end separater = separater:initial() -- 先頭だけが有効。 local t = str:split_table_infinity(separater) -- とりあえず、無制限分割 if type(max)~="number" then return t end -- maxがなければ終了 if max<1 then return t end -- maxが1未満なら、分割制限なし扱い。終了。 max = math.floor(max) -- 念のため、端数切捨て。 if max==1 then return str end -- maxが1なら、分割しないの意味。 -- それ以外なら、分割制限をする。 local letter = nil while max<#t do letter = table.remove(t) -- 最後を削る。 t[#t] = t[#t]..separater..letter -- 新しい最後に加える。 end return t end -- 文字列を単純に特定の文字で区切る。返り値はバラで。 string.split = function(str, separater, max) return unpack(str:split_table(separater, max)) end -- メタ文字用の変換テーブルの作成 local chara_code = {} for i=0, 255 do chara_code[i] = string.char(i) end local temp = "^$()%.[]*+-?" for i, v in ipairs({temp:byte(1, temp:len())}) do chara_code[v] = "%"..string.char(v) end string.kill_meta_chara = function(str) local ret = "" for i, v in ipairs({str:byte(1, str:len())}) do ret = ret .. chara_code[v] end return ret end end
解説
追加されるのは以下のものです。
- string.initial
- その文字列の先頭の文字を返します。半角全角を考慮します。
- string.is_kanji
- その文字列の先頭がShift-JISの漢字かどうかを返します。
- string.separate
- その文字列を文字に分解します。半角全角を考慮します。
- string.fill
- 長さと文字を指定し、元の長さの先頭に指定した文字を、全体が指定した長さに達するまで繰り返し追加します。
- string.han2zen
- その文字列に含まれる半角文字を(変換できるだけ)全角文字に変換して返します。半角文字の「ジ」を「ジ」に変換するくらいはします。
- string.zen2han
- han2zenの逆です。
- string.tonumber
- 全角を半角にした後、数値として不要な文字列を消去し、それから数値に変換して返します。
- string.split
- 文字列を特定の文字で分割して返します。分割数も指定できます。
- string.kill_meta_chara
- 文字列に含まれる正規表現用のメタ文字を殺して、「安全」にします。
string.initial
先頭の文字を取得します。
local str = "English" local ini = str:initial() -- ini = "E" string:initial(str) -- これでも同じ結果に。 local str2 = "日本語" local ini2 = str:initial() -- ini2 = "日"
string.is_kanji
その文字列の先頭の文字が漢字であれば、trueを返します。
local str = "English" local is_kanji = str:is_kanji() -- is_kanji = false string:is_kanji(str) -- これでも同じ結果に。 local str2 = "ひらがな" local is_kanji2 = str2:is_kanji() -- is_kanji2 = false local str3 = "漢字" local is_kanji3 = str3:is_kanji() -- is_kanji3 = true
string.separate
文字列を文字単位でバラバラにします。
local str = "日本語" local letter1, letter2, letter3 = str:separate() -- "日", "本", "語"
もし、バラバラに受け取るのが嫌ならば、テーブルにまとめることもできます。
local str = "English" local res = str:separate_table() -- res = {[1]="E", [2]="n", [3]="g", [4]="l", [5]="i", [6]="s", [7]="h"}
string.fill
文字列の先頭に特定の文字列を追加します。
local num = 200 local str = tostring(num) -- str = "200" local str2 = str:fill(8, " ") -- str2 = " 200" local str3 = str:fill(8, "0") -- str3 = "00000200"
引数には、桁数と、埋めるのに使う文字(列)を指定します。
string.han2zen
その文字列に含まれる半角文字を全て全角に変換して返します。
local str = "ガッチャマン" local str2 = str:han2zen() -- str2 = "ガッチャマン"
string.zen2han
han2zenの逆です。その文字列に含まれる半角文字に変換可能な全角文字を全て半角文字に変換して返します。
local str = "012ガッチャマン" local str2 = str:zen2han() -- str2 = "012ガッチャマン"
string.tonumber
文字列を数値に変換しますが、tonumberとの違いは、
- 全角数字を半角にする。
- 数字表現に使えない文字は全て削除する。
点で、数値が返されます。
local str = "01日本語23" local num = str:tonumber() -- num = 123
string.split
文字列を指定した文字で分割して返します。最大分割数も指定できます。指定しなければ無制限です。
local str = "1,2,3,4,5,6,7" str:split(",") -- 1 2 3 4 5 6 7 に分割される。 local t = str:split_table(",") -- t = {"1", "2", "3", "4", "5", "6", "7"}
- 分割文字を省略すると、""が採用され、string.separate/str.separate_tableと同じ結果になります。
- 分割文字に文字列を指定すると、その先頭の文字だけが採用されます。
- 最大分割数を省略すると、無制限に分割します。
- 最大分割数を指定すると、こうなります。
local str = "1,2,3,4,5,6,7" local t = str:split_table(",", 3) -- t = {"1", "2", "3,4,5,6,7"}
string.kill_meta_chara(2012/10/13追記)
文字列の中から正規表現で意味を持つメタ文字を見つけ出し、エスケープして「安全」にします。
この方法で「安全」にした文字列は、string.gsubなどでpaternとしてそのまま使うことができます。もちろん、メタ文字は無効にしていますので、単純に元になった文字列と全く同じ文字列を検索するだけになります。
local text = "これからテキストウィンドウに表示する予定の文字列" text:gsub("リア充", "リア(充/じゅう)") -- テキスト中に「リア充」の文字があればフリガナを振りたい。 -- しかし、上文はエラーを出して失敗する。「充」の文字コードにメタ文字が含まれるからである。 local ruby = string.kill_meta_chara("リア充") text:gsub(ruby, "リア(充/じゅう)") -- これなら、エラーを出さない。