查看源代码 String (Elixir v1.16.2)

Elixir 中的字符串是 UTF-8 编码的二进制数据。

Elixir 中的字符串是 Unicode 字符的序列,通常用双引号括起来,例如 "hello""héllò"

如果字符串本身必须包含双引号,则必须使用反斜杠对其进行转义,例如:"this is a string with \"double quotes\""

可以使用 <>/2 运算符连接两个字符串。

iex> "hello" <> " " <> "world"
"hello world"

本模块中的函数根据 Unicode 标准,版本 15.1.0 运行。

插值

Elixir 中的字符串也支持插值。这允许您使用 #{} 语法将一些值放置在字符串中间。

iex> name = "joe"
iex> "hello #{name}"
"hello joe"

任何 Elixir 表达式在插值中都是有效的。如果给定字符串,则会按原样进行插值。如果给定任何其他值,Elixir 将尝试使用 String.Chars 协议将其转换为字符串。例如,这允许输出插值中的整数。

iex> "2 + 2 = #{2 + 2}"
"2 + 2 = 4"

如果要插值的 value 无法转换为字符串(因为它没有人类文本表示),则会引发协议错误。

转义字符

除了允许使用反斜杠转义双引号外,字符串还支持以下转义字符。

  • \0 - 空字节
  • \a - 响铃
  • \b - 退格
  • \t - 水平制表符
  • \n - 换行符(换行符)
  • \v - 垂直制表符
  • \f - 换页符
  • \r - 回车符
  • \e - 命令转义
  • \s - 空格
  • \# - 返回 # 字符本身,跳过插值
  • \\ - 单个反斜杠
  • \xNN - 由十六进制 NN 表示的字节
  • \uNNNN - 由 NNNN 表示的 Unicode 代码点
  • \u{NNNNNN} - 由 NNNNNN 表示的 Unicode 代码点

注意,通常不建议在 Elixir 字符串中使用 \xNN,因为引入无效的字节序列会导致字符串无效。如果必须使用十六进制表示法引入字符,最好使用 Unicode 代码点,例如 \uNNNN。事实上,理解 Unicode 代码点在进行字符串的低级操作时至关重要,所以接下来我们将详细探讨它们。

Unicode 和代码点

为了促进跨多种语言的计算机之间有意义的通信,需要一个标准,以便一台机器上的 0 和 1 在传输到另一台机器时具有相同的含义。Unicode 标准充当我们所知的所有字符的官方登记簿:这包括来自古典和历史文本的字符、表情符号以及格式化和控制字符。

Unicode 将其所有字符组织到代码表中,每个字符都分配了一个唯一的数字索引。这个数字索引称为代码点。

在 Elixir 中,您可以在字符文字前使用 ? 来显示其代码点。

iex> ?a
97
iex> 
322

请注意,大多数 Unicode 代码表都使用其十六进制 (hex) 表示法来引用代码点,例如 97 在十六进制中转换为 0061,我们可以使用 \u 转义字符后跟其代码点号来表示 Elixir 字符串中的任何 Unicode 字符。

iex> "\u0061" === "a"
true
iex> 0x0061 = 97 = ?a
97

十六进制表示法还有助于您查找有关代码点的信息,例如 https://codepoints.net/U+0061 包含有关小写字母 a(即代码点 97)的所有数据表。请记住,您可以通过调用 Integer.to_string/2 来获取数字的十六进制表示。

iex> Integer.to_string(?a, 16)
"61"

UTF-8 编码和编码

现在我们了解了 Unicode 标准是什么以及代码点是什么,我们终于可以谈谈编码了。代码点是 我们存储什么,而编码则处理 我们如何存储它:编码是实现。换句话说,我们需要一种机制将代码点号转换为字节,以便它们可以存储在内存中、写入磁盘等。

Elixir 使用 UTF-8 来编码其字符串,这意味着代码点被编码为一系列 8 位字节。UTF-8 是一种 可变宽度 字符编码,它使用一个到四个字节来存储每个代码点。它能够编码所有有效的 Unicode 代码点。让我们看一个例子。

iex> string = "héllo"
"héllo"
iex> String.length(string)
5
iex> byte_size(string)
6

虽然上面的字符串有 5 个字符,但它使用了 6 个字节,因为两个字节用于表示字符 é

音节簇

本模块还使用音节簇的概念(以下简称音节)。音节可以包含多个代码点,这些代码点可能会被读者视为单个字符。例如,“é” 可以表示为单个“带重音的 e”代码点,如上面字符串 "héllo" 中所示,也可以表示为字母“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 标准附件 #29

有关将二进制文件转换为其他编码以及 Unicode 规范化机制,请参见 Erlang 的 :unicode 模块。

字符串和二进制操作

为了根据 Unicode 标准运行,本模块中的许多函数都以线性时间运行,因为它们需要遍历整个字符串,以考虑正确的 Unicode 代码点。

例如,String.length/1 在输入增大时会花费更长时间。另一方面,Kernel.byte_size/1 始终以恒定时间运行(即与输入大小无关)。

这意味着与直接使用二进制数据的低级操作相比,使用本模块中的函数通常会带来性能成本。

二进制语法 <<>> 中也提供了一个 utf8 修饰符。它可以用于从二进制文件/字符串中匹配代码点。

iex> <<eacute::utf8>> = "é"
iex> eacute
233

您还可以通过调用 String.to_charlist/1 将字符串完全转换为整数代码点的列表,在 Elixir 中称为“字符列表”。

iex> String.to_charlist("héllo")
[104, 233, 108, 108, 111]

如果您想查看字符串的底层字节,而不是其代码点,一个常见的技巧是将空字节 <<0>> 连接到它。

iex> "héllo" <> <<0>>
<<104, 195, 169, 108, 108, 111, 0>>

或者,您可以通过向 IO.inspect/2 传递一个选项来查看字符串的二进制表示。

IO.inspect("héllo", binaries: :as_binaries)
#=> <<104, 195, 169, 108, 108, 111>>

自同步

UTF-8 编码是自同步的。这意味着,如果遇到格式错误的数据(即根据编码定义不可能存在的数据),则只需要拒绝一个代码点。

