精華區beta mud_sanc 關於我們 聯絡資訊
中階 LPC Descartes of Borg November 1993 第五章: 高級的字串處理 5.1 字串是什麼 基礎 LPC 課本教你字串是簡單資料型態. LPC 一般來說也這樣處理字串. 不過, 在底下的 driver 程式是以 C 寫成的, 它沒有字串資料型態. driver 實際上 視字串為複雜資料型態, 由字元的陣列所組成 ---- 一種簡單的 C 資料型態. LPC 在另一方面來說, 並不認識字元資料型態 (可能有一兩種 driver 認得字元 資料型態, 但是一般上來說不認得) . 其結果是, 你可以對字串作一些類似陣列 的處理, 而其他的 LPC 資料型態則否. 你第一個該學與字串有關的外部函式是 strlen(). 這個外部函式傳回一個 LPC 字串中, 以字元為單位的長度. 就從這個外部函式的行為來說, 你可以看到 driver 視字串由更小的元素所組成, 並以此處理之. 在本章之中, 你將學到如 何以更基礎的字元和子字串層次處理字串. 5.2 字串是字元陣列 你可以對陣列作的事, 幾乎都可以用於字串, 除了在字元基礎上指定其值以外. 最基本的是, 你實際上可以在字元前後加上 '' (單引號) 將它當作字元常數. 所以 'a' 和 "a" 在 LPC 中是完全不一樣的東西. 'a' 表示是一個字元, 不 能用於指定敘述或其他的運算式中 (比較兩值的式子除外). 另一方面, "a" 是 由單一字元所組成的字串. 你可以加減其他的字串, 並指定它為變數值. 對字串變數來說, 你可以存取單獨的字元跟字元常數作比較. 其語法與陣列相同. 換句話說, 以下敘述: if(str[2] == 'a') 是一個有效的 LPC 敘述, 將 str 的第二個字元與 'a' 字元作比較. 你必須 非常小心, 你不會把陣列元素與字元相比較, 也不會把字串的字元與字串相比較. LPC 也讓你使用範圍運算子 (range operator) .. 一起存取多個字元: if(str[0..1] == "ab") 換句話講, 你可以看 str 字串中第 0 到 1 個字元是什麼. 如同陣列, 你必 須小心使用索引或範圍運算子, 才不會試著參考比最後一個索引還大的索引數. 這樣會導致錯誤. 現在你可以看到字串和陣列之間的幾處相似點: 1) 兩者你都可以藉由索引存取個別的元素. a) 字串個別的元素是字元. b) 陣列個別的元素符合陣列的資料型態. 2) 你可以運算一個範圍之內的值. a) 例: "abcdef"[1..3] 是 "bcd" 字串 b) 例: ({ 1, 2, 3, 4, 5 })[1..3] 整數陣列 ({ 2, 3, 4 }) 當然, 你應該記住基本上的相異點: 字串不是由更基本的 LPC 資料型態所組成. 換句話說, 你沒辦法將值指定給字串中單獨的字元. 5.3 sscanf() 外部函式 不使用 sscanf(), 你在 LPC 中就無法更有效處理字串. 沒有它, 你就只能處 理傳給命令函式之命令敘述的整個字串. 換句話講, 你沒辦法處理一個像 "give sword to leo" 的命令, 因為你沒有方法分析 "sword to leo" 的成分. 像這種使用多個參數的命令, 它們使用 sscanf() 外部函式讓命令更接近英文. 大部分的人都覺得 sscanf() 的說明文件相當難懂. 這個函式並不算是非常符合 說明文件中的格式. 如同前述, 這函式用於讀取字串, 並分析出有用的成分. 技 術上來說, 它讀取一個字串, 並分析成一個或一個以上的各種型態之變數. 舉個 例子: int give(string str) { string what, whom; if(!str) return notify_fail("Give what to whom?\n"); if(sscanf(str, "%s to %s", what, whom) != 2) return notify_fail("Give what to whom?\n"); ... 其餘的 give 程式碼 ... } sscanf() 外部函式需要三個以上的參數. 第一個參數是你想分析的字串. 第二 個參數稱為控制字串. 控制字串是一個模型, 表示原來所寫的字串格式為何, 它 該如何分析. 其餘的參數是變數, 你會由控制字串指定值給它們. 控制字串由三種不同的元素組成: 1) 常數 2) 被分析的變數參數 3) 要丟棄的變數 在 sscanf() 之中你變數參數的數目必須與控制字串中第二種元素的數目相等. 在上述的例子中, 控制字串是 "%s to %s", 是三個元素的控制字串, 由一個常 數部分 (" to ") 和兩個被分析的變數參數 ("%s") 組成. 在此沒有要丟棄的變 數. 控制字串基本上指出函式應該在 str 字串中尋找 " to ". 在此常數之前不管 是什麼東西, 會以字串型態放在第一個變數參數中. 同理, 常數後面的任何東西, 會放在第二個. 變數元素以 % 符號跟著一個解釋碼表示. 如果變數元素要丟棄, % 符號之後跟 著 * 號, 再跟著解釋變數的碼. 常見的變數元素解釋碼是 s 表示字串, 和 d 表示整數. 另外, 你的 mudlib 可能支援其他的轉換碼, 像是 f 表示浮點數. 所以在上述的兩個例子中, 控制字串中的 %s 指出原來字串中, 不管什麼東西出 現在對應的位置上, 就會以字串被分析成新的變數. 來一個簡單的練習. 你要怎麼把字串 "145" 轉成一個整數 ?? 答案: int x; sscanf("145", "%d", x); sscanf() 執行之後, x 會等於整數 145. 無論何時, 你使用控制字串分析一個字串, 函式會尋找原來字串中第一次出現第 一個常數的地方. 舉個例, 如果你的字串是 "magic attack 100", 並撰寫了以 下的程式碼: int improve(string str) { string skill; int x; if(sscanf(str, "%s %d", skill, x) != 2) return 0; ... } 你會發現你得到 sscanf() 錯誤的傳回值 (稍後再多討論傳回值) . 控制字串 "%s %d", 是由被分析的兩個變數和一個常數組成的. 常數是 " ". 所以函式尋 找原字串中第一次出現 " " 的地方, 把 " " 之前的任何東西放入 skill, 並 試著把 " " 之後的任何東西放入 x. 這樣一來, 把 "magic attack 100" 分成 "magic" 和 "attack 100" 兩個部分. 但是函式沒辦法把 "attack 100" 變成一 個整數, 所以它傳回 1, 表示有一個變數值成功分析出來 ("magic" 轉 skill). 也許你已經從上面的例子中猜到, 但是 sscanf() 外部函式傳回一個整數, 是從 原字串成功分析出來的變數值個數. 這裡有些傳回值的例子讓你看看: sscanf("swo rd descartes", "%s to %s", str1, str2) 傳回: 0 sscanf("swo rd descartes", "%s %s", str1, str2) 傳回: 2 sscanf("200 gold to descartes", "%d %s to %s", x, str1, str2) 傳回: 3 sscanf("200 gold to descartes", "%d %*s to %s", x, str1) 傳回: 2 x 是一個整數, 而 str1 和 str2 是字串. 5.4 總結 LPC 字串可以視為字元的陣列, 但是你要牢記的是, LPC 並沒有字元資料型態 (絕大多數, 但不是所有的 driver 皆是). 既然字元不是一種真正的 LPC 資 料型態, 你就無法像其他資料型態一樣, 處理一個 LPC 字串中單獨的字元. 注 意, 雖然字串和陣列之間的相似關係可以讓你比較容易瞭解字串的範圍運算子和 索引的概念, 兩者仍有不同之處. 雖然除了 sscanf() 之外, 高級的字串處理仍牽涉到其他的外部函式, 它們卻不 常需要用到. 你應該閱讀你 mud 中這些外部函式的 man 或 help 檔案: explode() 、implode() 、replace_string()、sprintf(). 這些都是非常有價 值的工具, 尤其是你想在 mudlib 層次上撰寫程式碼之時. Copyright (c) George Reese 1993 譯者: Spock of the Final Frontier 98.Jul.26.