newLISP 字串簡介
字串處理工具是程式語言的重要組成部分。newLISP 擁有許多易於使用且功能強大的字串處理工具,如果您特定需求沒有得到滿足,可以輕鬆地將更多工具新增到您的工具箱中。
這是一次對 newLISP 字串樂團 的導覽。
您可以透過三種方式編寫字串
- 用雙引號括起來
- 用花括號包圍
- 由標記程式碼標記
像這樣
(set 's "this is a string")
(set 's {this is a string})
(set 's [text]this is a string[/text])
所有三種方法都可以處理最多 2048 個字元的字串。對於超過 2048 個字元的字串,始終使用 [text] 和 [/text] 標籤來包圍字串。
如果您希望處理跳脫字元,例如 \n 和 \t,或程式碼數字 (\046),則始終使用第一種方法,即引號。
(set 's "this is a string \n with two lines")
(println s)
this is a string with two lines
(println "\110\101\119\076\073\083\080") ; decimal ASCII
newLISP
(println "\x6e\x65\x77\x4c\x49\x53\x50") ; hex ASCII
newLISP
雙引號字元必須用反斜槓轉義,反斜槓也必須用反斜槓轉義,如果您希望它們出現在字串中。
對於小於 2048 個字元的字串,如果您不希望處理任何跳脫字元,請使用第二種方法,即花括號(或“大括號”)。
(set 's {strings can be enclosed in \n"quotation marks" \n })
(println s)
strings can be enclosed in \n"quotation marks" \n
這是一種非常有用的編寫字串的方式,因為您不必擔心在每個引號字元前加反斜槓,或在其他反斜槓前加反斜槓。您可以在花括號字串內巢狀花括號對,但不能有未匹配的花括號。我喜歡使用花括號來編寫字串,因為它們朝向正確的方向(而普通 愚蠢 的引號則沒有),而且您的文字編輯器可能會平衡和匹配它們。
第三種方法,使用 [text] 和 [/text] 標記,適用於跨越多行的較長文字字串,並且當 newLISP 輸出大量文字時會自動使用。同樣,您不必擔心可以和不能包含哪些字元 - 您可以隨意放入任何字元,顯而易見的例外是 [/text]。跳脫字元,例如 \n 或 \046,也不會被處理。
(set 'novel (read-file {my-latest-novel.txt}))
;->
[text]
It was a dark and "stormy" night...
...
The End.
[/text]
如果您想知道字串的長度,請使用 length
(length novel)
;-> 575196
newLISP 可以輕鬆處理數百萬個字元的字串。
而不是 length,使用 utf8len 獲取 Unicode 字串的長度
(utf8len (char 955))
;-> 1
(length (char 955))
;-> 2
許多函式,例如檔案讀取函式,會為您返回字串或字串列表。但是,如果您想從頭開始構建字串,一種方法是從 char 函式開始。它將提供的數字轉換為具有該程式碼數字的等效字元字串。它也可以反轉操作,將提供的字元字串轉換為其等效程式碼數字。)
(char 33)
;-> "!"
(char "!")
;-> 33
(char 955) ; Unicode lambda character, decimal code
;-> "\206\187"
(char 0x2643) ; Unicode symbol for Jupiter, hex code
;-> "\226\153\131"
當您執行支援 Unicode 的 newLISP 版本時,最後兩個示例可用。由於 Unicode 以十六進位制為主,因此您可以為 char 提供以 0x 開頭的十六進位制數字。要檢視實際字元,請使用列印命令
(println (char 955))
λ
;-> "\206\187"
(println (char 0x2643))
♃
;-> "\226\140\152"
(println (char (int (string "0x" "2643")))) ; equivalent
♃
;-> "\226\140\152"
反斜槓數字是 println 函式的結果,可能是 Unicode 字元的位元組值。
您可以使用 char 以其他方式構建字串
(join (map char (sequence (char "a") (char "z"))))
;-> "abcdefghijklmnopqrstuvwxyz"
這使用 char 找出 a 和 z 的 ASCII 程式碼數字,然後使用 sequence 生成這兩個程式碼數字之間的列表。然後將 char 函式對映到列表的每個元素上,從而生成一個字串列表。最後,透過 join 將此列表轉換為單個字串。
join 在構建字串時還可以使用分隔符
(join (map char (sequence (char "a") (char "z"))) "-")
;-> "a-b-c-d-e-f-g-h-i-j-k-l-m-n-o-p-q-r-s-t-u-v-w-x-y-z"
與 join 類似的是 append,它直接對字串起作用
(append "con" "cat" "e" "nation")
;-> "concatenation"
但更實用的是 string,它將任何數字、列表和字串集合轉換為單個字串。
(string '(sequence 1 10) { produces } (sequence 1 10) "\n")
;-> (sequence 1 10) produces (1 2 3 4 5 6 7 8 9 10)
請注意,第一個列表沒有被求值(因為它被引用),但第二個列表被求值以生成一個數字列表,並且結果列表 - 包括括號 - 被轉換為字串。
string 函式結合花括號和標記等各種字串標記,是將變數的值包含在字串中的一種方法
(set 'x 42)
(string {the value of } 'x { is } x)
;-> "the value of x is 42"
您還可以使用 format 來組合字串和符號值。請參閱格式化字串。
dup 製作副本
(dup "spam" 10)
;-> "spamspamspamspamspamspamspamspamspamspam"
而 date 生成一個日期字串
(date)
;-> "Wed Jan 25 15:04:49 2006"
或者,您可以給它一個自 1970 年以來的秒數進行轉換
(date 1230000000)
;-> "Tue Dec 23 02:40:00 2008"
請參閱處理日期和時間。
現在您已經有了字串,還有許多函式可以對其進行操作。其中一些是破壞性函式 - 它們永久更改字串,可能會永久丟失資訊。另一些是構造性的,生成一個新的字串,而不影響舊字串。請參閱破壞性函式。
reverse 是破壞性的
(set 't "a hypothetical one-dimensional subatomic particle")
(reverse t)
;-> "elcitrap cimotabus lanoisnemid-eno lacitehtopyh a"
現在 t 已永久更改。但是,大小寫轉換函式不是破壞性的,它們會生成新的字串,不會損害舊字串
(set 't "a hypothetical one-dimensional subatomic particle")
(upper-case t)
;-> "A HYPOTHETICAL ONE-DIMENSIONAL SUBATOMIC PARTICLE"
(lower-case t)
;-> "a hypothetical one-dimensional subatomic particle"
(title-case t)
;-> "A hypothetical one-dimensional subatomic particle"
如果您知道要提取字串的哪個部分,請使用以下建構函式之一
(set 't "a hypothetical one-dimensional subatomic particle")
(first t)
;-> "a"
(rest t)
;-> " hypothetical one-dimensional subatomic particle"
(last t)
;-> "e"
(t 2) ; counting from 0
;-> "h"
您也可以將此技術應用於列表。請參閱從列表中選擇專案。
slice 會為您提供現有字串的切片,從切口處向前計數(正整數)或從末尾處向後計數(負整數),以獲得指定數量的字元或直到指定的位點
(set 't "a hypothetical one-dimensional subatomic particle")
(slice t 15 13)
;-> "one-dimension"
(slice t -8 8)
;-> "particle"
(slice t 2 -9)
;-> "hypothetical one-dimensional subatomic"
(slice "schwarzwalderkirschtorte" 19 -1)
;-> "tort"
也有一個快捷方式可以做到這一點。將所需的開始和長度放在字串前的列表中
(15 13 t)
;-> "one-dimension"
(0 14 t)
;-> "a hypothetical"
如果您不想要連續的字元,而是要為新字串挑選一些字元,請使用 select 以及一系列字元索引號
(set 't "a hypothetical one-dimensional subatomic particle")
(select t 3 5 24 48 21 10 44 8)
;-> "yosemite"
(select t (sequence 1 49 12)) ; every 12th char starting at 1
;-> " lime"
這對於查詢隱藏在文字中的秘密編碼訊息很有用。
trim 和 chop 都是構造性字串編輯函式,從原始字串的末端向內工作。
chop 從末尾開始工作
(chop t) ; defaults to the last character
;-> "a hypothetical one-dimensional subatomic particl"
(chop t 9) ; chop 9 characters off
;-> "a hypothetical one-dimensional subatomic"
trim 可以從兩端移除字元
(set 's " centred ")
(trim s) ; defaults to removing spaces
;-> "centred"
(set 's "------centred------")
(trim s "-")
;-> "centred"
(set 's "------centred******")
(trim s "-" "*") ; front and back
;-> "centred"
您已經看到 push 和 pop 在向列表新增和刪除專案。它們也適用於字串。使用 push 向字串新增字元,使用 pop 從字串中刪除一個字元。除非您指定索引,否則字串將被新增到字串開頭或從字串開頭刪除。
(set 't "some ")
(push "this is " t)
(push "text " t -1)
;-> t is now "this is some text"
pop 始終返回彈出的內容,但 push 返回修改後的操作目標。當您想要拆分字串並按順序處理各個部分時,它很有用。例如,要列印 newLISP 版本號,該版本號儲存為 4 位或 5 位整數,請使用以下方法
(set 'version-string (string (sys-info -2)))
; eg: version-string is now "10303"
(set 'dev-version (pop version-string -2 2)) ; always two digits
; dev-version is "03", version-string is "103"
(set 'point-version (pop version-string -1)) ; always one digit
; point-version is "3", version-string is now "10"
(set 'version version-string) ; one or two digits
(println version "." point-version "." dev-version)
10.3.03
從字串的右側開始工作並使用 pop 提取資訊並將其刪除在一個操作中更容易。
更改字串內部字元有兩種方法。要麼使用字元的索引號,要麼指定要查詢或更改的子字串。
要透過索引號更改字元,請使用 **setf**,這是用於更改字串、列表和陣列的通用函式
(set 't "a hypothetical one-dimensional subatomic particle")
(setf (t 0) "A")
;-> "A"
t
;-> "A hypothetical one-dimensional subatomic particle"
您也可以使用 **nth** 和 **setf** 來指定位置
(set 't "a hypothetical one-dimensional subatomic particle")
;-> "a hypothetical one-dimensional subatomic particle"
(setf (nth 0 t) "A")
;-> "A"
t
;-> "A hypothetical one-dimensional subatomic particle"
以下是如何“遞增”字串的第一個(第零個)字母
(set 'wd "cream")
;-> "cream"
(setf (wd 0) (char (+ (char $it) 1)))
;-> "d"
wd
;-> "dream"
$it包含 **setf** 表示式第一部分找到的值,並且它的數值被遞增以形成第二部分。
如果您不想(或不能)使用索引號或字元位置,請使用 **replace**,這是一個功能強大的破壞性函式,它對字串執行各種有用的操作。以以下形式使用它
(replace old-string source-string replacement)
所以
(set 't "a hypothetical one-dimensional subatomic particle")
(replace "hypoth" t "theor")
;-> "a theoretical one-dimensional subatomic particle"
**replace** 是破壞性的,但是如果您想以構造性方式使用 **replace** 或其他破壞性函式的副作用,而無需修改原始字串,請使用 **copy** 函式
(set 't "a hypothetical one-dimensional subatomic particle")
(replace "hypoth" (copy t) "theor")
;-> "a theoretical one-dimensional subatomic particle"
t
;-> "a hypothetical one-dimensional subatomic particle"
副本被 **replace** 修改。原始字串 *t* 不會受到影響。
**replace** 是 newLISP 函式組中的一員,這些函式接受正則表示式以定義文字中的模式。對於大多數函式,您需要在表示式末尾新增一個額外的數字,它指定正則表示式操作的選項:0 表示基本匹配,1 表示不區分大小寫的匹配,等等。
(set 't "a hypothetical one-dimensional subatomic particle")
(replace {h.*?l(?# h followed by l but not too greedy)} t {} 0)
;-> "a one-dimensional subatomic particle"
有時我會在正則表示式中添加註釋,以便幾天後閱讀程式碼時知道我試圖做什麼。在 (?# 和後面的右括號之間的文字會被忽略。
如果您樂於使用與 Perl 相容的正則表示式 (PCRE),那麼您會喜歡 **replace** 及其使用正則表示式的兄弟姐妹(**find**、**regex**、**find-all**、**parse**、**starts-with**、**ends-with**、**directory** 和 **search**)。完整詳細資訊請參閱 newLISP 參考手冊。
您必須將您的模式引導透過 newLISP 閱讀器和正則表示式處理器。請記住引號括起來的字串和花括號括起來的字串之間的區別?引號允許處理跳脫字元,而花括號則不處理。花括號有一些優勢:它們在視覺上彼此相對,它們沒有讓人困惑的智慧和啞版本,您的文字編輯器可能會為您平衡它們,並且它們允許您在字串中使用更常見的引號字元,而無需始終對其進行轉義。但是,如果您使用引號,則必須將反斜槓加倍,以便單個反斜槓完好無損地到達正則表示式處理器
(set 'str "\s")
(replace str "this is a phrase" "|" 0) ; oops, not searching for \s (white space) ...
;-> thi| i| a phra|e ; but for the letter s
(set 'str "\\s")
(replace str "this is a phrase" "|" 0)
;-> this|is|a|phrase ; ah, better!
**replace** 使用一組系統變數 $0,$1,$2,一直到 $15,來更新匹配項。它們指的是模式中的括號表示式,等效於您可能熟悉的 \1,\2(如果您使用過 grep)。例如
(set 'quotation {"I cannot explain." She spoke in a low, eager voice,
with a curious lisp in her utterance. "But for God's sake do what I
ask you. Go back and never set foot upon the moor again."})
(replace {(.*?),.*?curious\s*(l.*p\W)(.*?)(moor)(.*)}
quotation
(println {$1 } $1 { $2 } $2 { $3 } $3 { $4 } $4 { $5 } $5)
4)
$1 "I cannot explain." She spoke in a low $2 lisp $3 in her utterance. "But for God's sake do what I ask you. Go back and never set foot upon the $4 moor $5 again."
在這裡,我們查找了五個模式,它們由任何以逗號開頭並以單詞 curious 結尾的字串隔開。$0 儲存匹配的表示式,$1 儲存第一個括號內的子表示式,依此類推。
如果您更喜歡使用引號而不是我在這裡使用的花括號,請記住,某些字元必須用反斜槓進行轉義。
前面的示例說明了 **replace** 的一個重要功能,即替換不必僅僅是一個簡單的字串或列表,它可以是任何 newLISP 表示式。每次找到模式時,都會對替換表示式進行求值。您可以使用它來提供動態計算的替換值,或者您可以對找到的文字執行任何其他操作。甚至可以對與找到的文字毫無關係的表示式進行求值。
以下是一個示例:搜尋字母 t 後面是字母 h 或任何母音,並打印出 **replace** 找到的組合
(set 't "a hypothetical one-dimensional subatomic particle")
(replace {t[h]|t[aeiou]} t (println $0) 0)
th ti to ti
;-> "a hypothetical one-dimensional subatomic particle"
對於找到的每個匹配的文字片段,第三個表示式
(println $0)
被求值。這是在函式執行時檢視正則表示式引擎在做什麼的好方法。在這個例子中,原始字串似乎沒有改變,但實際上它確實改變了,因為(println $0)做了兩件事:它列印了字串,並且它將值返回給 **replace**,從而用自身替換找到的文字。無形縫合!如果替換表示式沒有返回字串,則不會發生替換。
您還可以執行其他有用的操作,例如構建匹配項列表以供以後處理,並且您可以使用 newLISP 系統變數和任何其他函式來使用找到的任何文字。
在下一個示例中,我們查詢字母 a、e 或 c,並將每個出現的字母強制轉換為大寫
(replace "a|e|c" "This is a sentence" (upper-case $0) 0)
;-> "This is A sEntEnCE"
作為另一個示例,這裡有一個簡單的搜尋和替換操作,它統計字母 'o' 在字串中出現的次數,並將原始字串中的每個出現替換為到目前為止的計數。替換是一個表示式塊,這些表示式塊被分組到一個 **begin** 表示式中。每次找到匹配項時,都會對該塊進行求值
(set 't "a hypothetical one-dimensional subatomic particle")
(set 'counter 0)
(replace "o" t
(begin
(inc counter)
(println {replacing "} $0 {" number } counter)
(string counter)) ; the replacement text should be a string
0)
replacing "o" number 1 replacing "o" number 2 replacing "o" number 3 replacing "o" number 4 "a hyp1thetical 2ne-dimensi3nal subat4mic particle"
**println** 的輸出不會出現在字串中;整個 **begin** 表示式的最終值為計數器的字串版本,因此它將被插入到字串中。
以下是一個關於 **replace** 的另一個例子。假設我有一個文字檔案,內容如下
1 a = 15 2 another_variable = "strings" 4 x2 = "another string" 5 c = 25 3x=9
我想編寫一個 newLISP 指令碼,以 10 為倍數重新編號行,從 10 開始,並將文字對齊,使等號對齊,如下所示
10 a = 15 20 another_variable = "strings" 30 x2 = "another string" 40 c = 25 50 x = 9
(我不知道這是什麼語言!)
以下指令碼將完成此操作
(set 'file (open ((main-args) 2) "read"))
(set 'counter 0)
(while (read-line file)
(set 'temp
(replace {^(\d*)(\s*)(.*)} ; the numbering
(current-line)
(string (inc counter 10) " " $3)
0))
(println
(replace {(\S*)(\s*)(=)(\s*)(.*)} ; the spaces around =
temp
(string $1 (dup " " (- 20 (length $1))) $3 " " $5)
0)))
(exit)
我在 **while** 迴圈中使用了兩個 **replace** 操作,以使事情更清晰。第一個操作將一個臨時變數設定為 replace 操作的結果。搜尋字串({^(\d*)(\s*)(.*)})是一個正則表示式,它查詢一行開頭的任何數字,後面跟著一些空格,後面跟著 *任何東西*。替換字串((string (inc counter 10) " " $3) 0))由一個遞增的計數器值組成,後面跟著第三個匹配項(即我剛剛查詢的 *任何東西*)。
第二個 replace 操作的結果被打印出來。我在臨時變數 *temp* 中搜索更多具有中間等號的字串和空格
({(\S*)(\s*)(=)(\s*)(.*)})
替換表示式由重要的發現元素($1、$3、$5)構成,但它還包含一個快速計算,用於計算將等號移至字元 20 所需的空間量,這應該是第一個專案的寬度與位置 20 之間的差(我任意地將位置 20 作為等號的位置)。
正則表示式對於新手來說並不容易,但它們非常強大,特別是與 newLISP 的 **replace** 函式結合使用時,因此值得學習。
您可以對字串執行各種測試。newLISP 的比較運算子透過查詢並比較字元的程式碼編號來工作,直到做出決定為止
(> {Higgs Boson} {Higgs boson}) ; nil
(> {Higgs Boson} {Higgs}) ; true
(< {dollar} {euro}) ; true
(> {newLISP} {LISP}) ; true
(= {fred} {Fred}) ; nil
(= {fred} {fred}) ; true
當然,newLISP 的靈活引數處理允許您同時測試大量字串
(< "a" "c" "d" "f" "h")
;-> true
這些比較函式還允許您使用單個引數。如果您只提供一個引數,newLISP 會根據第一個引數的型別,幫助您假設您指的是 0 或 ""
(> 1) ; true - assumes > 0
(> "fred") ; true - assumes > ""
要檢查兩個字串是否具有共同特徵,您可以使用 **starts-with** 和 **ends-with**,或者使用更通用的模式匹配命令 **member**、**regex**、**find** 和 **find-all**。**starts-with** 和 **ends-with** 很簡單
(starts-with "newLISP" "new") ; does newLISP start with new?
;-> true
(ends-with "newLISP" "LISP")
;-> true
它們還可以接受正則表示式,使用其中一個正則表示式選項(0 是最常用的選項)
(starts-with {newLISP} {[a-z][aeiou](?\#lc followed by lc vowel)} 0)
;-> true
(ends-with {newLISP} {[aeiou][A-Z](?\# lc vowel followed by UCase)} 0)
;-> false
**find**、**find-all**、**member** 和 **regex** 在字串中查詢所有內容。**find** 返回匹配子字串的索引
(set 't "a hypothetical one-dimensional subatomic particle")
(find "atom" t)
;-> 34
(find "l" t)
;-> 13
(find "L" t)
;-> nil ; search is case-sensitive
**member** 檢查一個字串是否在另一個字串中。它返回字串的剩餘部分,包括搜尋字串,而不是第一個出現的索引。
(member "rest" "a good restaurant")
;-> "restaurant"
**find** 和 **member** 都允許您使用正則表示式
(set 'quotation {"I cannot explain." She spoke in a low,
eager voice, with a curious lisp in her utterance. "But for
Gods sake do what I ask you. Go back and never set foot upon
the moor again."})
(find "lisp" quotation) ; without regex
;-> 69 ; character 69
(find {i} quotation 0) ; with regex
;-> 15 ; character 15
(find {s} quotation 1) ; case insensitive regex
;-> 20 ; character 20
(println "character "
(find {(l.*?p)} quotation 0) ": " $0) ; l followed by a p
;-> character 13: lain." She sp
**find-all** 的工作方式類似於 **find**,但它返回所有匹配字串的列表,而不是僅第一個匹配項的索引。它始終接受正則表示式,因此 - 這一次 - 您不必在末尾新增正則表示式選項編號。
(set 'quotation {"I cannot explain." She spoke in a low,
eager voice, with a curious lisp in her utterance. "But for
Gods sake do what I ask you. Go back and never set foot upon
the moor again."})
(find-all "[aeiou]{2,}" quotation $0) ; two or more vowels
;-> ("ai" "ea" "oi" "iou" "ou" "oo" "oo" "ai")
或者您可以使用 **regex**。如果字串不包含模式,它將返回 **nil**,但是,如果它包含模式,它將返回一個包含匹配的字串和子字串以及每個字串的起始位置和長度的列表。結果可能相當複雜
(set 'quotation
{She spoke in a low, eager voice, with a curious lisp in her utterance.})
(println (regex {(.*)(l.*)(l.*p)(.*)} quotation 0))
("She spoke in a low, eager voice, with a curious lisp in
her utterance." 0 70 "She spoke in a " 0 15 "low, eager
voice, with a curious " 15 33 "lisp" 48 4 " in her
utterance." 52 18)
此結果列表可以解釋為“第一個匹配項從字元 0 開始,持續 70 個字元,第二個從字元 0 開始,持續 15 個字元,另一個從字元 15 開始,持續 33 個字元”,依此類推。
匹配項也儲存在系統變數($0,$1,...)中,您可以使用簡單的迴圈輕鬆檢查這些變數
(for (x 1 4)
(println {$} x ": " ($ x)))
$1: She spoke in a $2: low, eager voice, with a curious $3: lisp $4: in her utterance.
兩個函式允許您將字串轉換為列表,以便使用 newLISP 廣泛的列表處理功能進行操作。名為 **explode** 的函式開啟一個字串,並返回一個單字元列表
(set 't "a hypothetical one-dimensional subatomic particle")
(explode t)
:-> ("a" " " "h" "y" "p" "o" "t" "h" "e" "t" "i" "c" "a" "l"
" " "o" "n" "e" "-" "d" "i" "m" "e" "n" "s" "i" "o" "n" "a"
"l" " " "s" "u" "b" "a" "t" "o" "m" "i" "c" " " "p" "a" "r"
"t" "i" "c" "l" "e")
爆炸很容易用 **join** 反轉。**explode** 也可以接受一個整數。這定義了片段的大小。例如,要將一個字串分成密碼學家風格的 5 個字母組,請刪除空格並使用 **explode** 如下
(explode (replace " " t "") 5)
;-> ("ahypo" "theti" "calon" "e-dim" "ensio" "nalsu" "batom" "icpar" "ticle")
你可以對 **find-all** 做類似的技巧。不過注意結尾
(find-all ".{3}" t) ; this regex drops chars!
;-> ("a h" "ypo" "the" "tic" "al " "one" "-di" "men"
; "sio" "nal" " su" "bat" "omi" "c p" "art" "icl")
**parse** 是一種將字串分解並返回各個部分的強大方法。單獨使用時,它將字串分解,通常在單詞邊界處,吃掉邊界,並返回一個包含剩餘部分的列表
(parse t) ; defaults to spaces...
;-> ("a" "hypothetical" "one-dimensional" "subatomic" "particle")
或者你可以提供一個分隔符,**parse** 會在遇到該字元時將字串分解
(set 'pathname {/System/Library/Fonts/Courier.dfont})
(parse pathname {/})
;-> ("" "System" "Library" "Fonts" "Courier.dfont")
順便說一下,我可以透過過濾掉第一個空字串來消除列表中的第一個空字串
(clean empty? (parse pathname {/}))
;-> ("System" "Library" "Fonts" "Courier.dfont")
你也可以指定分隔符字串而不是分隔符字元
(set 't (dup "spam" 8))
;-> "spamspamspamspamspamspamspamspam"
(parse t {am}) ; break on "am"
;-> ("sp" "sp" "sp" "sp" "sp" "sp" "sp" "sp" "")
最重要的是,你可以指定一個正則表示式分隔符。與 newLISP 中大多數正則表示式函式一樣,確保提供選項標誌(0 或其他)
(set 't {/System/Library/Fonts/Courier.dfont})
(parse t {[/aeiou]} 0) ; split at slashes and vowels
;-> ("" "Syst" "m" "L" "br" "ry" "F" "nts" "C" "" "r" "" "r.df" "nt")
這是眾所周知的快速且不太可靠的 HTML 標籤剝離器
(set 'html (read-file "/Users/Sites/index.html"))
(println (parse html {<.*?>} 4)) ; option 4: dot matches newline
為了解析 XML 字串,newLISP 提供了 **xml-parse** 函式。見 使用 XML.
在對文字使用 **parse** 時要小心。除非你明確指定你要什麼,否則它會認為你傳遞給它的是 newLISP 原始碼。這可能會產生令人驚訝的結果
(set 't {Eats, shoots, and leaves ; a book by Lynn Truss})
(parse t)
;-> ("Eats" "," "shoots" "," "and" "leaves") ; she's gone!
分號在 newLISP 中被認為是註釋字元,因此 **parse** 忽略了它以及該行上的所有後續內容。使用分隔符或正則表示式告訴它你真正想要什麼
(set 't {Eats, shoots, and leaves ; a book by Lynn Truss})
(parse t " ")
;-> ("Eats," "shoots," "and" "leaves" ";" "a" "book" "by" "Lynn" "Truss")
或者
(parse t "\\s" 0) ; white space
;-> ("Eats," "shoots," "and" "leaves" ";" "a" "book" "by" "Lynn" "Truss")
如果你想以其他方式分割字串,請考慮使用 **find-all**,它返回一個包含匹配模式的字串列表。如果你可以將分割操作指定為正則表示式,那麼你很幸運。例如,如果你想將數字分成三組數字,請使用此技術
(set 'a "1212374192387562311")
(println (find-all {\d{3}|\d{2}$|\d$} a))
;-> ("121" "237" "419" "238" "756" "231" "1")
; alternatively
(explode a 3)
;-> ("121" "237" "419" "238" "756" "231" "1")
該模式必須考慮結尾處有 2 位或 1 位數字的情況。
**parse** 在分隔符完成工作後會吃掉它們 - **find-all** 會找到東西並返回它找到的東西。
(find-all {\w+} t ) ; word characters
;-> ("Eats" "shoots" "and" "leaves" "a" "book" "by" "Lynn" "Truss")
(parse t {\w+} 0 ) ; eats and leaves delimiters
;-> ("" ", " ", " " " "; " " " " " " " " " "")
還有其他與字串一起工作的函式。**search** 在磁碟上的檔案中查詢字串
(set 'f (open {/private/var/log/system.log} {read}))
(search f {kernel})
(seek f (- (seek f) 64)) ; rewind file pointer
(dotimes (n 3)
(println (read-line f)))
(close f)
此示例在 system.log 中查詢字串kernel. 如果找到,newLISP 將檔案指標倒帶 64 個字元,然後打印出三行,顯示上下文中的行。
還有一些函式用於處理 base64 編碼的檔案以及加密字串。
值得一提的是 **format** 函式,它允許你將 newLISP 表示式的值插入到預定義的模板字串中。使用 %s 表示模板內字串表示式的位置,使用其他 % 程式碼包含數字。例如,假設你想要顯示一個檔案列表,如下所示
folder: Library
file: mach
一個適合資料夾(目錄)的模板如下所示
"folder: %s" ; or
" file: %s"
為 **format** 函式提供一個模板字串,後跟產生檔案或資料夾名稱的表示式 (f)
(format "folder: %s" f) ; or
(format " file: %s" f)
當此表示式被求值時,f 的內容將被插入到 %s 所在的字串中。使用 **directory** 函式以這種格式生成目錄列表的程式碼如下所示
(dolist (f (directory))
(if (directory? f)
(println (format "folder: %s" f))
(println (format " file: %s" f))))
我正在使用 **directory?** 函式來選擇正確的模板字串。典型的列表如下所示
folder: . folder: .. file: .DS_Store file: .hotfiles.btree folder: .Spotlight-V100 folder: .Trashes folder: .vol file: .VolumeIcon.icns folder: Applications folder: Applications (Mac OS 9) folder: automount folder: bin folder: Cleanup At Startup folder: cores ...
有很多格式程式碼可用於生成你想要的輸出。使用數字來控制字串和數字的對齊方式和精度。只需確保格式字串中的 % 結構與之後出現的表示式或符號匹配,並且兩者數量相同。
以下是一個其他示例。我們將以十進位制、十六進位制和二進位制形式顯示前 400 個左右的 Unicode 字元。我們將使用 **bits** 函式來生成二進位制字串。我們在格式字串之後將一個包含三個值的列表提供給 **format**,該格式字串包含三個條目
(for (x 32 0x01a0)
(println (char x) ; the character, then
(format "%4d\t%4x\t%10s" ; decimal \t hex \t binary-string
(list x x (bits x)))))
32 20 100000 ! 33 21 100001 " 34 22 100010 # 35 23 100011 $ 36 24 100100 % 37 25 100101 & 38 26 100110 ' 39 27 100111 ( 40 28 101000 ) 41 29 101001 ...
最後,我必須提到 **eval** 和 **eval-string**。這兩者都允許你將 newLISP 程式碼提供給 newLISP 進行求值。如果它是有效的 newLISP,你將看到求值的結果。**eval** 需要一個表示式
(set 'expr '(+ 1 2))
(eval expr)
;-> 3
**eval-string** 需要一個字串
(set 'expr "(+ 1 2)")
(eval-string expr)
;-> 3
這意味著你可以使用我們遇到的任何函式來構建 newLISP 程式碼,然後讓 newLISP 對其進行求值。**eval** 在定義宏(延遲求值直到你選擇執行的函式)時特別有用。見 宏.
你可以使用 **eval** 和 **eval-string** 來編寫編寫程式的程式。
以下有趣的新 LISP 程式碼片段不斷地無意識地重新排列幾個字串並嘗試對結果進行求值。不成功的嘗試會被安全地捕獲。當它最終變成有效的 newLISP 時,它將被成功求值,結果將滿足結束條件並結束迴圈。
(set 'code '(")" "set" "'valid" "true" "("))
(set 'valid nil)
(until valid
(set 'code (randomize code))
(println (join code " "))
(catch (eval-string (join code " ")) 'result))
true 'valid set ) ( ) ( set true 'valid 'valid ( set true ) set 'valid true ( ) 'valid ) ( true set set true ) ( 'valid true ) ( set 'valid 'valid ( true ) set true 'valid ( ) set 'valid ) ( true set true ( 'valid ) set set ( 'valid ) true set true 'valid ( ) ( set 'valid true )
我使用過顯然使用這種程式設計技術編寫的程式...