当前位置: 首页>>代码示例 >>用法及示例精选 >>正文


Elixir String用法及代码示例


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 始终以恒定时间运行(即,无论输入大小如何)。

这意味着与直接使用二进制文件的更底层操作相比,使用此模块中的函数通常会产生性能成本:

在许多情况下,可以避免使用 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"

尽管上面的函数有效,但它的性能很差。要计算字符串的长度,我们需要完全遍历它,所以我们同时遍历prefixfull字符串,然后将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-lang.org大神的英文原创作品 String。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。