6.4 文字列操作
このライブラリは部分文字列の検索・抽出やパターンマッチングといった文字列操作のための一般的な関数を提供します。Lua で文字列にアクセスするときのインデックスは最初の文字が 1 です (C のように 0 ではありません)。負のインデックスも使うことができ、文字列の最後から始まって後ろ向きに進んだ位置として解釈されます。例えば文字列の最後の文字にはインデックス -1 でアクセスできます。
文字列ライブラリの関数は全て string
というテーブルの中に提供されます。また文字列ライブラリは文字列型のメタテーブルの __index
フィールドに string
テーブルを設定するので、文字列ライブラリの関数はオブジェクト指向のスタイルで呼び出すことができます。例えば string.byte(s,i)
は s:byte(i)
とできます。
文字列ライブラリは一バイトで一文字を表すエンコーディングを仮定します。
string.byte (s [, i [, j]])
文字 s[i]
, s[i+1]
, ..., s[j]
を表す内部数値コードを返します。i
のデフォルト値は 1 で、j
のデフォルト値は i
です。インデックスは string.sub
と同じ規則に従って修正されます。
数値コードがプラットフォーム間でポータブルであるとは限りません。
string.char (...)
ゼロ個以上の整数を受け取ります。返り値は文字列であり、長さが引数の数と等しく、各文字の内部数値コードが同じ位置の引数と一致します。
数値コードがプラットフォーム間でポータブルであるとは限りません。
string.dump (function [, strip])
与えられた関数のバイナリ表現 (バイナリチャンク) を表す文字列を返します。この文字列を load
すると関数のコピー (のアップバリューを新しくしたもの) が返ります。strip
を true にすると、バイナリ表現から関数のデバッグ情報を削除して容量を節約することが許可されます。
アップバリューを持つ関数のバイナリ表現にはアップバリューの数だけが保存され、(再) 読み込み時にはこれらのアップバリューに新しいインスタンスが与えられます。アップバリューの初期化に関して詳しくは load
を参照してください。またデバッグライブラリを使えば関数のアップバリューの再読み込みを好きな方法で行えます。
string.find (s, pattern [, init [, plain]])
文字列 s
に含まれるパターン pattern
の最初のマッチを検索します (参照: § 6.4.1)。マッチが見つかった場合には、マッチの始まりと終わりを示す s
のインデックスが返ります。見つからなければ fail が返ります。三つ目の省略可能な数値引数 init
は検索を始める場所を表します。デフォルト値は 1 であり、負の値も許されます。第四引数 plain
も省略可能であり、これを ture
とするとパターンマッチの機能が無効化されます。このときは単純な部分文字列の検索が行われ、pattern
に含まれる特殊文字は通常の文字として処理されます。
キャプチャを持つパターンのマッチが成功した場合には、二つのインデックスの後にキャプチャされた値も返ります。
string.format (formatstring, ...)
第一引数の文字列で表されるフォーマットに従って、後ろに続く可変個数の引数を文字列に変換します。値から文字列への変換は基本的に ISO C の sprintf
と同様です。異なるのは変換指定子 (および修飾子) の *
, h
, L
, l
, n
が使えないこと、そして q
という Lua 特有の指定子が使えることです。
変換指定子 q
は真偽値・nil・数値・文字列を Lua ソースコードに埋め込める文字列に変換します。真偽値と nil はそのまま (つまり "true"
, "false"
, "nil"
に) 変換されます。浮動小数点は精度を保つために 16 進数で書かれます。文字列は二重引用符で囲まれ、Lua インタープリタが正しく読めるよう必要に応じてエスケープを行いながら出力されます。例えば、
string.format('%q', 'a string with "quotes" and \n new line')
が出力する文字列の一例は次の通りです:
"a string with \"quotes\" and \
new line"
指定子 q
は修飾子 (フラグ・幅・長さ) をサポートしません。
変換指定子 A
, a
, E
, e
, f
, G
, g
は全て数値を引数に取ります。また変換指定子 c
, d
, i
, o
, u
, X
, x
が期待するのは整数です。Lua が C89 コンパイラでコンパイルされた場合には、A
と a
(16 進浮動小数点数) は修飾子をサポートしません。
変換指定子 s
は文字列を受け取ります。引数が文字列でない場合には tostring
と同じ規則で文字列に変換されます。修飾子が付いていない指定子 s
に対応する文字列はゼロ文字を含むべきではありません。
変換指定子 p
は lua_topointer
が返すポインタを受け取ります。この指定子を使うと、テーブル・ユーザーデータ・スレッド・文字列・関数に対するユニークな文字列識別子が得られます。他の値 (数値・nil・真偽値) に対する lua_topointer
の返り値をこの指定子に渡すと、NULL
ポインタを表す文字列となります。
string.gmatch (s, pattern [, init])
呼び出すたびに文字列 s
に含まれる pattern
のキャプチャを一つずつ返す関数を返します (参照: § 6.4.1)。pattern
にキャプチャを指定しなければ、返り値の関数は呼び出すたびにマッチ全体を返します。三つ目の省略可能な数値引数 init
は探索を始める場所を表します。デフォルトは 1 で、負でも構いません。
例えば次のループは文字列 s
に含まれる単語をイテレートする処理であり、各単語が一行ごとに表示されます:
s = "hello world from Lua"
for w in string.gmatch(s, "%a+") do
print(w)
end
次の例は与えられた文字列に含まれる key=value
という形のペアをテーブルに格納します:
t = {}
s = "from=world, to=Lua"
for k, v in string.gmatch(s, "(%w+)=(%w+)") do
t[k] = v
end
この関数ではパターンの最初にキャレット '^'
があってもアンカーとして機能することはありません。アンカーを含むパターンが複数回マッチすることはあり得ないためです。
string.gsub (s, pattern, repl [, n])
s
をコピーし、コピーに含まれる全ての (n
が与えられるなら n
個の) パターン pattern
を repl
で「置き換えた」文字列を返します (参照: § 6.4.1)。repl
は文字列・テーブル・関数のいずれかです。この他に gsub
は二つ目の返り値としてマッチの総数を返します。gsub
という名前は「グローバル置換 (Global SUBstitution)」から来ています。
repl
が文字列なら、その値が置換後の文字列として使われます。そのとき文字 %
はエスケープ文字として機能します: 1 から 9 の d
に対するシーケンス %d
はキャプチャされた d
番目の文字列を表し、%0
はマッチ全体を表します。単一の %
は %%
というシーケンスで表されます。
repl
がテーブルなら、マッチごとに一つ目のキャプチャをキーとしてテーブルが検索されます。
repl
が関数なら、マッチごとにキャプチャされた全ての部分文字列を引数として関数が呼ばれます。
いずれの場合でも、パターンにキャプチャがないときはパターン全体が一つのキャプチャに含まれるものとして振る舞います。
テーブルの検索および関数の呼び出しが返した値が文字列または数値の場合には、それが置換文字列として使われます。返り値が false または nil の場合には置換文字列は無いものとして扱われます (マッチは元の文字列のままとなります)。
いくつか例を示します:
x = string.gsub("hello world", "(%w+)", "%1 %1")
--> x="hello hello world world"
x = string.gsub("hello world", "%w+", "%0 %0", 1)
--> x="hello hello world"
x = string.gsub("hello world from Lua", "(%w+)%s*(%w+)", "%2 %1")
--> x="world hello Lua from"
x = string.gsub("home = $HOME, user = $USER", "%$(%w+)", os.getenv)
--> x="home = /home/roberto, user = roberto"
x = string.gsub("4+5 = $return 4+5$", "%$(.-)%$", function (s)
return load(s)()
end)
--> x="4+5 = 9"
local t = {name="lua", version="5.4"}
x = string.gsub("$name-$version.tar.gz", "%$(%w+)", t)
--> x="lua-5.4.tar.gz"
string.len (s)
文字列を受け取ってその長さを返します。空文字列 ""
の長さは 0 です。文字列中のゼロ文字も長さに含めるので、例えば "a\000bc\000"
の長さは 5 です。
string.lower (s)
受け取った文字列をコピーし、コピーに含まれる大文字を小文字にして返します。大文字でない文字は変更されずそのままです。「大文字」の定義は現在のロケールに依存します。
string.match (s, pattern [, init])
文字列 s
で最初に見つかるパターン pattern
のマッチを返します (参照: § 6.4.1)。マッチが見つかればマッチのキャプチャを返し、見つからなければ fail を返します。キャプチャを持たない pattern
では、マッチ全体が返り値となります。三つ目の省略可能な数値引数 init
は検索を始める位置を表します。デフォルトは 1 で、負の値を使うこともできます。
string.pack (fmt, v1, v2, ...)
フォーマット文字列 fmt
に従って値 v1
, v2
, ... をバイナリ形式にシリアライズ (パック) し、その結果のバイナリ文字列を返します。フォーマット文字列については § 6.4.2 を参照してください。
string.packsize (fmt)
与えられたフォーマット文字列を使って string.pack
を実行したときに返る文字列の長さを返します。このフォーマット文字列には s
や z
のような可変長のオプションを含めることができません。フォーマット文字列については § 6.4.2 を参照してください。
string.rep (s, n [, sep])
n
個の文字列 s
を sep
で繋いだ文字列を返します。デフォルトの sep
は空文字列であり、このとき隣り合う s
の間に区切りはありません。n
が正でないときは空文字列を返します。
(この関数を一度呼び出すだけで非常に簡単にマシンのメモリを使い切ってしまえることに注意してください)
string.reverse (s)
文字列 s
を反転させた文字列を返します。
string.sub (s, i [, j])
s
の i
文字目で始まり j
文字目で終わる部分文字列を返します。i
と j
に負の値を指定することもできます。j
が与えられなかった場合には -1 とみなされます (文字列の長さと同じ意味です)。例えば string.sub(s,1,j)
は s
の最初から j
文字を返し、正の i
に対する string.sub(s,-i)
は最後の i
文字を返します。
負の i
と j
を変換した後、i
が 1 より小さければ 1 に修正され、j
が文字列の長さより大きければ文字列の長さに修正されます。この修正の後に i
が j
より大きければ、空文字列が返ります。
string.unpack (fmt, s [, pos])
フォーマット文字列 (§ 6.4.2) fmt
に従って文字列 s
に埋め込まれた値を返します (参照: string.pack
)。
string.upper (s)
受け取った文字列をコピーし、コピーに含まれる小文字を全て大文字に変更した文字列を返します。小文字以外の文字は変更されずにそのままです。「小文字」の定義は現在のロケールに依存します。
6.4.1. パターン
Lua におけるパターン (pattern) は正規表現によって表され、パターンマッチを行う関数 string.find
, string.gmatch
, string.gsub
, string.match
で使われるパターンとして解釈されます。この節ではパターンを表す文字列の構文および意味 (何にマッチするのか) を説明します。
文字クラス
文字クラス (character class) は文字の集合を表すのに利用されます。Lua で文字クラスの記述に利用できる文字の組み合わせは次の通りです:
x
: 文字x
そのものを表します。ただしx
は魔法文字 (magic character)^$()%.[]*+-?
のいずれでもないとします。.
(ドット): 任意の文字を表します。%a
: 全ての英字を表します。%c
: 全ての制御文字を表します。%d
: 全ての数字を表します。%g
: 空白を除く全ての印字可能文字を表します。%l
: 全ての小文字を表します。%p
: 全ての句読文字 (punctuation character) を表します。%s
: 全ての空白文字を表します。%u
: 全ての大文字を表します。%w
: 全ての英数字を表します。%x
: 16 進数で使われる全ての文字を表します。%x
(x
は英数字でない任意の文字): 文字x
を表します。これは魔法文字をエスケープする標準的な方法です。英数字でない文字 (魔法文字でない句読文字を含む) の前に'%'
を付けると、その文字そのものが表されます。-
[set]
:set
に含まれる全ての文字の和集合を意味するクラスを表します。昇順に並んだ二つの文字を'-'
でつなぐと、その区間にある全ての文字を表せます。またこれまでに示した%x
という形のクラスは全てset
で利用できます。例えば[%w_]
(および[_%w]
) は英数字とアンダースコアを表し、[0-7]
は 8 進数で使われる文字を表し、[0-7%l%-]
は 8 進数で使われる文字および小文字、さらに一つの文字'-'
を表します。四角括弧閉じ (
]
) をset
に加えるには、set
の最初の文字を]
とします。またハイフン-
をset
に加えるには、最初の文字または最後の文字を-
とします (両方の場合でエスケープを使うこともできます)。区間とクラスを混ぜたときに何が起こるかは定義されていません。そのため
[%a-z]
や[a-%%]
といったパターンは意味を持ちません。 [^set]
:set
の補集合を表します。set
は上と同様に解釈されます。
小文字を使って表されるクラス (%a
, %c
, ...) で大文字を使うと、元のクラスの補集合が表されます。例えば %S
は空白でない文字を表します。
「文字」や「空白」といった文字グループの定義は現在のロケールに影響を受けます。例えば [a-z]
と %l
は一致しない可能性があります。
パターンアイテム
パターンアイテム (pattern item) は次のいずれかです:
- 単一の文字クラス: クラスに含まれる任意の一文字とマッチします。
- 後ろに
*
が付いた単一の文字クラス: クラスに含まれる文字のゼロ個以上の列とマッチします。この反復アイテムは必ず可能な中で最も長い列とマッチします。 - 後ろに
+
が付いた単一の文字クラス: クラスに含まれる文字の一つ以上の列とマッチします。この反復アイテムは必ず可能な中で最も長い列とマッチします。 - 後ろに
-
が付いた単一の文字クラス: これもクラスに含まれる文字のゼロ個以上の列とマッチします。ただし*
とは異なり、この反復アイテムは必ず可能な中で最も短い列とマッチします。 - 後ろに
?
が付いた単一の文字クラス: クラスに含まれる文字一つまたは空文字列とマッチします。可能な場合には必ず一つの文字とマッチします。 %n
(n
は 1 から 9 の数字):n
個目のキャプチャされた文字列と等しい部分文字列とマッチします。%bxy
(x
とy
は異なる二つの文字):x
で始まってy
で終わる、x
とy
のバランスの取れた文字列とマッチします。つまり文字列を左から右に読んでx
が現れるたびに 1 を加え、y
が現れるたびに 1 を減らしなら数字を数えていったときに、数字が 0 となる最初のy
がマッチする部分文字列の最後の文字となります。例えば%b()
というアイテムはバランスの取れた括弧を持つ文字列とマッチします。%f[set]
(境界 (frontier) パターン):set
に含まれる文字が前にあり、set
に含まれない文字が後ろにあるような位置にある空文字列とマッチします。set
は上の説明と同様に解釈されます。マッチ対象の先頭と末尾は文字'\0'
として処理されます。
パターン
パターン (pattern) はパターンアイテムを並べたものです。キャレット (^
) がパターンの最初にあると、マッチが対象の文字列の先頭に固定されます。また $
がパターンの最後にあると、マッチが対象の文字列の末尾に固定されます。これ以外の場所にある ^
と $
は特殊な意味を持たず、文字そのものを表します。
キャプチャ
パターンの中に括弧で囲われた部分パターンがあると、それはキャプチャ (capture) となります。マッチが成功すると、キャプチャにマッチした対象文字列の部分文字列が保存され (キャプチャされ)、後で使えるようになります。キャプチャは括弧開きの位置に応じて番号が付きます。例えば "(a*(.)%w(%s*))"
というパターンでは "a*(.)%w(%s*)"
にマッチする部分文字列が最初のキャプチャとして保存され、"."
にマッチするのが二つ目のキャプチャ、"%s*"
にマッチするのが三つ目のキャプチャとなります。これらのキャプチャにはそれぞれ 1, 2, 3 の番号が付きます。
特別な場合として、()
というキャプチャは文字列における現在位置を表す数字をキャプチャします。例えばパターン "()aa()"
を文字列 flaaap
に適用すると、3 と 5 が保存された二つのキャプチャが作られます。
複数マッチ
関数 string.gsub
とイテレータ string.gmatch
は対象文字列に含まれるパターンのマッチを複数個見つけます。こういった場合には、新しいマッチは一つ前のマッチの終わりから少なくとも一バイト離れて終わる場合に限って有効とみなされます。言い換えると、あるマッチの直後にある空文字列にパターンがマッチすることはありません。例として次を考えます:
> string.gsub("abc", "()a*()", print);
--> 1 2
--> 3 3
--> 4 4
二つ目と三つ目の返り値はそれぞれ b
と c
の後にある空文字列とのマッチに対応します。a
の後にある空文字列とマッチしないのは、この空文字列が前回のマッチと同じ位置で終わっているからです。
6.4.2. pack と unpack のフォーマット文字列
string.pack
, string.packsize
, string.unpack
は第一引数にフォーマット文字列を受け取ります。これは作成あるいは読み込みの対象となるデータのレイアウトを記述する文字列です。
フォーマット文字列は変換オプションを並べたものであり、利用できるオプションは次の通りです ([n]
は省略可能な整数値を表します):
<
: リトルエンディアンに設定>
: ビッグエンディアンに設定=
: エンディアンをネイティブなものに設定![n]
: 最大アライメントをn
に設定 (デフォルトはネイティブのアライメント)b
: 符号付きバイト (char
)B
: 符号無しバイト (char
)h
: 符号付きshort
(ネイティブサイズ)H
: 符号無しshort
(ネイティブサイズ)l
: 符号付きshort
(ネイティブサイズ)L
: 符号無しshort
(ネイティブサイズ)j
:lua_Integer
J
:lua_Unsigned
T
:size_t
(ネイティブサイズ)i[n]
:n
バイトの符号付きint
(n
のデフォルトはネイティブサイズ)I[n]
:n
バイトの符号無しint
(n
のデフォルトはネイティブサイズ)f
:float
(ネイティブサイズ)d
:double
(ネイティブサイズ)n
:lua_Number
cn
:n
バイトの固定長文字列z
: ゼロ終端文字列s[n]
: 長さが先頭についた文字列 (長さはn
バイトの符号無し整数 (デフォルトはsize_t
) として表される)x
: 1 バイトのパディングXop
: オプションop
に応じてアライメントを調整する空の要素 (op
自体は無視される)- (空白): 何も表さない (無視される)
パディング・空白・設定に関するオプション ("xX <=>!"
) を除いた各オプションは string.pack
の引数および string.unpack
の返り値に対応します。
オプション "!n"
, "sn"
, "in"
, "In"
の n
は 1 から 16 までの整数です。整数の値に対してはオーバーフローのチェックが行われます。つまり string.pack
は引数が指定されたサイズに収まることを確認し、string.unpack
は読み込んだ値が Lua の整数に収まることを確認します。
任意のフォーマット文字列は先頭に "!1="
があるものとして処理されます。つまり最大アライメントは 1 (アライメント無し) で、エンディアンはネイティブです。
ネイティブなエンディアンを使う処理ではシステム全体がビッグエンディアンまたはリトルエンディアンであることが仮定されます1。string.pack
と string.unpack
はエンディアンが混ざるフォーマット文字列に対して正しく動作しません。
アライメントは次のように処理されます: 各オプションについて、オプションが要求するサイズと最大アライメントの最小値の倍数で始まるデータまでパディングが追加されます。この最小値は 2 のべきである必要があります。オプション "c"
と "z"
はアラインされず、オプション "s"
は最初の整数のアライメントに従います。
パディングは string.pack
ではゼロ埋めされ、string.unpack
では無視されます。
-
訳注: マシンのエンディアンを実行中に切り替えると正しく動かないということ。非常に古いマシンにはこの機能を持つものが存在した。Lua のフォーラム (http://lua.2524044.n2.nabble.com/NoW-A-question-about-mixed-endianness-td7685791.html) に関連する議論がある。[return]