本模块依赖于此行为来忽略此类无效字符。例如,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", ""]

当反复执行相同的匹配时,已编译的模式很有用。但请注意,已编译的模式不能存储在模块属性中,因为模式是在运行时生成的,并且不会在编译时保留。

总结

类型

以 UTF-8 编码的单个 Unicode 代码点。它可能是一个或多个字节。

多个代码点,读者可能会将其视为单个字符。

replace/4split/3 等函数中使用的模式。

t()

UTF-8 编码的二进制文件。

函数

返回给定 UTF-8 stringposition 处的音节。如果 position 大于 string 长度,则返回 nil

计算两个字符串之间的袋距离。

将给定字符串中的第一个字符转换为大写,其余字符根据mode转换为小写。

将字符串拆分成具有共同特征的字符块。

返回一个以字符串编码的代码点列表。

搜索string是否包含给定的contents

根据mode将给定字符串中的所有字符转换为小写。

返回重复n次的字符串subject

如果string以给定的任何后缀结尾,则返回true

如果string1在规范上等效于string2,则返回true

从 UTF-8 字符串中返回第一个音节,如果字符串为空,则返回nil

根据扩展音节群算法返回字符串中的 Unicode 音节。

计算两个字符串之间的 Jaro 距离(相似度)。

从 UTF-8 字符串中返回最后一个音节,如果字符串为空,则返回nil

返回 UTF-8 字符串中的 Unicode 音节数量。

检查string是否与给定的正则表达式匹配。

返回表示编辑脚本的关键字列表。

返回字符串中的下一个代码点。

返回字符串中的下一个音节。

string中的所有字符转换为由form标识的 Unicode 规范化形式。

返回一个新的字符串,该字符串用一个前导填充器填充,该填充器由padding中的元素组成。

返回一个新的字符串,该字符串用一个尾随填充器填充,该填充器由padding中的元素组成。

检查字符串是否仅包含可打印字符,最多character_limit个字符。

返回一个新的字符串,该字符串是通过用replacement替换subjectpattern的出现次数创建的。

返回一个新的字符串,该字符串是通过用replacement(默认情况下为"�")替换所有无效字节创建的。

stringmatch的所有前导出现次数替换为matchreplacement

如果string中的前缀与match匹配,则用replacement替换前缀。

如果string中的后缀与match匹配,则用replacement替换后缀。

stringmatch的所有尾随出现次数替换为matchreplacement

反转给定字符串中的音节。

返回从范围开始处的偏移量到范围结束处的偏移量的子字符串。

返回从偏移量start开始,长度为length的子字符串。

在每个 Unicode 空格出现的位置将字符串拆分为子字符串,忽略前导和尾随空格。空格组被视为单个出现。拆分不会发生在不间断空格上。

根据模式将字符串拆分为部分。

在指定的偏移量处将字符串拆分为两个。当给定的偏移量为负数时,位置从字符串的末尾开始计数。

返回一个按需拆分字符串的可枚举对象。

如果string以给定的任何前缀开头,则返回true

将字符串转换为现有原子或创建一个新的原子。

将字符串转换为字符列表。

将字符串转换为现有原子,如果原子不存在,则引发异常。

返回一个其文本表示形式为string的浮点数。

返回一个其文本表示形式为string的整数。

返回一个其文本表示形式为以base为底的string的整数。

返回一个字符串,其中已删除所有前导和尾随 Unicode 空格。

返回一个字符串,其中已删除所有前导和尾随to_trim字符。

返回一个字符串,其中已删除所有前导 Unicode 空格。

返回一个字符串,其中已删除所有前导to_trim字符。

返回一个字符串,其中已删除所有尾随 Unicode 空格。

返回一个字符串,其中已删除所有尾随to_trim字符。

根据mode将给定字符串中的所有字符转换为大写。

检查string是否仅包含有效字符。

类型

@type codepoint() :: t()

以 UTF-8 编码的单个 Unicode 代码点。它可能是一个或多个字节。

@type grapheme() :: t()

多个代码点,读者可能会将其视为单个字符。

@type pattern() ::
  t() | [nonempty_binary()] | (compiled_search_pattern :: :binary.cp())

replace/4split/3 等函数中使用的模式。

它必须是以下之一

@type t() :: binary()

UTF-8 编码的二进制文件。

类型String.t()binary()对于分析工具是等效的。虽然对于阅读文档的人来说,String.t()暗示它是一个 UTF-8 编码的二进制文件。

函数

@spec at(t(), integer()) :: grapheme() | nil

返回给定 UTF-8 stringposition 处的音节。如果 position 大于 string 长度,则返回 nil

示例

iex> String.at("elixir", 0)
"e"

iex> String.at("elixir", 1)
"l"

iex> String.at("elixir", 10)
nil

iex> String.at("elixir", -1)
"r"

iex> String.at("elixir", -10)
nil
链接到此函数

bag_distance(string1, string2)

查看源代码 (自 1.8.0 起)
@spec bag_distance(t(), t()) :: float()

计算两个字符串之间的袋距离。

返回一个介于 0 和 1 之间的浮点值,表示string1string2之间的袋距离。

袋距离旨在成为两个字符串之间距离的有效近似值,以快速排除差异很大的字符串。

该算法概述在 Ilaria Bartolini、Paolo Ciaccia 和 Marco Patella 撰写的“使用近似距离的度量树的字符串匹配”论文中。

示例

iex> String.bag_distance("abc", "")
0.0
iex> String.bag_distance("abcd", "a")
0.25
iex> String.bag_distance("abcd", "ab")
0.5
iex> String.bag_distance("abcd", "abc")
0.75
iex> String.bag_distance("abcd", "abcd")
1.0
链接到此函数

capitalize(string, mode \\ :default)

查看源代码
@spec capitalize(t(), :default | :ascii | :greek | :turkic) :: t()

将给定字符串中的第一个字符转换为大写,其余字符根据mode转换为小写。

mode可以是:default:ascii:greek:turkic:default模式考虑 Unicode 标准中概述的所有非条件转换。:ascii仅将字母 A 到 Z 转换为大写。:greek包含在希腊语中找到的上下文敏感映射。:turkic正确处理带有无点变体的字母i

