Elixir学习笔记——二进制、字符串和字符列表

在“基本类型”中,我们学习了一些关于字符串的知识,并使用 is_binary/1 函数进行检查:

iex>string = "hello"
"hello"
iex>is_binary(string)
true

在本章中,我们将明确二进制到底是什么、它们与字符串的关系以及 Elixir 中单引号值“like this”的含义。虽然字符串是计算机语言中最常见的数据类型之一,但它们却非常复杂,经常被误解。要理解 Elixir 中的字符串,我们必须了解 Unicode 和字符编码,特别是 UTF-8 编码。

Unicode和码位

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

Unicode 将其库中的所有字符组织成代码表,每个字符都被赋予一个唯一的数字索引。这个数字索引称为码位。

---------------------------------------------------------------------------------------------------------------------------------

码位

在字符编码术语中,码位或称编码位置,即英文的code point或code position,是组成码空间(或代码页)的数值。 例如,ASCII码包含128个码位,范围是016进制到7F16进制,扩展ASCII码包含256个码位,范围是016进制到FF16进制,而Unicode包含1,114,112个码位,范围是016进制到10FFFF16进制。Unicode码空间划分为17个Unicode字符平面(基本多文种平面,16个辅助平面),每个平面有65,536(= 216)个码位。因此Unicode码空间总计是17 × 65,536 = 1,114,112.

定义
码位的抽象意涵, 不同于下列概念:

1.作为具体编码的比特流。例如,UTF-16编码的比特流,既可以是大尾序,也可以是小尾序。
2.具有特定字形的字符. 因为字符集中的字符(码位)的具体外观随字体(font)——字体显示样式——的不同而变化。
3.特定码空间的编码方式。例如,一个Unicode码空间的码位,可以用UTF-8编码;也可以用UTF-16编码。
4.用不同字形显示一个字符,即字位.

---------------------------------------------------------------------------------------------------------------------------------

在 Elixir 中,您可以在字符文字前使用 ? 来显示其码位:

iex>?a
97
iex>?ł
322

请注意,大多数 Unicode 代码表将通过其十六进制 (hex) 表示来引用码位,例如97转换为十六进制的0061,我们可以使用 \uXXXX 符号和其码位号的十六进制表示来表示 Elixir 字符串中的任何 Unicode 字符:

iex>“\u0061”==“a”
true
iex>0x0061 = 97 = ?a
97

十六进制表示还可以帮助您查找有关码位的信息,例如 https://codepoints.net/U+0061 有一个关于小写字母 a(又名码位 97)的数据表。

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 个字节,因为使用两个字节来表示字符 é。

注意:如果您在 Windows 上运行,您的终端可能默认不使用 UTF-8。您可以在输入 iex (iex.bat) 之前运行 chcp 65001 来更改当前会话的编码。

除了定义字符外,UTF-8 还提供了字素的概念。字素可能由多个通常被视为一个的字符组成。例如,女消防员表情符号表示为三个字符的组合:女性表情符号 (👩)、隐藏的零宽度连接符和消防车表情符号 (🚒):

iex>String.codepoints("👩‍🚒")
["👩", "‍", "🚒"]
iex>String.graphemes("👩‍🚒")
["👩‍🚒"]

但是,Elixir 足够聪明,知道它们被视为单个字符,因此长度仍然为 1:

iex>String.length("👩‍🚒")
1

注意:如果您在终端中看不到上面的表情符号,则需要确保您的终端支持表情符号并且您正在使用可以呈现它们的字体。

虽然这些规则听起来很复杂,但 UTF-8 编码的文档随处可见。此页面本身以 UTF-8 编码。编码信息会提供给您的浏览器,然后浏览器会知道如何相应地呈现所有字节、字符和字素。

如果您想查看字符串在文件中存储的确切字节数,一个常用技巧是将空字节 <<0>> 连接到它:

iex>“hełło”<> <<0>>
<<104, 101, 197, 130, 197, 130, 111, 0>>

或者,您可以使用 IO.inspect/2 查看字符串的二进制表示:

iex>IO.inspect(“hełło”, binaries: :as_binaries)
<<104, 101, 197, 130, 197, 130, 111>>

我们有点超前了。让我们讨论一下位串,了解 <<>> 构造函数的确切含义。

二进制字符串

虽然我们已经介绍了码位和 UTF-8 编码,但我们仍需要更深入地了解我们究竟如何存储编码字节,这就是我们介绍二进制字符串的地方。二进制字符串是 Elixir 中的基本数据类型,用 <<>>/1 语法表示。二进制字符串是内存中连续的位序列。

