二进制,字符串和字符串
在“Basic types”中,我们学习了字符串并使用了is_binary/1
函数进行检查:
iex> string = "hello" "hello" iex> is_binary(string) true
在本章中,我们将了解'like this'
Elixir 中的二进制文件,它们如何与字符串关联,以及单引号值。
UTF-8 和 Unicode
一个字符串是一个 UTF-8 编码二进制。为了正确理解我们的意思,我们需要了解字节和代码点之间的区别。
Unicode 标准将代码点分配给我们知道的许多字符。例如,该字母a
具有代码点,97
而该字母ł
具有代码点322
。将字符串写入"hełło"
磁盘时,我们需要将此代码点转换为字节。如果我们通过一个规则,说一个字节表示一个代码点,我们就不能写"hełło"
,因为它使用的代码点322
的ł
,和一个字节只能从代表一个号码0
来255
。但是,当然,由于您可以"hełło"
在屏幕上阅读,因此必须以某种方式表示。这就是编码进来的地方。
当以字节表示代码点时,我们需要以某种方式对它们进行编码。Elixir选择了 UTF-8 编码作为其主要和默认编码。当我们说一个字符串是一个 UTF-8 编码二进制文件时,我们的意思是一个字符串是一组字节,用来表示某些代码点的方式,正如 UTF-8 编码所指定的那样。
由于我们有像ł
分配给代码点的字符322
,实际上我们需要多于一个字节来表示它们。这就是为什么我们在计算byte_size/1
字符串与其相比时看到的差异String.length/1
:
iex> string = "hełło" "hełło" iex> byte_size(string) 7 iex> String.length(string) 5
在那里,byte_size/1
计算底层的原始字节数,并对String.length/1
字符进行计数。
注意:如果你在 Windows 上运行,你的终端有可能默认不使用 UTF-8。您可以
chcp 65001
在输入iex
(iex.bat
)前通过运行来更改当前会话的编码。
UTF-8 需要一个字节来表示字符h
,e
和o
,但有两个字节来表示ł
。在Elixir中,你可以通过使用?
以下代码获得角色的代码点:
iex> ?a 97 iex> ?ł 322
还可以使用的函数在String
模块拆分在其个别字符的字符串,每一个为长度为1的字符串:
iex> String.codepoints("hełło") ["h", "e", "ł", "ł", "o"]
你会看到 Elixir 对使用字符串的工作有很好的支持。它也支持许多 Unicode 操作。事实上,Elixir 通过了文章“The string type is broken”中展示的所有测试。
但是,字符串只是故事的一部分。如果一个字符串是一个二进制文件,并且我们已经使用了该is_binary/1
函数,那么Elixir必须有一个赋予字符串赋权的基础类型。它确实!我们来谈谈二进制文件。
二进制文件(和位串)
在 Elixir 中,您可以使用<<>>
以下方法定义二进制文件:
iex> <<0, 1, 2, 3>> <<0, 1, 2, 3>> iex> byte_size(<<0, 1, 2, 3>>) 4
二进制是一个字节序列。这些字节可以以任何方式进行组织,即使是不会使其成为有效字符串的序列:
iex> String.valid?(<<239, 191, 19>>) false
字符串连接操作实际上是一个二进制连接操作符:
iex> <<0, 1>> <> <<2, 3>> <<0, 1, 2, 3>>
Elixir 中的一个常见技巧是将空字节连接<<0>>
到字符串以查看其内部二进制表示形式:
iex> "hełło" <> <<0>> <<104, 101, 197, 130, 197, 130, 111, 0>>
给二进制数的每个数字都表示一个字节,因此必须高达255.二进制数允许修饰符存储大于255的数字或将代码点转换为其 UTF-8 表示形式:
iex> <<255>> <<255>> iex> <<256>> # truncated <<0>> iex> <<256 :: size(16)>> # use 16 bits (2 bytes) to store the number <<1, 0>> iex> <<256 :: utf8>> # the number is a code point "Ā" iex> <<256 :: utf8, 0>> <<196, 128, 0>>
如果一个字节有8位,如果我们传递1位的大小会发生什么?
iex> <<1 :: size(1)>> <<1::size(1)>> iex> <<2 :: size(1)>> # truncated <<0::size(1)>> iex> is_binary(<<1 :: size(1)>>) false iex> is_bitstring(<<1 :: size(1)>>) true iex> bit_size(<< 1 :: size(1)>>) 1
该值不再是一个二进制数,而是一个比特串 - 一堆比特!所以二进制是一个比特串,其比特数可以被8整除。
iex> is_binary(<<1 :: size(16)>>) true iex> is_binary(<<1 :: size(15)>>) false
我们还可以在二进制文件/位串上进行模式匹配:
iex> <<0, 1, x>> = <<0, 1, 2>> <<0, 1, 2>> iex> x 2 iex> <<0, 1, x>> = <<0, 1, 2, 3>> ** (MatchError) no match of right hand side value: <<0, 1, 2, 3>>
注意二进制模式中的每个条目预计将完全匹配8位。如果我们想匹配未知大小的二进制文件,可以在模式结尾使用二进制修饰符:
iex> <<0, 1, x :: binary>> = <<0, 1, 2, 3>> <<0, 1, 2, 3>> iex> x <<2, 3>>
使用字符串连接运算符可以实现类似的结果<>
:
iex> "he" <> rest = "hello" "hello" iex> rest "llo"
有关二进制/位字符串构造函数的完整参考<<>>
可以在 Elixir 文档中找到。这就结束了我们对比特串,二进制文件和字符串的浏览。一个字符串是一个 UTF-8 编码二进制数,二进制是一个比特串,其比特数可以被8整除。虽然这表明了Elixir提供的位和字节处理的灵活性,但99%的时间你将使用二进制并使用is_binary/1
和byte_size/1
功能。
Charlists
charlist 只不过是一个代码点列表。字符列表可以用单引号文字创建:
iex> 'hełło' [104, 101, 322, 322, 111] iex> is_list 'hełło' true iex> 'hello' 'hello' iex> List.first('hello') 104
您可以看到,charlist 不包含字节,而是包含单引号之间字符的代码点(注意,如果任何整数超出 ASCII 范围,默认情况下 IEx 将只输出代码点)。所以,虽然双引号表示一个字符串(即二进制),单引号表示一个charlist(即列表)。
在实践中,charlists 主要用于与 Erlang 接口,特别是不接受二进制文件作为参数的旧库。您可以使用to_string/1
和to_charlist/1
函数将charlist转换为字符串并返回:
iex> to_charlist "hełło" [104, 101, 322, 322, 111] iex> to_string 'hełło' "hełło" iex> to_string :hello "hello" iex> to_string 1 "1"
请注意,这些函数是多态的。它们不仅将 charlists 转换为字符串,还将整数转换为字符串,将原子转换为字符串等等。
通过二进制文件,字符串和 charlists,现在是讨论关键值数据结构的时候了。