另请参见upcase/2capitalize/2,以了解其他转换。如果您想要一个不将字符串的其余部分转换为小写的该函数的变体,请参见 Erlang 的:string.titlecase/1

示例

iex> String.capitalize("abcd")
"Abcd"
iex> String.capitalize("ABCD")
"Abcd"

iex> String.capitalize("fin")
"Fin"
iex> String.capitalize("olá")
"Olá"
@spec chunk(t(), :valid | :printable) :: [t()]

将字符串拆分成具有共同特征的字符块。

特征可以是以下两个选项之一

  • :valid - 字符串被拆分为有效和无效字符序列的块

  • :printable - 字符串被拆分为可打印和不可打印字符序列的块

返回一个二进制列表,其中每个二进制列表仅包含一种类型的字符。

如果给定的字符串为空,则返回一个空列表。

示例

iex> String.chunk(<<?a, ?b, ?c, 0>>, :valid)
["abc\0"]

iex> String.chunk(<<?a, ?b, ?c, 0, 0xFFFF::utf16>>, :valid)
["abc\0", <<0xFFFF::utf16>>]

iex> String.chunk(<<?a, ?b, ?c, 0, 0x0FFFF::utf8>>, :printable)
["abc", <<0, 0x0FFFF::utf8>>]
@spec codepoints(t()) :: [codepoint()]

返回一个以字符串编码的代码点列表。

要以其自然整数表示形式检索代码点,请参见to_charlist/1。有关代码点和音节的详细信息,请参见String 模块文档。

示例

iex> String.codepoints("olá")
["o", "l", "á"]

iex> String.codepoints("оптими зации")
["о", "п", "т", "и", "м", "и", " ", "з", "а", "ц", "и", "и"]

iex> String.codepoints("ἅἪῼ")
["ἅ", "Ἢ", "ῼ"]

iex> String.codepoints("\u00e9")
["é"]

iex> String.codepoints("\u0065\u0301")
["e", "́"]
链接到此函数

contains?(string, contents)

查看源代码
@spec contains?(t(), [t()] | pattern()) :: boolean()

搜索string是否包含给定的contents

contents可以是字符串、字符串列表或已编译的模式。如果contents是一个列表,则此函数将搜索contents中的任何字符串是否为string的一部分。

在列表中搜索字符串

如果您想检查string是否列在contents中,其中contents是一个列表,请改用Enum.member?(contents, string)

示例

iex> String.contains?("elixir of life", "of")
true
iex> String.contains?("elixir of life", ["life", "death"])
true
iex> String.contains?("elixir of life", ["death", "mercury"])
false

参数也可以是已编译的模式

iex> pattern = :binary.compile_pattern(["life", "death"])
iex> String.contains?("elixir of life", pattern)
true

空字符串将始终匹配

iex> String.contains?("elixir of life", "")
true
iex> String.contains?("elixir of life", ["", "other"])
true

空列表将永远不会匹配

iex> String.contains?("elixir of life", [])
false

iex> String.contains?("", [])
false

请注意,此函数可以在音节边界内或跨音节边界进行匹配。例如,取音节“é”,它由字符“e”和重音组成。以下是true

iex> String.contains?(String.normalize("é", :nfd), "e")
true

但是,如果“é”由单个字符“带重音的 e”表示,那么它将返回false

iex> String.contains?(String.normalize("é", :nfc), "e")
false
链接到此函数

downcase(string, mode \\ :default)

查看源代码
@spec downcase(t(), :default | :ascii | :greek | :turkic) :: t()

根据mode将给定字符串中的所有字符转换为小写。

mode可以是:default:ascii:greek:turkic:default模式考虑 Unicode 标准中概述的所有非条件转换。:ascii仅将字母 A 到 Z 转换为小写。:greek包含在希腊语中找到的上下文敏感映射。:turkic正确处理带有无点变体的字母 i。

另请参阅 upcase/2capitalize/2 以获取其他转换。

示例

iex> String.downcase("ABCD")
"abcd"

iex> String.downcase("AB 123 XPTO")
"ab 123 xpto"

iex> String.downcase("OLÁ")
"olá"

:ascii 模式忽略 Unicode 字符,并在您知道字符串仅包含 ASCII 字符时提供更高效的实现。

iex> String.downcase("OLÁ", :ascii)
"olÁ"

:greek 模式正确处理希腊语中对上下文敏感的西格玛。

iex> String.downcase("ΣΣ")
"σσ"

iex> String.downcase("ΣΣ", :greek)
"σς"

并且 :turkic 正确处理带点变体的字母 i。

iex> String.downcase("Iİ")
"ii̇"

iex> String.downcase("Iİ", :turkic)
"ıi"
@spec duplicate(t(), non_neg_integer()) :: t()

返回重复n次的字符串subject

由编译器内联。

示例

iex> String.duplicate("abc", 0)
""

iex> String.duplicate("abc", 1)
"abc"

iex> String.duplicate("abc", 2)
"abcabc"
链接到此函数

ends_with?(string, suffix)

查看源代码
@spec ends_with?(t(), t() | [t()]) :: boolean()

如果string以给定的任何后缀结尾,则返回true

suffixes 可以是单个后缀或后缀列表。

示例

iex> String.ends_with?("language", "age")
true
iex> String.ends_with?("language", ["youth", "age"])
true
iex> String.ends_with?("language", ["youth", "elixir"])
false

空后缀将始终匹配。

iex> String.ends_with?("language", "")
true
iex> String.ends_with?("language", ["", "other"])
true
链接到此函数

equivalent?(string1, string2)

查看源代码
@spec equivalent?(t(), t()) :: boolean()

如果string1在规范上等效于string2,则返回true

它在比较字符串之前执行规范分解形式 (NFD) 的规范化。此函数等效于

String.normalize(string1, :nfd) == String.normalize(string2, :nfd)

如果您计划连续多次比较多个字符串,可以提前对其进行规范化,然后直接进行比较,以避免多次规范化操作。

示例

iex> String.equivalent?("abc", "abc")
true

iex> String.equivalent?("man\u0303ana", "mañana")
true

iex> String.equivalent?("abc", "ABC")
false

iex> String.equivalent?("nø", "nó")
false
@spec first(t()) :: grapheme() | nil

