#author("2024-10-06T07:53:44+09:00","","") *Luaライブラリ 要注意点 ~正規表現ライブラリライブラリ~ [#y82939dd] **パターン [#s9c2f1b0] -LUAのRegexpは他のメジャーなスクリプト言語とは記述方法が異なるため、注意が必要 >パターンは、''string.match''関数などで文字列を検索するために使用されるものです。~ パターンは、特別な意味を持つ特殊文字と、それ以外の通常文字から構成されます。~ 通常文字は、それ自身に合致します。~ たとえば、aは、a自身に合致します。~ 特殊文字を含まないパターンは、その文字列そのままが対象文字列中から検索されます。~ ~ パターンで使用する特殊文字は以下のとおりです。 --一般的な正規表現記述では「\」でエスケープするが、LUA独自の表現では、「%」でエスケープする。~ 例えば、[%[%]]は各カッコの文字集合となる。~ --文字クラスの小文字と大文字の関係は補集合となる。~ 例えば、%wが英数字ならば、%Wは英数字以外である。~ %lや%wなど、文字クラスは「ロケール」に従う。よって厳密には、アルファベットだけが対象ではない。アルファベットのみを対象としたければ[a-zA-Z]など明示すべきである。 --デフォルトは最長マッチ。+は1回以上、*は0回以上の最長マッチ、-は0回以上の最短マッチ。(''-は既存の正規表現でいうところの「*?」と思っておけばよいだろう。'') --%b<xy>は他言語ではあまり見かけない変わった文字クラスである。~ %b[]などとすると「[」で始まり、「]」で終わる文字列にマッチすることとなる。(これは事実上 一般的な正規表現でいうところの\[.+?\] と同じことであろう) **キャプチャ(後方参照等) [#p094fc7f] >パターンは、カッコで囲まれたサブパターンを含むことができ、これをキャプチャと呼びます。~ パターンが合致したときには、キャプチャに相当する部分文字列は保存され、後で使用することができるようになります。~ キャプチャの順序は開きカッコの出現順です。~ ~ たとえば、パターン"(a*(.)%w(%s*))"において、最初のキャプチャはa*(.)%w(%s*)に相当する部分文字列であり、~ 2番目のキャプチャは.に、3番目のキャプチャは%s*に相当する部分文字列となります。~ ~ Luaパターンの''特殊なケースとして、空っぽのキャプチャ()は、その場所の文字列の中での位置をキャプチャします''。~ たとえば、パターン"()aa()"を文字列"flaaap"に適用した場合、3と5がキャプチャされます。この空っぽのキャプチャは正規表現では使えません。 -後方参照と返り値 #sh(lua){{ pair = "name = Anna" key, value = string.atch(pair, "(%a+)%s*=%s*(%a+)") key, value = string.match(pair, "(%a+)%s*=%s*(%a+)") print(key, value) --> name, Annna }} -後方参照とキャプチャ用メタ文字① #sh(lua){{ s = [[then he said: "it's all right"!]] q, quotePart = s:match("([\"'])(.-)%1") --> 通常の正規表現でいうところの\1 print(quotePart) --> it's all right print(quote) --> " }} -後方参照とキャプチャ用メタ文字② #sh(lua){{ --隣り合う文字列(2文字単位)を入れ替える print(string.gsub("hello Lua!", "(.)(.)", "%2%1" )) --> ehll ouLa }} -後方参照とキャプチャ用メタ文字③ #sh(lua){{ function trim(s) return (s:gsub("^%s*(.-)%s*$", "%1") ---> ( )で囲むことで、gsubの1つ目の返り値だけを、返す。 end }} -空の()と後方参照 ()のキャプチャは特別な意味を持っており、対象文字列中での位置を数値としてキャプチャする。 #sh(lua){{ print(string.match("hello", "()ll()")) --> 3 5 }} -パターン自体をランタイムに作る #sh(lua){{ pattern = string.rep("[^\n]", 70).."[^\n]*" 70個以上の文字列がある行 }} **string.find(str, pattern, init, plain) [#x88b19a6] >~ |str|文字列型|文字列strの先頭から、パターンpatternに合致する部分を探します。&br;合致する部分が見つかったときには、文字列の先頭が1として、その部分の先頭、最後の位置の2つの数値を戻り値として返します。&br;合致する部分が見つからないときにはnilを返します。| |pattern|文字列型または正規表現オブジェクト|また、patternがキャプチャを持っている場合には、キャプチャされた文字列は、位置を表す2つの数値に続く戻り値として返されます。| |init|数値型(FIXNUM)|3つ目の引数initを指定した場合には、パターンpatternを探し始める場所を先頭ではなく、init文字目から始めることができます。&br;initには負の数も指定でき、その場合は末尾から何文字目、という形になります。| |plain|論理型|4つめの引数plainにtrueを指定した場合には、patternはパターンではなく単なる文字列であると解釈されます。&br;したがって、patternに単純に一致する部分を探すようになります。| > #sh(lua){{ s = "hello world" i, j = string.find(s, "hello") print(i, j) ---> 1, 5 s.sub(1,5) ---> "hello" }} #sh(lua){{ a, b = string.find("ABCDE", "BC") -- a=2, b=3 a, b = string.find("ABCDE", "%a*") -- a=1, b=5 a, b = string.find("ABCDE", /\a*/) -- a=1, b=5 a, b = string.find("あいうえお", "いう") -- a=3, b=6 a, b, c = string.find("ABCDE", "B(.)D") -- a=2, b=4, c="C" }} **string.gsub(str, pattern, repl, n) [#nfd53d31] >~ |str|文字列型|文字列strに対して、パターンpatternに合致する部分をすべて引数replの指示通りに置き換えた新しい文字列と、patternが見つかった数を返します。文字列strは一切変更されません。| |pattern|文字列型または正規表現オブジェクト|| |repl|文字列型、テーブル型、関数型のいずれか|replがテーブル型の場合、pattern中の最初のキャプチャをキーとするreplの要素の値が置き換え文字列となります。patternがキャプチャを含まない場合、patternに合致する部分全体がキーとなります。&br;replが関数型の場合、pattern中のすべてのキャプチャを引数としてreplが呼び出され、その戻り値が置き換え文字列となります。patternがキャプチャを含まない場合、patternに合致する部分全体が引数となります。&br;replがテーブル型もしくは関数型の場合、テーブルの要素の値もしくは関数の戻り値が文字列型であれば、それがそのまま置き換え文字列となります。数値型(FIXNUM)の場合は、文字列に変換したものが置き換え文字列になります。&br;テーブルの要素の値もしくは関数の戻り値がfalseあるいはnilの場合には、置き換えは実行されず、元の文字列がそのまま残ります。他の値になった場合には、エラーです。| |n|数値型(FIXNUM)|4番目の引数nを指定した場合には、置き換えは最初のn個だけ実行されます。&br;省略した場合は、見つかった置き換えはすべて実行されます。&br;replが文字列型の場合、replそのものが置き換え文字列となります。replの中の以下の文字列は特別な意味を持ちます。&br;%0&br; patternに合致する部分全体を表わす。&br;%1~%9&br; patternの中の、指定した順番のキャプチャに合致する文字列を表わす。&br;%%&br; パーセント文字そのものを表わす。| #sh(lua){{ x, y = string.gsub("HELLO world", /o/i, "o") -- x = "HELLo world", y = 2 x, y = string.gsub("hello world", "(%w+)", "%1 %1") -- x = "hello hello world world", y = 2 x, y = string.gsub("hello world from Lua", /(\w+)\s*(\w+)/, "%2 %1") -- x = "world hello Lua from", y = 2 t = { name = "lua", version = "5.1" } x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t) -- x = "lua-5.1.tar.gz" function f(s) if s == "hello" then return "bye" elseif s == "world" then return "universe" else return "unknown" end end x = string.gsub("hello world", "(%w+)", f) -- x = "bye universe" }} -テーブル #sh(lua){{ function expands (s) return (s:gsub("$(%w+)", _G)) --> 該当のキャプチャを、テーブルのキーとして評価して、_G[key]を返す。( ) を付けることで、1番目のみ返す。 end name = "Lua"; status = "great" print(expands("$name is $status, isn't it?")) --> Lua is great, isn't it? }} -関数 #sh(lua){{ function expands (s) return (s:gsub("$(%w+)", function (n) return tostring(_G[n]) end)) end print(expands("print=$print"; a = $a)) --> print = function: 0x8f9das0f0; a = nil }} **string.gmatch(str, pattern) [#b3c90608] >~ |str|文字列型| |pattern|文字列型または正規表現オブジェクト| >文字列strに対して、呼び出されるごとにパターンpatternのキャプチャを順に返すイテレータ関数を返します。~ パターンにキャプチャが含まれていないときには、イテレータ関数の戻り値はパターンに合致する文字列全体となります。~ ~ string.gmatchは一般for文とともに使うことで、文字列から条件に合う部分を次々に抜き出して処理することができます。~ ~以下の例では、文字列から単語を抜き出し、1行に一つずつ表示しています。 #sh(lua){{ s = "hello world from Lua" for w in string.gmatch(s, "%a+") do print(w) end }} また、次の例では、key=value形式の文字列を探し、テーブルtにそれを設定しています。 #sh(lua){{ t = {} s = "from=world, to=Lua" for k, v in string.gmatch(s, "(%w+)=(%w+)") do t[k] = v end }}