Elixir語言中 String
相關用法介紹如下。
Elixir 中的字符串是 UTF-8 編碼的二進製文件。
Elixir 中的字符串是一係列 Unicode 字符,通常寫在雙引號字符串之間,例如 "hello"
和 "héllò"
。
如果字符串本身必須有 double-quote,則必須使用反斜杠對雙引號進行轉義,例如:"this is a string with \"double quotes\""
。
您可以使用
運算符連接兩個字符串:<>/2
iex> "hello" <> " " <> "world"
"hello world"
插值
Elixir 中的字符串也支持插值。這允許您使用 #{}
語法在字符串中間放置一些值:
iex> name = "joe"
iex> "hello #{name}"
"hello joe"
任何 Elixir 表達式在插值內都是有效的。如果給定字符串,則按原樣插入字符串。如果給出任何其他值,Elixir 將嘗試使用
協議將其轉換為字符串。例如,這允許從插值輸出一個整數:String.Chars
iex> "2 + 2 = #{2 + 2}"
"2 + 2 = 4"
如果您要插入的值無法轉換為字符串,因為它沒有人工文本表示,則會引發協議錯誤。
轉義字符
除了允許 double-quotes 用反斜杠轉義外,字符串還支持以下轉義字符:
\a
- 貝爾\b
- 退格\t
- 水平選項卡\n
- 換行(新行)\v
- 垂直選項卡\f
- 換頁\r
- 回車\e
- 命令轉義\#
- 返回#
字符本身,跳過插值\xNN
- 由十六進製表示的字節NN
\uNNNN
- 由NNNN
表示的 Unicode 代碼點
請注意,通常不建議在 Elixir 字符串中使用 \xNN
,因為引入無效的字節序列會使字符串無效。如果您必須通過其十六進製表示來引入字符,最好使用 Unicode 代碼點,例如 \uNNNN
。事實上,在對字符串進行低級操作時,理解 Unicode 碼位是必不可少的,接下來讓我們詳細探討一下。
代碼點和字素簇
此模塊中的函數根據The Unicode Standard, Version 14.0.0 執行。
根據標準,代碼點是單個 Unicode 字符,可以用一個或多個字節表示。
例如,盡管代碼點 "é" 是單個字符,但其底層表示使用兩個字節:
iex> String.length("é")
1
iex> byte_size("é")
2
此外,該模塊還介紹了字素簇的概念(從現在起稱為字素)。字形可以由多個代碼點組成,讀者可以將其視為單個字符。例如,"é" 可以表示為單個“帶尖音符的 e”代碼點,也可以表示為字母 "e" 後跟“組合尖音符”(兩個代碼點):
iex> string = "\u0065\u0301"
iex> byte_size(string)
3
iex> String.length(string)
1
iex> String.codepoints(string)
["e", "́"]
iex> String.graphemes(string)
["é"]
雖然上麵的例子是由兩個字符組成的,但它被用戶認為是一個字符。
字形也可以是被某些語言解釋為一個的兩個字符。例如,某些語言可能會將"ch" 視為單個字符。但是,由於此信息取決於區域設置,因此此模塊不考慮它。
通常,此模塊中的函數依賴於 Unicode 標準,但不包含任何特定於語言環境的行為。關於字素的更多信息可以在Unicode Standard Annex #29 中找到。
要將二進製文件轉換為不同的編碼和 Unicode 規範化機製,請參閱 Erlang 的 :unicode
模塊。
字符串和二進製操作
根據 Unicode 標準,此模塊中的許多函數以線性時間運行,因為它們需要考慮適當的 Unicode 代碼點遍曆整個字符串。
例如,隨著輸入的增長,
將花費更長的時間。另一方麵,String.length/1
始終以恒定時間運行(即,無論輸入大小如何)。Kernel.byte_size/1
這意味著與直接使用二進製文件的更底層操作相比,使用此模塊中的函數通常會產生性能成本:
Kernel.binary_part/3
Kernel.bit_size/1
Kernel.byte_size/1
Kernel.is_bitstring/1
Kernel.is_binary/1
- 加上一些用於處理二進製文件(字節)的函數
:binary
模塊
在許多情況下,可以避免使用
模塊以支持二進製函數或模式匹配。例如,假設您有一個字符串 String
prefix
並且您想從另一個名為 full
的字符串中刪除此前綴。
有人可能會想寫:
iex> take_prefix = fn full, prefix ->
...> base = String.length(prefix)
...> String.slice(full, base, String.length(full) - base)
...> end
iex> take_prefix.("Mr. John", "Mr. ")
"John"
盡管上麵的函數有效,但它的性能很差。要計算字符串的長度,我們需要完全遍曆它,所以我們同時遍曆prefix
和full
字符串,然後將full
切片,再次遍曆。
改進它的第一次嘗試可能是使用範圍:
iex> take_prefix = fn full, prefix ->
...> base = String.length(prefix)
...> String.slice(full, base..-1)
...> end
iex> take_prefix.("Mr. John", "Mr. ")
"John"
雖然這要好得多(我們不會遍曆 full
兩次),但它仍然可以改進。在這種情況下,由於我們想從字符串中提取子字符串,我們可以使用
和Kernel.byte_size/1
,因為我們不可能在由超過一個字節組成的代碼點的中間進行切片:Kernel.binary_part/3
iex> take_prefix = fn full, prefix ->
...> base = byte_size(prefix)
...> binary_part(full, base, byte_size(full) - base)
...> end
iex> take_prefix.("Mr. John", "Mr. ")
"John"
或者幹脆使用模式匹配:
iex> take_prefix = fn full, prefix ->
...> base = byte_size(prefix)
...> <<_::binary-size(base), rest::binary>> = full
...> rest
...> end
iex> take_prefix.("Mr. John", "Mr. ")
"John"
另一方麵,如果您想根據整數值動態地對字符串進行切片,那麽使用
是最好的選擇,因為它可以保證我們不會錯誤地將有效代碼點拆分為多個字節。String.slice/3
整數代碼點
盡管代碼點表示為整數,但此模塊將其編碼格式的代碼點表示為字符串。例如:
iex> String.codepoints("olá")
["o", "l", "á"]
有幾種方法可以檢索字符代碼點。可以使用?
構造:
iex> ?o
111
iex> ?á
225
或者也通過模式匹配:
iex> <<aacute::utf8>> = "á"
iex> aacute
225
正如我們在上麵看到的,代碼點可以通過它們的十六進製代碼插入到字符串中:
iex> "ol\u00E1"
"olá"
最後,要將字符串轉換為整數代碼點列表,在 Elixir 中稱為 "charlists",您可以調用 String.to_charlist
:
iex> String.to_charlist("olá")
[111, 108, 225]
Self-synchronization
UTF-8 編碼是self-synchronizing。這意味著如果遇到格式錯誤的數據(即根據編碼定義不可能的數據),則隻需拒絕一個代碼點。
此模塊依賴此行為來忽略此類無效字符。例如,
將返回正確的結果,即使輸入了無效的代碼點也是如此。length/1
換句話說,該模塊期望在其他地方檢測到無效數據,通常是在從外部源檢索數據時。例如,從數據庫讀取字符串的驅動程序將負責檢查編碼的有效性。
可用於將字符串分成有效和無效部分。String.chunk/2
編譯二進製模式
此模塊中的許多函數都使用模式。例如,
可以在給定模式的情況下將一個字符串拆分為多個字符串。此模式可以是字符串、字符串列表或編譯模式:String.split/3
iex> String.split("foo bar", " ")
["foo", "bar"]
iex> String.split("foo bar!", [" ", "!"])
["foo", "bar", ""]
iex> pattern = :binary.compile_pattern([" ", "!"])
iex> String.split("foo bar!", pattern)
["foo", "bar", ""]
當一次又一次地完成相同的匹配時,編譯模式很有用。請注意,編譯後的模式不能存儲在模塊屬性中,因為模式是在運行時生成的,並且不會在編譯時存活。
相關用法
- Elixir StringIO.flush用法及代碼示例
- Elixir String.contains?用法及代碼示例
- Elixir String.reverse用法及代碼示例
- Elixir String.to_integer用法及代碼示例
- Elixir String.pad_trailing用法及代碼示例
- Elixir String.split_at用法及代碼示例
- Elixir String.valid?用法及代碼示例
- Elixir String.replace_prefix用法及代碼示例
- Elixir String.printable?用法及代碼示例
- Elixir String.replace_trailing用法及代碼示例
- Elixir String.trim_leading用法及代碼示例
- Elixir String.length用法及代碼示例
- Elixir String.replace_suffix用法及代碼示例
- Elixir String.first用法及代碼示例
- Elixir String.upcase用法及代碼示例
- Elixir String.graphemes用法及代碼示例
- Elixir String.at用法及代碼示例
- Elixir String.replace用法及代碼示例
- Elixir String.next_grapheme用法及代碼示例
- Elixir String.next_codepoint用法及代碼示例
- Elixir String.myers_difference用法及代碼示例
- Elixir String.trim_trailing用法及代碼示例
- Elixir String.normalize用法及代碼示例
- Elixir String.ends_with?用法及代碼示例
- Elixir String.downcase用法及代碼示例
注:本文由純淨天空篩選整理自elixir-lang.org大神的英文原創作品 String。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。