从 UTF-8 字符串中返回第一个音节,如果字符串为空,则返回nil

示例

iex> String.first("elixir")
"e"

iex> String.first("եոգլի")
"ե"

iex> String.first("")
nil
@spec graphemes(t()) :: [grapheme()]

根据扩展音节群算法返回字符串中的 Unicode 音节。

该算法概述于 Unicode 标准附录 #29,Unicode 文本分段 中。

有关代码点和字符的详细信息,请参阅 String 模块文档。

示例

iex> String.graphemes("Ńaïve")
["Ń", "a", "ï", "v", "e"]

iex> String.graphemes("\u00e9")
["é"]

iex> String.graphemes("\u0065\u0301")
["é"]
链接到此函数

jaro_distance(string1, string2)

查看源代码
@spec jaro_distance(t(), t()) :: float()

计算两个字符串之间的 Jaro 距离(相似度)。

返回一个介于 0.0(相当于没有相似性)和 1.0(完全匹配)之间的浮点值,表示 Jaro string1string2 之间的距离。

Jaro 距离度量旨在最适合短字符串,例如人名。Elixir 本身使用此函数来提供“您是否想说?”功能。例如,当您调用模块中的函数,并且函数名有错别字时,我们会尝试根据 jaro_distance/2 得分,建议最相似的可用函数名(如果有)。

示例

iex> String.jaro_distance("Dwayne", "Duane")
0.8222222222222223
iex> String.jaro_distance("even", "odd")
0.0
iex> String.jaro_distance("same", "same")
1.0
@spec last(t()) :: grapheme() | nil

从 UTF-8 字符串中返回最后一个音节,如果字符串为空,则返回nil

它遍历整个字符串以查找其最后一个字符。

示例

iex> String.last("")
nil

iex> String.last("elixir")
"r"

iex> String.last("եոգլի")
"ի"
@spec length(t()) :: non_neg_integer()

返回 UTF-8 字符串中的 Unicode 音节数量。

示例

iex> String.length("elixir")
6

iex> String.length("եոգլի")
5
@spec match?(t(), Regex.t()) :: boolean()

检查string是否与给定的正则表达式匹配。

示例

iex> String.match?("foo", ~r/foo/)
true

iex> String.match?("bar", ~r/foo/)
false

Elixir 还提供基于文本的匹配运算符 =~/2 和函数 Regex.match?/2 作为测试字符串与正则表达式的备选方法。

链接到此函数

myers_difference(string1, string2)

查看源代码 (自 1.3.0 起)
@spec myers_difference(t(), t()) :: [{:eq | :ins | :del, t()}]

返回表示编辑脚本的关键字列表。

有关更多信息,请查看 List.myers_difference/2

示例

iex> string1 = "fox hops over the dog"
iex> string2 = "fox jumps over the lazy cat"
iex> String.myers_difference(string1, string2)
[eq: "fox ", del: "ho", ins: "jum", eq: "ps over the ", del: "dog", ins: "lazy cat"]
@spec next_codepoint(t()) :: {codepoint(), t()} | nil

返回字符串中的下一个代码点。

结果是一个元组,包含代码点和字符串的剩余部分,或者如果字符串到达末尾则为 nil

String 模块中的其他函数一样,next_codepoint/1 使用无效的 UTF-8 的二进制文件。如果字符串以在 UTF-8 编码中无效的字节序列开头,则返回的元组的第一个元素是一个包含第一个字节的二进制文件。

示例

iex> String.next_codepoint("olá")
{"o", "lá"}

iex> invalid = "\x80\x80OK" # first two bytes are invalid in UTF-8
iex> {_, rest} = String.next_codepoint(invalid)
{<<128>>, <<128, 79, 75>>}
iex> String.next_codepoint(rest)
{<<128>>, "OK"}

与二进制模式匹配的比较

二进制模式匹配提供了一种类似的方式来分解字符串

iex> <<codepoint::utf8, rest::binary>> = "Elixir"
"Elixir"
iex> codepoint
69
iex> rest
"lixir"

虽然并不完全等效,因为 codepoint 作为整数出现,并且模式将不匹配无效的 UTF-8。

然而,二进制模式匹配更简单、更高效,因此选择最适合您的用例的选项。

@spec next_grapheme(t()) :: {grapheme(), t()} | nil

返回字符串中的下一个音节。

结果是一个元组,包含字符和字符串的剩余部分,或者如果字符串到达末尾则为 nil

示例

iex> String.next_grapheme("olá")
{"o", "lá"}

iex> String.next_grapheme("")
nil
链接到此函数

normalize(string, form)

查看源代码
@spec normalize(t(), :nfd | :nfc | :nfkd | :nfkc) :: t()

string中的所有字符转换为由form标识的 Unicode 规范化形式。

无效的 Unicode 代码点将被跳过,剩余的字符串将被转换。如果您希望算法在无效的代码点上停止并返回,请使用 :unicode.characters_to_nfd_binary/1:unicode.characters_to_nfc_binary/1:unicode.characters_to_nfkd_binary/1:unicode.characters_to_nfkc_binary/1 代替。

规范化形式 :nfkc:nfkd 不应该盲目地应用于任意文本。由于它们消除了许多格式区分,因此它们将阻止与许多传统字符集的往返转换。

形式

支持的形式为

  • :nfd - 规范分解形式。字符按规范等价分解,多个组合字符按特定顺序排列。

  • :nfc - 规范组合形式。字符被分解,然后按规范等价重新组合。

  • :nfkd - 兼容分解形式。字符按兼容等价分解,多个组合字符按特定顺序排列。

  • :nfkc - 兼容组合形式。字符被分解,然后按兼容等价重新组合。

示例

iex> String.normalize("yêṩ", :nfd)
"yêṩ"

iex> String.normalize("leña", :nfc)
"leña"

iex> String.normalize("fi", :nfkd)
"fi"

iex> String.normalize("fi", :nfkc)
"fi"
链接到此函数

pad_leading(string, count, padding \\ [" "])

查看源代码
@spec pad_leading(t(), non_neg_integer(), t() | [t()]) :: t()