默认情况下,使用 8 位(即 1 个字节)将每个数字存储在二进制字符串中,但您可以通过 ::n 修饰符手动指定位数,以 n 位表示大小,或者可以使用更详细的声明 ::size(n):

iex><<42>> == <<42::8>>
true
iex><<3::4>>
<<3::size(4)>>

例如,十进制数 3 用 4 位二进制表示时为 0011,相当于值 0、0、1、1,每个值使用 1 位存储:

iex><<0::1, 0::1, 1::1, 1::1>> == <<3::4>>
true

任何超出预置位数可存储的值都将被截断:

iex><<1>> == <<257>>
true

此处,257 二进制表示为100000001,但由于我们只保留了 8 位来表示它(默认情况下),最左边的位将被忽略,并且该值将被截断为 00000001,或者仅仅是十进制的 1。

二进制序列

二进制序列是位数可被 8 整除的二进制字符串。这意味着每个二进制二进制序列都是二进制字符串,但并非每个二进制字符串都是二进制二进制序列。我们可以使用 is_bitstring/1 和 is_binary/1 函数来演示这一点。

iex>is_bitstring(<<3::4>>)
true
iex>is_binary(<<3::4>>)
false
iex>is_bitstring(<<0, 255, 42>>)
true
iex>is_binary(<<0, 255, 42>>)
true
iex>is_binary(<<42::16>>)
true

我们可以对二进制二进制序列/二进制字符串进行模式匹配:

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 位)。如果我们想要匹配未知大小的二进制序列,我们可以在模式末尾使用 binary 修饰符:

iex><<0, 1, x::binary>> = <<0, 1, 2, 3>>
<<0, 1, 2, 3>>
iex>x
<<2, 3>>

在二进制序列上进行模式匹配时,还有其他几个修饰符很有用。binary-size(n) 修饰符将匹配二进制序列中的 n 个字节:

iex><<head::binary-size(2), rest::binary>> = <<0, 1, 2, 3>>
<<0, 1, 2, 3>>
iex>head
<<0, 1>>
iex>rest
<<2, 3>>

字符串是 UTF-8 编码的二进制,其中每个字符的码位使用 1 到 4 个字节进行编码。因此,每个字符串都是二进制序列,但由于 UTF-8 标准编码规则,并非每个二进制序列都是有效字符串。

iex>is_binary("hello")
true
iex>is_binary(<<239, 191, 19>>)
true
iex>String.valid?(<<239, 191, 19>>)
false

字符串连接运算符 <> 实际上是二进制序列连接运算符:

iex>"a" <> "ha"
"aha"
iex><<0, 1>> <> <<2, 3>>
<<0, 1, 2, 3>>

鉴于字符串是二进制序列的,我们也可以对字符串进行模式匹配:

iex><<head, rest::binary>> = "banana"
"banana"
iex>head == ?b
true
iex>rest
"anana"

但是,请记住二进制序列模式匹配适用于bytes,因此对多字节字符的字符串(如“über”)进行匹配不会匹配该字符,它将匹配该字符的第一个字节:

iex>"ü" <> <<0>>
<<195, 188, 0>>
iex><<x, rest::binary>> = "über"
"über"
iex>x == ?ü
false
iex>rest
<<188, 98, 101, 114>>

上面,x 仅匹配多字节 ü 字符的第一个字节。

因此,在对字符串进行模式匹配时,使用 utf8 修饰符非常重要:

iex><<x::utf8, rest::binary>> = "über"
"über"
iex>x == ?ü
true
iex>rest
"ber"

字符列表(Charlists)

我们对二进制字符串、二进制和字符串的介绍已接近尾声,但我们还需要解释一种数据类型:Charlists。

字符列表是整数列表,其中所有整数都是有效码位。实际上,您不会经常遇到它们,只有在特定场景中才会遇到,例如与不接受二进制作为参数的旧 Erlang 库交互。

iex>~c"hello"
~c"hello"
iex>[?h, ?e, ?l, ?l, ?o]
~c"hello"

~c 符号(我们将在“符号”一章后面介绍符号)表示我们正在处理字符列表而不是常规字符串。

字符列表包含整数码位,而不是字节。但是,只有当所有码位都在 ASCII 范围内时,列表才会以符号形式打印:

iex>~c"hełło"
[104, 101, 322, 322, 111]
iex>is_list(~c"hełło")
true

这样做是为了简化与 Erlang 的互操作性,尽管这可能会导致一些令人惊讶的行为。例如,如果您存储的整数列表恰好在 0 到 127 之间,则默认情况下 IEx 会将其解释为字符列表,并显示相应的 ASCII 字符。

