unicode 控制字符
unicode 官网:https://home.unicode.org/unicode 各种类型详细介绍:https://www.compart.com/en/unicode/category/Cf对零宽度字符完全没有头绪的可以先玩下这个Demo在綫解密:https://330k.github.io/misc_tools/unicode_steganography.html
1 前言在所有主要的Web浏览器中内存中的字符顺序(逻辑)与它们显示的顺序(可视)是不同的。
Unicode 定义了它其中每个字符的方向属性,浏览器应用的一组规则(通过这个来进行自动判断文本Unicode方向属性应该使用哪种方向)在显示时产生正确的顺序由Unicode双向算法进行描述,也可简称为BIDI算法。
控制字符,有时候也称非打印字符,是出现在特定的信息文本中,表示某一控制功能的字符。这类字符并不显示,只包含某种特定的功能。
2 Unicode方向属性Unicode方向属性包含以下三种类型:
强字符:大部分的字符都属于强字符,比如英文字母、汉字和阿拉伯字母。它们的方向性是确定的(英文字母是从左到右,而阿拉伯字母则从右到左),和其上下文的bidi属性无关。并且,强字符在bidi算法中可能会影响其前后字符的方向性;
弱字符:数字和数字相关的符号属于弱字符。它们的方向性是确定的,但对其前后字符的方向性不会产生影响;
中性字符:大部分的标点符号和空格属于中性字符。它们的方向性是不确定的,由上下文的bidi属性来决定其方向。
方向性
相关字符
效果
Left-to-Right (LTR)
强字符从左至右(英文字母、汉字以及世界上大部分从左到右书写的文字)
方向性确定,LTR,和上下文无关。并且可能会影响其前后字符的方向性。
Right-to-Left (RTL)
强字符从右至左(阿拉伯文字、波斯语以及大部分从右到左书写的文字)
方向性确定,RTL,和上下文无关。并且可能会影响其前后字符的方向性。
Left-to-Right (LTR) / Right-to-Left (RTL)
弱字符(数字和数字相关的符号)
和强字符一样方向性也是确定的,但是不会影响前后字符的方向性,也不会受强方向性影响。
Neutral
中性字符(大部分标点符号和空格)
方向性不确定,由上下文环境决定其方向。
2.1 全局方向(也叫基础方向)全局方向是一个区域中的总体方向,例如一个页面、一个段落或一个句子。中英文环境一般是 (LTR) 从左至右,而阿拉伯文环境则为 (RTL) 右至左的书写顺序。我们可以通过dir属性或者direction样式,设置指定方向。
2.2 方向串目前我们还没说到文章开始提到的 LRO 和 PDF 控制字符,下面我们先把这两个控制字符从号码中去掉,仅将 “(415)555-3695” 套用到阿拉伯文和中英文环境,观察会出现哪些问题:
123456
phone:(415)555-3695
هاتف:(415)555-3695
هاتف:(415)555-3695
هاتف:(415)555-3695
可以看到在中英文环境中,文本、数字和标点符号都按照从左至右的顺序书写,展示正常。
但在阿拉伯文环境中,电话号码好像按符号分割分组并方向展示了,实际上不是故意这样输入的,而是输入 阿拉伯文字+电话号码 之后自动就变成这样了。这是怎么回事?
这里要引入 方向串 (Directional Run) 的概念,是指在一段文字中具有相同方向性的连续字符,并且其前后没有相同方向性的其它方向串。
全局方向、文本中的字符强弱类型 决定了如何分割方向串,以上面的例子做分析:
文中首个强类型字符是阿拉伯文字,因此其所在的文本区域的全局方向为RTL,但弱类型的数字则保持了原方向LTR,而中性字符”-“没有被强字符包围则跟随了全局方向。
对于以上含有阿拉伯文字的段落,我们可以得到6个不同的方向串。正是因为中性符号被全局方向影响,使得原本的号码被拆分成不同的方向串,从而重新排列。
3 Unicode控制字符为了解决上面的问题你,Unicode标准中定义了一系列方向性控制字符,这些字符在页面上并不显示,也不占用展示空间。他们像是一些标记,影响着BIDI算法对文字书写方向的判断。下面介绍一些常用的控制字符。
3.1 隐式控制字符
名称
方向
Unicode Code
HTML Code
Left-To-Right Mark (LRM)
左->右
U+200E
&lrm、、
Right-To-Left Mark (RLM)
右->左
U+200F
&rlm、、
隐式控制字符的概念比较简单,可以理解为一个不会展示出来的强字符,LRM 为从左到右的强字符,而 RLM 为从右到左的强字符。
那我们如何利用隐式控制解决上面号码的问题?两种解决方案:
把影响全局方向的强字符左右用 LTR 包裹起来,强制改变他们的方向
把每个中性字符 -、() 左右用 LTR 包裹起来,中性字符被左至右的强字符包裹,它的方向也应该会变为从左至右。
还有一种是使用显示控制字符,这里也先写一下,后面会详细介绍。使用 LRO 可以修改强字符方向。
1234567891011121314
再举个例子:
12345
123456@qq.com
123456@qq.com
但写这么多未免繁琐,所以 iOS 实现相同效果只用了 LRO 和 PDF 两个字符,这两个字符又有什么作用呢?
3.2 显式控制字符显式控制字符需要成对使用,前四个字符 LER RLE LRO RLO 为开始字符,最后一个 PDF 为结束字符。
LRE & RLE : 接下来的文字片段内的方向变为 从左至右 / 从右至左。效果类似基础方向,将一段文本中的基础方向变更。
LRO & RLO : 顾名思义 override,接下来的所有 Unicode 字符的方向性将被覆盖为 从左至右强字符 / 从右至左强字符。
3.2.1 Embedding
名称
方向
Unicode Code
HTML Code
Left-To-Right Embedding (LRE)
左->右
U+202A
, dir='ltr'
Right-To-Left Embedding (RLE)
右->左
U+202B
, dir='rtl'
Pop Directional Formatting (PDF)
结束标记
U+202C
使用LRE或者RLE,可以改变控制字符后的文本区域的全局方向。但不影响强字符和弱字符的方向。
效果和 dir属性或者direction样式 相同。
3.2.2 Override
名称
方向
Unicode Code
HTML Code
Left-To-Right Override (LRO)
左->右
U+202D
, ..
Right-To-Left Override (RLO)
右->左
U+202E
, ..
LRO或者RLO会强制将控制符后的字符的方向属性覆盖为对应的方向,各种字符都会被影响,包括强字符。
结束标记符使用的也是PDF,在此处PDF 表示为。
3.2.3 Isolate
名称
方向
Unicode Code
HTML Code
Left-To-Right Isolate (LRI)
左->右
U+2066
,
Right-To-Left Isolate (RLI)
右->左
U+2067
,
First Strong Isolate (FSI)
自适应
U+2068
,
Pop Directional Isolate (PDI)
结束标记
U+2069
,
Isolate控制符用来在双向文字中加入脱离与其父元素的全局方向的方向串,可以使用标签实现。标签有点类似与标签的作用,但不同的是标签本身带有默认方向属性,dir默认值为auto。
虽然控制符和相应的HTML元素能够达到相同的控制效果,但需要注意的是,有些浏览器现阶段并不支持等新标签,比如IE。
4 相关的CSS属性除了上文提到的控制文本方向,我们还可以使用css中unicode-bidi和direction属性决定文字渲染方向、书写方向。
具体使用方法可以参考以下教程:
direction: CSS direction 属性unicode-bidi: CSS unicode-bidi 属性
5 iOS 对通讯录号码的处理在从右至左的书写环境,虽然作为弱字符的数字还是按照从左至右的顺序书写,但是包含中性字符标点符号的电话号码,因为受到基础方向的影响,导致算法在不同环境下生成了不同的方向串,最终展示出错。
苹果为了避免这种错误产生,使用 LRO 和 PDF 控制字符包裹号码部分,使得其中的字符始终为强字符从左至右。
所以iOS 通讯录中复制电话号码都出的两个字符,并不是什么 bug,而是有意为之的,是为了避免不同语言环境下,电话号码的展示不一致。
6 Python 代码在Python中,可以使用unicodedata模块来判断一个字符串是否包含RLO或LRO字符,并将其删除或替换为其他字符
1234567891011import unicodedatadef remove_RLO_LRO(s): # 判断字符串是否包含RLO或LRO字符 has_RLO = any(unicodedata.bidirectional(c) == 'R' for c in s) has_LRO = any(unicodedata.bidirectional(c) == 'L' for c in s) if has_RLO or has_LRO: # 将RLO和LRO替换为空格 s = s.replace('\u202b', '').replace('\u202e', '') return s.strip() return s
7 结语虽然前面介绍了几种改变文本方向的方法,但实际情况会复杂得多。大家可根据实际情况使用。