返回一个新的字符串,该字符串用一个前导填充器填充,该填充器由padding中的元素组成。

将字符串列表作为 padding 传递将为每个缺失条目获取列表中的一个元素。如果列表比插入次数短,则填充将从列表开头重新开始。传递一个字符串 padding 等效于传递其中的字符列表。如果没有给出 padding,它将默认为空格。

count 小于或等于 string 的长度时,将返回给定的 string

如果给定的 padding 包含非字符串元素,则引发 ArgumentError

示例

iex> String.pad_leading("abc", 5)
"  abc"

iex> String.pad_leading("abc", 4, "12")
"1abc"

iex> String.pad_leading("abc", 6, "12")
"121abc"

iex> String.pad_leading("abc", 5, ["1", "23"])
"123abc"
链接到此函数

pad_trailing(string, count, padding \\ [" "])

查看源代码
@spec pad_trailing(t(), non_neg_integer(), t() | [t()]) :: t()

返回一个新的字符串,该字符串用一个尾随填充器填充,该填充器由padding中的元素组成。

将字符串列表作为 padding 传递将为每个缺失条目获取列表中的一个元素。如果列表比插入次数短,则填充将从列表开头重新开始。传递一个字符串 padding 等效于传递其中的字符列表。如果没有给出 padding,它将默认为空格。

count 小于或等于 string 的长度时,将返回给定的 string

如果给定的 padding 包含非字符串元素,则引发 ArgumentError

示例

iex> String.pad_trailing("abc", 5)
"abc  "

iex> String.pad_trailing("abc", 4, "12")
"abc1"

iex> String.pad_trailing("abc", 6, "12")
"abc121"

iex> String.pad_trailing("abc", 5, ["1", "23"])
"abc123"
链接到此函数

printable?(string, character_limit \\ :infinity)

查看源代码
@spec printable?(t(), 0) :: true
@spec printable?(t(), pos_integer() | :infinity) :: boolean()

检查字符串是否仅包含可打印字符,最多character_limit个字符。

采用一个可选的 character_limit 作为第二个参数。如果 character_limit0,则此函数将返回 true

示例

iex> String.printable?("abc")
true

iex> String.printable?("abc" <> <<0>>)
false

iex> String.printable?("abc" <> <<0>>, 2)
true

iex> String.printable?("abc" <> <<0>>, 0)
true
链接到此函数

replace(subject, pattern, replacement, options \\ [])

查看源代码
@spec replace(t(), pattern() | Regex.t(), t() | (t() -> t() | iodata()), keyword()) ::
  t()

返回一个新的字符串,该字符串是通过用replacement替换subjectpattern的出现次数创建的。

subject 始终是一个字符串。

pattern 可以是字符串、字符串列表、正则表达式或已编译的模式。

replacement 可以是字符串或函数,该函数接收匹配的模式,必须以字符串或 iodata 的形式返回替换。

默认情况下,它替换所有出现的情况,但可以通过 :global 选项控制此行为;请参阅下面的“选项”部分。

选项

  • :global - (布尔值) 如果为 true,则用 replacement 替换所有出现的 pattern,否则仅替换第一个出现的 pattern。默认为 true

示例

iex> String.replace("a,b,c", ",", "-")
"a-b-c"

iex> String.replace("a,b,c", ",", "-", global: false)
"a-b,c"

模式也可以是字符串列表,替换也可以是接收匹配项的函数

iex> String.replace("a,b,c", ["a", "c"], fn <<char>> -> <<char + 1>> end)
"b,b,d"

当模式为正则表达式时,可以在 replacement 字符串中给出 \N\g{N} 来访问正则表达式中的特定捕获。

iex> String.replace("a,b,c", ~r/,(.)/, ",\\1\\g{1}")
"a,bb,cc"

请注意,我们必须转义反斜杠转义字符(即,我们使用 \\N 而不是 \N 来转义反斜杠;\\g{N} 也是如此)。通过给出 \0,可以在替换字符串中注入整个匹配项。

也可以给出已编译的模式

iex> pattern = :binary.compile_pattern(",")
iex> String.replace("a,b,c", pattern, "[]")
"a[]b[]c"

当提供空字符串作为 pattern 时,该函数将将其视为每个字符之间的隐式空字符串,并且字符串将被交织在一起。如果提供空字符串作为 replacement,则将返回 subject

iex> String.replace("ELIXIR", "", ".")
".E.L.I.X.I.R."

iex> String.replace("ELIXIR", "", "")
"ELIXIR"

请注意,此函数可以在字符边界内或跨字符边界进行替换。例如,以字符“é”为例,它由字符“e”和重音符号组成。以下将仅替换字母“e”,并将重音符号移至字母“o”

iex> String.replace(String.normalize("é", :nfd), "e", "o")
"ó"

但是,如果“é”由单个字符“带重音的 e”表示,则它根本不会被替换

iex> String.replace(String.normalize("é", :nfc), "e", "o")
"é"
链接到此函数

replace_invalid(bytes, replacement \\ "�")

查看源代码 (自 1.16.0 起)

返回一个新的字符串,该字符串是通过用replacement(默认情况下为"�")替换所有无效字节创建的。

示例

iex> String.replace_invalid("asd" <> <<0xFF::8>>)
"asd�"

iex> String.replace_invalid("nem rán bề bề")
"nem rán bề bề"

iex> String.replace_invalid("nem rán b" <> <<225, 187>> <> " bề")
"nem rán b� bề"

iex> String.replace_invalid("nem rán b" <> <<225, 187>> <> " bề", "ERROR!")
"nem rán bERROR! bề"
链接到此函数

replace_leading(string, match, replacement)

查看源代码
@spec replace_leading(t(), t(), t()) :: t()

stringmatch的所有前导出现次数替换为matchreplacement

如果不存在出现的情况,则返回未修改的字符串。

如果 match"",则此函数将引发 ArgumentError 异常:发生这种情况是因为此函数替换 string 开头的 match 的所有出现情况,并且不可能替换 "" 的“多个”出现情况。

示例

iex> String.replace_leading("hello world", "hello ", "")
"world"
iex> String.replace_leading("hello hello world", "hello ", "")
"world"