iex>heartbeats_per_minute = [99, 97, 116]
~c"cat"

您始终可以通过调用 inspect/2 函数强制将字符列表打印在其列表表示中:

iex>inspect(heartbeats_per_minute, charlists: :as_list)
"[99, 97, 116]"

此外,您可以使用 to_string/1 和 to_charlist/1 将字符列表转换为字符串并转换回:

iex>to_charlist("hełło")
[104, 101, 322, 322, 111]
iex>to_string(~c"hełło")
"hełło"
iex>to_string(:hello)
"hello"
iex>to_string(1)
"1"

上述函数是多态的,换句话说,它们接受多种形状:它们不仅可以将字符列表转换为字符串(反之亦然),还可以转换整数、原子等等。

字符串(二进制)连接使用 <> 运算符,但字符列表(作为列表)使用列表连接运算符 ++:

iex>~c"this " <> ~c"fails"
** (ArgumentError) expected binary argument in <> operator but got: ~c"this "
    (elixir) lib/kernel.ex:1821: Kernel.wrap_concatenation/3
    (elixir) lib/kernel.ex:1808: Kernel.extract_concatenations/2
    (elixir) expanding macro: Kernel.<>/2
    iex:1: (file)
iex>~c"this " ++ ~c"works"
~c"this works"
iex>"he" ++ "llo"
** (ArgumentError) argument error
    :erlang.++("he", "llo")
iex>"he" <> "llo"
"hello"

讨论完二进制、字符串和字符列表后,是时候讨论键值数据结构了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/849155.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【RuoYi】如何解决Postman无法访问RuoYi中的接口数据

一、前言 最近&#xff0c;写项目要求需要将数据返回&#xff0c;指定的接口&#xff0c;并且需要使用Postman来测试接口数据&#xff0c;看是否能够请求到数据。然后项目用的是RuoYi的框架&#xff0c;RuoYi使用了SpringSecurity来做的安全框架&#xff0c;所以在访问的时候&a…

【Linux】ip命令详解

Linux网络排查 目录 一、ip命令介绍 1.1 ip命令简介 1.2 ip命令的由来 二、ip命令使用帮助 2.1 ip命令的help帮助信息 2.2 ip命令对象介绍 2.3 ip命令选项介绍 三、查看网络信息 3.1 显示当前网络接口信息 3.2 显示网络设备运行状态 3.3 显示详细设备信息 3.4 查看…

基于.NetCore和ABP.VNext的项目实战八:使用Redis缓存数据

这里将集成Redis,使用Redis来缓存数据,在appsettings.json配置Redis的连接字符串 //appsettings.json ..."Caching": {"IsOpen": "true","RedisConnectionString": "127.0.0.1:6379,ConnectTimeout=15000,SyncTimeout=5000&qu…

面向大模型的存储加速方案

参考&#xff1a;面向大模型的存储加速方案设计和实践-百度开发者中心 (baidu.com) 对于一个典型的训练来说&#xff0c;可能迭代多轮 epoch。在每个 epoch 内&#xff0c;首先需要对数据集进行随机打散&#xff0c;然后将打散后的数据划分为若干 batch&#xff0c;每读取一个 …

智谱AI最新开源模型CHATGLM4-9B试用

智谱AI最近开源了GLM4-9B模型。之前已开源chatglm1到chatglm3&#xff0c;相比前面开源的相比GLM3-6B有了大幅度提升。本次开源基本的GLM4-9B&#xff0c;还开源了对话版GLM-4-9B-Chat&#xff0c; 多模态版GLM-4V-9B&#xff0c; 长文本版GLM-4-9B-Chat-1M。 在语义、数学、推…

软件杯 题目:基于深度学习卷积神经网络的花卉识别 - 深度学习 机器视觉

文章目录 0 前言1 项目背景2 花卉识别的基本原理3 算法实现3.1 预处理3.2 特征提取和选择3.3 分类器设计和决策3.4 卷积神经网络基本原理 4 算法实现4.1 花卉图像数据4.2 模块组成 5 项目执行结果6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基…

Nginx location 配置:从入门到精通

Nginx的 location 配置是Nginx服务器配置中非常关键的一部分&#xff0c;它决定了如何响应不同的HTTP请求。以下是关于Nginx location配置的详细解释&#xff1a; 一、基本语法 location指令的基本语法如下&#xff1a; location [modifier] pattern {# 配置内容 }其中&#…

零拷贝技术

背景 磁盘可以说是计算机系统重最慢的硬件之一&#xff0c;读写速度相对内存10以上&#xff0c;所以针对优化磁盘的技术非常的多&#xff0c;比如&#xff1a;零拷贝、直接I/O、异步I/O等等&#xff0c;这些优化的目的就是为了提高系统的吞吐量&#xff0c;另外操作系统内核中的…

