この初版はLua 5.0向けに書かれました。後のバージョンにもほぼ当てはまりますが、いくつかの違いがあります。
第4版はLua 5.3を対象としており、Amazonやその他の書店で入手できます。
書籍を購入することで、Luaプロジェクトへの支援にもなります。


27.2 – 文字列操作

C関数がLuaから文字列引数を受け取る際、守るべきルールは2つだけです。文字列にアクセス中にスタックから文字列をポップしないこと、そして文字列を決して変更しないことです。

C関数がLuaに返す文字列を作成する必要がある場合は、状況がより厳しくなります。今度は、バッファの割り当て/解放、バッファオーバーフローなどの面倒を見るのはCコードの役目です。それでも、Lua APIはこれらのタスクを支援するいくつかの関数を提供します。

標準APIは、最も基本的な文字列操作である部分文字列の抽出と文字列連結の2つをサポートしています。部分文字列を抽出するには、基本的な操作であるlua_pushlstringが追加の引数として文字列長を取得することを覚えておいてください。したがって、文字列sの位置iからj(両端を含む)の範囲の部分文字列をLuaに渡したい場合は、次の処理を行うだけです。

    lua_pushlstring(L, s+i, j-i+1);
例として、与えられたセパレーター(1文字)に従って文字列を分割し、部分文字列を含むテーブルを返す関数が必要だとします。たとえば、次のような呼び出し
    split("hi,,there", ",")
は、テーブル{"hi", "", "there"}を返す必要があります。次のように、簡単な実装を記述できます。これは追加のバッファを必要とせず、処理できる文字列のサイズに制約を設けません。
    static int l_split (lua_State *L) {
      const char *s = luaL_checkstring(L, 1);
      const char *sep = luaL_checkstring(L, 2);
      const char *e;
      int i = 1;
    
      lua_newtable(L);  /* result */
    
      /* repeat for each separator */
      while ((e = strchr(s, *sep)) != NULL) {
        lua_pushlstring(L, s, e-s);  /* push substring */
        lua_rawseti(L, -2, i++);
        s = e + 1;  /* skip separator */
      }
    
      /* push last substring */
      lua_pushstring(L, s);
      lua_rawseti(L, -2, i);
    
      return 1;  /* return the table */
    }

文字列を連結するために、Luaはlua_concatというAPIに特定の関数を提供しています。これはLuaの..演算子と同等です。数値を文字列に変換し、必要な場合はメタメソッドをトリガーします。さらに、一度に2つ以上の文字列を連結できます。lua_concat(L, n)を呼び出すと、スタックの最上位にあるn個の値が連結(およびポップ)され、その結果が最上位に残されます。

もう1つの便利な関数はlua_pushfstringです。

    const char *lua_pushfstring (lua_State *L,
                                 const char *fmt, ...);
これはC関数のsprintfに多少似ており、書式文字列といくつかの追加の引数に従って文字列を作成します。ただし、sprintfとは異なり、バッファを提供する必要はありません。Luaは、必要に応じて、動的に文字列を作成します。バッファオーバーフローなどの心配はありません。関数は結果の文字列をスタックにプッシュし、そのポインタを返します。現在、この関数は%%(文字`%´用)、%s(文字列用)、%d(整数用)、%f(Luaの数値、つまり倍精度浮動小数点数用)、および%c(整数を受け入れて文字として書式設定します)のディレクティブのみを受け入れます。オプション(幅や精度など)は受け入れません。

lua_concatlua_pushfstringは、少数の文字列のみを連結したい場合に役立ちます。ただし、多数の文字列(または文字)を連結する必要がある場合、セクション11.6で見たように、1つずつアプローチすると非常に非効率になる可能性があります。代わりに、補助ライブラリが提供するバッファ機能を使用できます。Auxlibは、これらのバッファを2つのレベルで実装しています。最初のレベルは、I/O操作のバッファに似ています。ローカルバッファに小さな文字列(または個々の文字)を収集し、バッファがいっぱいになったときにそれらをLuaに(lua_pushlstringで)渡します。2番目のレベルは、セクション11.6で見たスタックアルゴリズムのlua_concatとバリアントを使用して、複数のバッファフラッシュの結果を連結します。

auxlibのバッファ機能を詳細に説明するために、その使用法の簡単な例を見てみましょう。次のコードは、ファイルlstrlib.cから直接、string.upperの実装を示しています。

    static int str_upper (lua_State *L) {
      size_t l;
      size_t i;
      luaL_Buffer b;
      const char *s = luaL_checklstr(L, 1, &l);
      luaL_buffinit(L, &b);
      for (i=0; i<l; i++)
        luaL_putchar(&b, toupper((unsigned char)(s[i])));
      luaL_pushresult(&b);
      return 1;
    }
auxlibのバッファを使用する最初のステップは、型luaL_Bufferの変数を宣言し、次にluaL_buffinitの呼び出しでそれを初期化することです。初期化後、バッファは状態Lのコピーを保持するため、バッファを操作する他の関数を呼び出すときに渡す必要はありません。マクロluaL_putcharは、単一の文字をバッファに入れます。Auxlibは、明示的な長さの文字列をバッファに入れるためのluaL_addlstringと、ゼロ終端文字列を配置するためのluaL_addstringも提供します。最後に、luaL_pushresultはバッファをフラッシュし、スタックの一番上に最終的な文字列を残します。これらの関数のプロトタイプは次のとおりです。
    void luaL_buffinit (lua_State *L, luaL_Buffer *B);
    void luaL_putchar (luaL_Buffer *B, char c);
    void luaL_addlstring (luaL_Buffer *B, const char *s,
                                          size_t l);
    void luaL_addstring (luaL_Buffer *B, const char *s);
    void luaL_pushresult (luaL_Buffer *B);

これらの関数を使用すると、バッファの割り当て、オーバーフロー、その他の詳細について心配する必要はありません。見たように、連結アルゴリズムは非常に効率的です。str_upper関数は、巨大な文字列(1MB以上)を問題なく処理します。

auxlibバッファを使用する場合は、1つの詳細について心配する必要があります。バッファにものを入れると、Luaスタックにいくつかの中間結果が保持されます。したがって、バッファの使用を開始する前に、スタックのトップが元の場所のままになるとは想定できません。さらに、バッファを使用している間(別のバッファを作成する場合でも)スタックを他のタスクに使用できますが、これらの使用のプッシュ/ポップカウントは、バッファにアクセスするたびにバランスをとる必要があります。この制限が厳しすぎる状況が1つあります。つまり、Luaから返された文字列をバッファに入れたい場合です。このような場合、スタックからポップした後にLuaからの文字列を使用することは決してないため、バッファに追加する前に文字列をポップすることはできません。しかし、ポップする前に文字列をバッファに追加することもできません。そうすると、スタックが間違ったレベルになってしまいます。言い換えれば、次のようなことはできません。

    luaL_addstring(&b, lua_tostring(L, 1));   /* BAD CODE */
これはよくある状況であるため、auxlibはスタックの最上位にある値をバッファに追加するための特別な関数を提供します。
    void luaL_addvalue (luaL_Buffer *B);
もちろん、最上位の値が文字列または数値でない場合にこの関数を呼び出すとエラーになります。