iex> String.replace_leading("hello world", "hello ", "ola ")
"ola world"
iex> String.replace_leading("hello hello world", "hello ", "ola ")
"ola ola world"

此函数可以在字符边界之间进行替换。有关更多信息和示例,请参阅 replace/3

链接到此函数

replace_prefix(string, match, replacement)

查看源代码
@spec replace_prefix(t(), t(), t()) :: t()

如果string中的前缀与match匹配,则用replacement替换前缀。

如果不存在匹配项,则返回未修改的字符串。如果 match 为空字符串 (""),则 replacement 仅附加到 string

示例

iex> String.replace_prefix("world", "hello ", "")
"world"
iex> String.replace_prefix("hello world", "hello ", "")
"world"
iex> String.replace_prefix("hello hello world", "hello ", "")
"hello world"

iex> String.replace_prefix("world", "hello ", "ola ")
"world"
iex> String.replace_prefix("hello world", "hello ", "ola ")
"ola world"
iex> String.replace_prefix("hello hello world", "hello ", "ola ")
"ola hello world"

iex> String.replace_prefix("world", "", "hello ")
"hello world"

此函数可以在字符边界之间进行替换。有关更多信息和示例,请参阅 replace/3

链接到此函数

replace_suffix(string, match, replacement)

查看源代码
@spec replace_suffix(t(), t(), t()) :: t()

如果string中的后缀与match匹配,则用replacement替换后缀。

如果不存在匹配项,则返回未修改的字符串。如果 match 为空字符串 (""),则 replacement 仅附加到 string

示例

iex> String.replace_suffix("hello", " world", "")
"hello"
iex> String.replace_suffix("hello world", " world", "")
"hello"
iex> String.replace_suffix("hello world world", " world", "")
"hello world"

iex> String.replace_suffix("hello", " world", " mundo")
"hello"
iex> String.replace_suffix("hello world", " world", " mundo")
"hello mundo"
iex> String.replace_suffix("hello world world", " world", " mundo")
"hello world mundo"

iex> String.replace_suffix("hello", "", " world")
"hello world"

此函数可以在字符边界之间进行替换。有关更多信息和示例,请参阅 replace/3

链接到此函数

replace_trailing(string, match, replacement)

查看源代码
@spec replace_trailing(t(), t(), t()) :: t()

stringmatch的所有尾随出现次数替换为matchreplacement

如果不存在出现的情况,则返回未修改的字符串。

如果 match"",则此函数会引发 ArgumentError 异常:这是因为此函数会替换 string 末尾所有出现的 match,而无法替换 "" 的“多个”出现。

示例

iex> String.replace_trailing("hello world", " world", "")
"hello"
iex> String.replace_trailing("hello world world", " world", "")
"hello"

iex> String.replace_trailing("hello world", " world", " mundo")
"hello mundo"
iex> String.replace_trailing("hello world world", " world", " mundo")
"hello mundo mundo"

此函数可以在字符边界之间进行替换。有关更多信息和示例,请参阅 replace/3

@spec reverse(t()) :: t()

反转给定字符串中的音节。

示例

iex> String.reverse("abcd")
"dcba"

iex> String.reverse("hello world")
"dlrow olleh"

iex> String.reverse("hello ∂og")
"go∂ olleh"

请记住,对同一字符串进行两次反转并不一定能得到原始字符串。

iex> "̀e"
"̀e"
iex> String.reverse("̀e")
"è"
iex> String.reverse(String.reverse("̀e"))
"è"

在第一个示例中,重音符号位于元音之前,因此被视为两个音素。但是,当你反转它一次时,你将有元音后跟重音符号,这变成一个音素。再次反转它将保留它作为一个单一的音素。

@spec slice(t(), Range.t()) :: t()

返回从范围开始处的偏移量到范围结束处的偏移量的子字符串。

如果范围的开始不是给定字符串的有效偏移量,或者范围是反向顺序,则返回 ""

如果范围的开始或结束为负数,则首先遍历整个字符串,以将负索引转换为正索引。

请记住,此函数使用 Unicode 音素,并将切片视为表示音素偏移量。如果你想在原始字节上拆分,请检查 Kernel.binary_part/3Kernel.binary_slice/2

示例

iex> String.slice("elixir", 1..3)
"lix"
iex> String.slice("elixir", 1..10)
"lixir"

iex> String.slice("elixir", -4..-1)
"ixir"
iex> String.slice("elixir", -4..6)
"ixir"
iex> String.slice("elixir", -100..100)
"elixir"

对于 start > stop 的范围,你需要显式地将它们标记为递增。