如何实现观察者模式和发布-订阅模式?

要实现观察者模式和发布-订阅模式&#xff0c;你需要遵循一些基本的步骤和原则。下面分别介绍这两种模式的实现方法。 如何实现观察者模式&#xff1a; 观察者模式&#xff08;Observer Pattern&#xff09;是一种行为设计模式&#xff0c;用于维护观察者&#xff08;Observe…

GAT1399协议分析(8)--ImageInfo字段详解

一、官方定义 接上一章节字段消息返回内容 "ImageInfo": { "ImageID": "34078100001190001002012024060513561300065", "InfoKind": 0, "ImageSource": "…

eclipse怎么导入python项目

python项目导入eclipse的步骤&#xff1a; 1、首先&#xff0c;打开Eclipse集成开发程序&#xff0c;在菜单栏上点击“File(F)”&#xff0c;在弹出来的列表中点击“导入&#xff08;I&#xff09;...”。 2、在弹出来的“导入”窗口中&#xff0c;选择“常规”选项中的“现有…

【uni-app】开发问题汇总

文章目录 1、APP获取dom2、添加页面&#xff0c;参考其他页面&#xff0c;国际化就是对应页面的导航的国际化"navigationBarTitleText": "%m.i.ForgetPaymentPassword.bartitle%",3、setStatusBarStyle这个导航栏设置方法不要了&#xff0c;导航栏现在都用…

SpringCloud Gateway中Filters详细说明

前面 https://blog.csdn.net/J080624/article/details/139494909 我们研究了GateWay中各种路由断言的使用。SpringCloud GateWay 还提供了各种过滤器用来对请求和响应进行处理。 官网地址&#xff1a;SpringCloud Gateway Filter 【1】GatewayFilter Factories 路由过滤器允…

java使用websocket遇到的问题

java使用websocket的bug 1 websocket连接正常但是收不到服务端发出的消息java的websocket并发的时候导致连接断开&#xff08;看着连接是正常的&#xff0c;但是实际上已经断开&#xff09; 1 websocket连接正常但是收不到服务端发出的消息 java的websocket并发的时候导致连接断…

oracle中varchar和varchar2的区别

在Oracle数据库中&#xff0c;VARCHAR和VARCHAR2都是用于存储可变长度字符数据的数据类型&#xff0c;但它们之间有一些关键的区别。以下是这两者的主要区别&#xff1a; 数据类型来源&#xff1a; VARCHAR&#xff1a;这是标准SQL提供的类型&#xff0c;具有广泛的数据库系统…

一问看懂 llana2

"Illana2" 可能是指 "Llama 2"&#xff0c;这是 Meta&#xff08;Facebook 的母公司&#xff09;推出的一个开源大型语言模型。Llama 2 是 LLaMA&#xff08;Large Language Model Meta AI&#xff09;的第二代版本&#xff0c;旨在用于各种自然语言处理任…

LeetCode|230. Kth Smallest Element in a BST

. 题目 Given the root of a binary search tree, and an integer k, return the kth smallest value (1-indexed) of all the values of the nodes in the tree. Example 1: Input: root [3,1,4,null,2], k 1Output: 1 Example 2: Input: root [5,3,6,2,4,null,null,…

DVWA-CSRF

CSRF Low 观察后端代码&#xff0c;只要password_new等于password_conf就可以修改密码。由于这两个参数是通过GET传递的&#xff0c;所以直接构造payload。 http://192.168.20.156/DVWA/vulnerabilities/csrf/?password_newpass&password_confpass&ChangeChange# 这…

cad导入su线条不在一个平面怎么办?

解决CAD导入sketchup线条不是共面问题&#xff0c;需要考虑到各个步骤如下&#xff1a; 1&#xff09;检查CAD文件。首先要检查CAD文件&#xff0c;确保线条是连接在一起的&#xff0c;并且看看有没有多余的线&#xff0c;以及是否有子线段没有合并&#xff0c;如果有会导致导入…

云南区块链商户平台发票助手成品

目录 1 概述2 功能对比3 项目演示图4 核心逻辑4.1智能赋码4.2 解密方法4.3 登录与检测4.4 发票金额大写转换4.5 检查登录是否失效4.6 验证码识别5 演示效果6 项目部署6.1 Web站点部署6.1.1 环境6.1.2 前端6.1.3 后端6.2 Docker部署6.2.1 构建镜像6.2.2 创建容器6.3.3 访问项目域…