Manipulasi String
27.2 Manipulasi String
Saat suatu fungsi C menerima sebuah argument string dari Lua, hanya terdapat dua aturan yang harus diamati, yaitu tidak mengeluarkan string dari stack selagi mengakses dan jangan pernah memodifikasi string.
Beberapa hal dapat lebih menuntut saat suatu fungsi C harus menciptakan sebuah string untuk kembali ke Lua. Sekarang, hal ini tergantung dari kode C untuk memperhatikan pengalokasian atau dealokasi buffer, buffer yang penuh (buffer overflow), dan semacamnya. Meskipun demikian, API Lua menyediakan beberapa fungsi untuk membantu tugas tersebut.
API standar menyediakan dukungan untuk dua operasi string paling dasar, yaitu substring extraction (pengambilan substring) dan string concatenation (penggabungan string). Untuk meng- ekstrak / mengambil sebuah substring, ingatlah bahwa operasi dasar lua_pushlstring mendapatkan panjang string sebagai suatu argumen ekstra. Oleh karena itu, jika anda ingin memberikan suatu substring kepada Lua dari suatu string s yang berkisar antara posisi i sampai j (dihitung juga), yang harus anda lakukan adalah
lua_pushlstring(L, s+i, j-i+1);
Sebagai suatu contoh, kira-kira anda menginginkan suatu fungsi yang dapat membagi sebuah string menurut sebuah pemisah yang telah diberikan (sebuah karakter tunggal) dan mengembalikan table dengan substring tersebut. Misalnya, panggilan
split("hi,,there", ",")
seharusnya mengembalikan tabel {"hi", "", "there"}. Kita telah dapat menuliskan suatu implementasi sederhana seperti berikut. Hal ini tidak memerlukan buffer (tempat penyimpanan) ekstra dan tidak meletakkan batasan pada ukuran string yang masih dapat diatasi.
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); /* push last substring */ lua_pushstring(L, s); lua_rawseti(L, -2, i);
Untuk menggabungkan string, Lua menyediakan suatu fungsi spesifik di dalam API, yang disebut dengan lua_concat. Lua_concat bernilai sama dengan operator .. pada Lua, yaitu mengkonversi bilangan - bilangan menjadi string dan memicu metamethodes jika diperlukan. Lebih dari itu, lua_concat dapat menggabungkan lebih dari dua string pada saat yang sama. Panggilan lua_concat(L, n) akan menggabungkan (dan mengeluarkan) nilai-n pada puncak stack dan meninggalkan hasil pada puncaknya.
Fungsi bantuan (help) lainnya adalah lua_pushfstring :
const char *lua_pushfstring (lua_State *L,
const char *fmt, ...);
Lua_pushfstring hampir mirip dengan fungsi C yaitu sprintf, lua_pushfstring membuat sebuah string menurut suatu format string dan beberapa argumen ekstra. Tidak seperti sprintf, bagaimanapun, anda tidak perlu menyediakan sebuah buffer. Lua secara dinamis membuat string untuk anda, sebesar yang dibutuhkan. Tidak ada kekhawatiran mengenai buffer overflow dan semacamnya. Fungsi tersebut mendorong string hasil pada stack dan mengembalikan sebuah pointer kedalamnya. Umumnya, fungsi ini hanya menerima %% (untuk karakter ‘%’), %s (untuk string), %d (untuk integer), %f (untuk bilangan pada Lua, yaitu double), dan %c (menerima integer dan memformatnya menjadi karakter) secara langsung. Fungsi ini tidak menerima pilihan apa pun (misalnya jarak atau ketelitian).
lua_concat dan lua_pushfstring sangat berguna saat anda ingin menggabungkan hanya untuk beberapa string. Bagaimanapun, jika kita ingin menggabungkan banyak string (atau karakter) secara bersama-sama, suatu pendekatan one-by-one tidaklah efisien, seperti yang kita lihat pada bagian 11.6. Sebagai gantinya, kita dapat menggunakan fasilitas buffer yang disediakan oleh auxiliary library . Auxlib menerapkan buffer-buffer ini ke dalam dua tingkat. Tingkat pertama mirip dengan buffer pada operasi I/O, yaitu mengumpulkan string yang kecil (atau karakter individual) pada buffer local dan memberikannya ke Lua (dengan lua_pushlstring) saat buffer sedang penuh. Tingkat yang kedua menggunakan lua_concat dan suatu varian dari algoritma stack yang kita lihat di bagian 11.6 untuk menggabungkan hasil dari berbagai aliran buffer.
Untuk menguraikan fasilitas buffer dari auxlib secara lebih terinci, mari kita melihat contoh yang sederhana tentang penggunaannya. Kode berikutnya memperlihatkan penerapan string.upper, benar-benar dari file lstrlib.c:
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;
Tahap pertama dalam menggunakan suatu buffer dari auxlib adalah mendeklarasikan sebuah variabel dengan tipe luaL_Buffer dan kemudian menginisialisasikannya dengan sebuah panggilan kepada luaL_buffinit. Setelah melakukan inisialisasi, buffer menyimpan suatu copy dari status L, jadi kita tidak perlu melewatinya saat memanggil fungsi lain yang memanipulasi buffer tersebut. Makro luaL_putchar meletakkan sebuah karakter tunggal ke dalam buffer. Auxlib juga menawarkan luaL_addlstring, untuk meletakkan sebuah string dengan panjang yang eksplisit / tegas ke dalam buffer, dan luaL_addstring, untuk meletakkan sebuah string batasan nol (zero-terminated string). Akhirnya, luaL_pushresult mengalir ke buffer dan meninggalkan string di puncak stack. Prototype dari fungsi-fungsi tersebut adalah sebagai berikut :
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);
Dalam menggunakan fungsi-fungsi tersebut, kita tidak perlu khawatir dengan pengalokasian buffer , overflows dan hal lainnya. Seperti yang kita lihat, penggabungan algoritma sungguh efisien. Fungsi str_upper dapat mengatasi string yang sangat besar (lebih dari 1MB) tanpa masalah.
Saat anda menggunakan buffer auxlib , anda harus cemas akan satu rincian. Seperti anda meletakkan sesuatu ke dalam buffer, hal itu menyimpan beberapa hasil perantara (intermediate) di dalam stack pada Lua. Oleh karena itu, anda tidak dapat berasumsi bahwa puncak stack akan tinggal dimana sebelumnya anda mulai menggunakan buffer tersebut. Lebih dari itu, walaupun anda dapat menggunakan stack untuk tugas lainnya selagi menggunakan buffer (bahkan untuk membangun buffer lain), penghitungan push / pop untuk penggunaannya harus seimbang setiap saat anda mengakses buffer tersebut. Di sini terdapat situasi yang jelas dimana pembatasan ini terlalu keras, yakni saat anda ingin meletakan sebuah string yang dikembalikan dari Lua ke dalam buffer. Pada kasus seperti itu, anda tidak dapat mengeluarkan string tersebut sebelum menambahkannya ke dalam buffer, karena anda tidak pernah menggunakan sebuah string dari Lua setelah mengeluarkannya dari dalam stack, tapi anda juga tidak dapat menambah string ke dalam buffer sebelum mengeluarkannya, sebab kemudian stack berada pada tingkatan yang salah. Dengan kata lain, anda tidak dapat melakukan hal seperti ini :
luaL_addstring(&b, lua_tostring(L, 1)); /* BAD CODE */
Karena ini merupakan situasi yang umum, auxlib menyediakan sebuah fungsi khusus untuk menambahkan nilai pada puncak stack ke dalam buffer :
void luaL_addvalue (luaL_Buffer *B);
Sudah pasti merupakan suatu kesalahan untuk memanggil fungsi ini jika nilai pada puncak bukanlah string atau bilangan.