iex> String.slice("elixir", 2..-1//1)
"ixir"
iex> String.slice("elixir", 1..-2//1)
"lixi"

你可以使用 ../0 作为 0..-1//1 的快捷方式,它会返回整个字符串。

iex> String.slice("elixir", ..)
"elixir"

步长可以是任何正数。例如,要获取字符串中每 2 个字符。

iex> String.slice("elixir", 0..-1//2)
"eii"

如果第一个位置在字符串结束之后或范围的最后一个位置之后,则返回空字符串。

iex> String.slice("elixir", 10..3//1)
""
iex> String.slice("a", 1..1500)
""
链接到此函数

slice(string, start, length)

查看源代码
@spec slice(t(), integer(), non_neg_integer()) :: grapheme()

返回从偏移量start开始,长度为length的子字符串。

如果偏移量大于字符串长度,则返回 ""

请记住,此函数使用 Unicode 音素,并将切片视为表示音素偏移量。如果你想在原始字节上拆分,请检查 Kernel.binary_part/3Kernel.binary_slice/3

示例

iex> String.slice("elixir", 1, 3)
"lix"

iex> String.slice("elixir", 1, 10)
"lixir"

iex> String.slice("elixir", 10, 3)
""

如果起始位置为负数,则会针对字符串长度进行规范化,并钳制到 0。

iex> String.slice("elixir", -4, 4)
"ixir"

iex> String.slice("elixir", -10, 3)
"eli"

如果 start 大于字符串长度,则返回空字符串。

iex> String.slice("elixir", 10, 1500)
""
@spec split(t()) :: [t()]

在每个 Unicode 空格出现的位置将字符串拆分为子字符串,忽略前导和尾随空格。空格组被视为单个出现。拆分不会发生在不间断空格上。

示例

iex> String.split("foo bar")
["foo", "bar"]

iex> String.split("foo" <> <<194, 133>> <> "bar")
["foo", "bar"]

iex> String.split(" foo   bar ")
["foo", "bar"]

iex> String.split("no\u00a0break")
["no\u00a0break"]
链接到此函数

split(string, pattern, options \\ [])

查看源代码
@spec split(t(), pattern() | Regex.t(), keyword()) :: [t()]

根据模式将字符串拆分为部分。

返回这些部分的列表。

pattern 可以是字符串、字符串列表、正则表达式或已编译的模式。

默认情况下,字符串将尽可能多地拆分为多个部分,但可以通过 :parts 选项进行控制。

只有当 :trim 选项设置为 true 时,才会从结果中删除空字符串。

当使用的模式是正则表达式时,字符串将使用 Regex.split/3 拆分。

选项

  • :parts (正整数或 :infinity) - 字符串最多拆分为此选项指定的多个部分。如果为 :infinity,则字符串将拆分为所有可能的部件。默认值为 :infinity

  • :trim (布尔值) - 如果为 true,则从结果列表中删除空字符串。

如果 pattern 是正则表达式,此函数还接受 Regex.split/3 接受的所有选项。

示例

使用字符串模式拆分

iex> String.split("a,b,c", ",")
["a", "b", "c"]

iex> String.split("a,b,c", ",", parts: 2)
["a", "b,c"]

iex> String.split(" a b c ", " ", trim: true)
["a", "b", "c"]

模式列表

iex> String.split("1,2 3,4", [" ", ","])
["1", "2", "3", "4"]

正则表达式

iex> String.split("a,b,c", ~r{,})
["a", "b", "c"]

iex> String.split("a,b,c", ~r{,}, parts: 2)
["a", "b,c"]

iex> String.split(" a b c ", ~r{\s}, trim: true)
["a", "b", "c"]

iex> String.split("abc", ~r{b}, include_captures: true)
["a", "b", "c"]

已编译的模式

iex> pattern = :binary.compile_pattern([" ", ","])
iex> String.split("1,2 3,4", pattern)
["1", "2", "3", "4"]

用空字符串拆分返回音素

iex> String.split("abc", "")
["", "a", "b", "c", ""]

iex> String.split("abc", "", trim: true)
["a", "b", "c"]

iex> String.split("abc", "", parts: 1)
["abc"]

iex> String.split("abc", "", parts: 3)
["", "a", "bc"]

请注意,此函数可以在音素边界内或跨音素边界进行拆分。例如,以音素“é”为例,它由字符“e”和重音符号组成。以下将字符串拆分为两个部分。

iex> String.split(String.normalize("é", :nfd), "e")
["", "́"]

但是,如果“é”由单个字符“带重音的 e”表示,则它会将字符串拆分为一个部分。

iex> String.split(String.normalize("é", :nfc), "e")
["é"]
链接到此函数

split_at(string, position)

查看源代码
@spec split_at(t(), integer()) :: {t(), t()}

在指定的偏移量处将字符串拆分为两个。当给定的偏移量为负数时,位置从字符串的末尾开始计数。

偏移量限制为字符串的长度。返回包含两个元素的元组。

注意:请记住,此函数在音素上拆分,因此必须线性遍历字符串。如果你想根据字节数拆分字符串或二进制文件,请使用 Kernel.binary_part/3

示例

iex> String.split_at("sweetelixir", 5)
{"sweet", "elixir"}

iex> String.split_at("sweetelixir", -6)
{"sweet", "elixir"}

iex> String.split_at("abc", 0)
{"", "abc"}

iex> String.split_at("abc", 1000)
{"abc", ""}

iex> String.split_at("abc", -1000)
{"", "abc"}
链接到此函数

splitter(string, pattern, options \\ [])

查看源代码
@spec splitter(t(), pattern(), keyword()) :: Enumerable.t()

返回一个按需拆分字符串的可枚举对象。

这与 split/3 形成对比,后者会提前拆分整个字符串。

此函数不设计为支持正则表达式。当使用正则表达式时,一次遍历字符串而不是像此函数那样分段遍历字符串通常效率更高。

选项

  • :trim - 当 true 时,不会发出空模式。

示例

iex> String.splitter("1,2 3,4 5,6 7,8,...,99999", [" ", ","]) |> Enum.take(4)
["1", "2", "3", "4"]

iex> String.splitter("abcd", "") |> Enum.take(10)
["", "a", "b", "c", "d", ""]

iex> String.splitter("abcd", "", trim: true) |> Enum.take(10)
["a", "b", "c", "d"]

也可以给出已编译的模式

iex> pattern = :binary.compile_pattern([" ", ","])
iex> String.splitter("1,2 3,4 5,6 7,8,...,99999", pattern) |> Enum.take(4)
["1", "2", "3", "4"]
链接到此函数

starts_with?(string, prefix)

查看源代码
@spec starts_with?(t(), t() | [t()]) :: boolean()

如果string以给定的任何前缀开头,则返回true

prefix 可以是字符串、字符串列表或已编译的模式。

示例

iex> String.starts_with?("elixir", "eli")
true
iex> String.starts_with?("elixir", ["erlang", "elixir"])
true
iex> String.starts_with?("elixir", ["erlang", "ruby"])
false

空字符串将始终匹配

iex> String.starts_with?("elixir", "")
true
iex> String.starts_with?("elixir", ["", "other"])
true

空列表将永远不会匹配

iex> String.starts_with?("elixir", [])
false

iex> String.starts_with?("", [])
false
@spec to_atom(t()) :: atom()

将字符串转换为现有原子或创建一个新的原子。

警告:此函数会动态创建原子,而原子不会被垃圾回收。因此,string 不应该是不受信赖的值,例如从套接字或在 Web 请求期间接收到的输入。考虑使用 to_existing_atom/1 代替。

默认情况下,原子的最大数量为 1_048_576。此限制可以使用 VM 选项 +t 提高或降低。

最大原子大小为 255 个 Unicode 代码点。

由编译器内联。

示例

iex> String.to_atom("my_atom")
:my_atom
@spec to_charlist(t()) :: charlist()

将字符串转换为字符列表。

具体来说,此函数接受 UTF-8 编码的二进制文件,并返回其整数代码点的列表。它类似于 codepoints/1,不同之处在于后者返回代码点列表作为字符串。

如果你需要使用字节,请查看 :binary 模块

示例

iex> String.to_charlist("foo")
~c"foo"
链接到此函数

to_existing_atom(string)

查看源代码
@spec to_existing_atom(t()) :: atom()

将字符串转换为现有原子,如果原子不存在,则引发异常。

最大原子大小为 255 个 Unicode 代码点。如果原子不存在,则会引发 ArgumentError

由编译器内联。

原子和模块

由于 Elixir 是一种编译语言,因此模块中定义的原子只有在该模块加载后才会存在,这通常发生在模块中的某个函数执行时。因此,通常建议只调用 String.to_existing_atom/1 来转换在进行函数调用以 to_existing_atom/1 的模块中定义的原子。

为了安全地从字符串中创建模块名称本身,建议使用 Module.safe_concat/1

示例

iex> _ = :my_atom
iex> String.to_existing_atom("my_atom")
:my_atom
@spec to_float(t()) :: float()

返回一个其文本表示形式为string的浮点数。

string 必须是浮点数的字符串表示形式,包括小数点。为了将没有小数点的字符串解析为浮点数,应使用 Float.parse/1。否则,将引发 ArgumentError

由编译器内联。

示例

iex> String.to_float("2.2017764e+0")
2.2017764

iex> String.to_float("3.0")
3.0

String.to_float("3")
** (ArgumentError) argument error
@spec to_integer(t()) :: integer()

返回一个其文本表示形式为string的整数。

string 必须是整数的字符串表示形式。否则,将引发 ArgumentError。如果你想解析可能包含格式错误整数的字符串,请使用 Integer.parse/1

由编译器内联。

示例

iex> String.to_integer("123")
123

传递不代表整数的字符串会导致错误。

String.to_integer("invalid data")
** (ArgumentError) argument error
链接到此函数

to_integer(string, base)

查看源代码
@spec to_integer(t(), 2..36) :: integer()

返回一个其文本表示形式为以base为底的string的整数。

由编译器内联。

示例

iex> String.to_integer("3FF", 16)
1023
@spec trim(t()) :: t()

返回一个字符串,其中已删除所有前导和尾随 Unicode 空格。

示例

iex> String.trim("\n  abc\n  ")
"abc"
@spec trim(t(), t()) :: t()

返回一个字符串,其中已删除所有前导和尾随to_trim字符。

示例

iex> String.trim("a  abc  a", "a")
"  abc  "
@spec trim_leading(t()) :: t()

返回一个字符串,其中已删除所有前导 Unicode 空格。

示例

iex> String.trim_leading("\n  abc   ")
"abc   "
链接到此函数

trim_leading(string, to_trim)

查看源代码
@spec trim_leading(t(), t()) :: t()

返回一个字符串,其中已删除所有前导to_trim字符。

示例

iex> String.trim_leading("__ abc _", "_")
" abc _"

iex> String.trim_leading("1 abc", "11")
"1 abc"
@spec trim_trailing(t()) :: t()

返回一个字符串,其中已删除所有尾随 Unicode 空格。

示例

iex> String.trim_trailing("   abc\n  ")
"   abc"
链接到此函数

trim_trailing(string, to_trim)

查看源代码
@spec trim_trailing(t(), t()) :: t()

返回一个字符串,其中已删除所有尾随to_trim字符。

示例

iex> String.trim_trailing("_ abc __", "_")
"_ abc "

iex> String.trim_trailing("abc 1", "11")
"abc 1"
链接到此函数

upcase(string, mode \\ :default)

查看源代码
@spec upcase(t(), :default | :ascii | :greek | :turkic) :: t()

根据mode将给定字符串中的所有字符转换为大写。

mode 可以是 :default:ascii:greek:turkic:default 模式考虑 Unicode 标准中概述的所有非条件转换。 :ascii 仅将字母 a 到 z 转换为大写。 :greek 包括希腊语中发现的上下文敏感映射。 :turkic 正确处理带点和无点变体的字母 i。

示例

iex> String.upcase("abcd")
"ABCD"

iex> String.upcase("ab 123 xpto")
"AB 123 XPTO"

iex> String.upcase("olá")
"OLÁ"

:ascii 模式忽略 Unicode 字符,并在您知道字符串仅包含 ASCII 字符时提供更高效的实现。

iex> String.upcase("olá", :ascii)
"OLá"

并且 :turkic 正确处理带点变体的字母 i。

iex> String.upcase("ıi")
"II"

iex> String.upcase("ıi", :turkic)
"Iİ"

另请参阅 downcase/2capitalize/2 以获取其他转换。

链接到此函数

valid?(string, algorithm \\ :default)

查看源代码

检查string是否仅包含有效字符。

algorithm 可以是 :default:fast_ascii。两种算法从验证的角度来看是等效的(它们将始终生成相同的输出),但 :fast_ascii 在特定场景中可以带来显著的性能优势。

如果以下所有条件都为真,你可能需要尝试使用 :fast_ascii 算法,看看它是否能在你的特定场景中提高性能。

  • 你正在 64 位平台上运行 Erlang/OTP 26 或更新版本。
  • 你预计大多数字符串的长度将超过 ~64 个字节。
  • 你预计大多数字符串将主要包含 ASCII 代码点。

请注意,:fast_ascii 算法不会影响正确性,你可以预期 String.valid?/2 的输出与算法无关。唯一可预期的差异是性能差异,与 :default 算法相比,预计性能将随着字符串长度大致线性提高。

示例

iex> String.valid?("a")
true

iex> String.valid?("ø")
true

iex> String.valid?(<<0xFFFF::16>>)
false

iex> String.valid?(<<0xEF, 0xB7, 0x90>>)
true

iex> String.valid?("asd" <> <<0xFFFF::16>>)
false

iex> String.valid?("a", :fast_ascii)
true

iex> String.valid?(4)
** (FunctionClauseError) no function clause matching in String.valid?/2