Unicode 控制字符及其有關(guān)的雙向算法(BIDI算法)

coverImage.jpg

前言

前段時(shí)間項(xiàng)目中遇到了一個(gè)有關(guān) Unicode 字符處理的問(wèn)題,今天抽空做個(gè)總結(jié)。先來(lái)說(shuō)下現(xiàn)象吧,有個(gè)用戶的昵稱中帶了阿拉伯文,在App里顯示的時(shí)候,所有和這個(gè)帶有阿拉伯文的username做過(guò)拼接的字符串,都被翻轉(zhuǎn)了,比如下面這樣:

// 阿拉伯字符 "??"   Unicode編號(hào):U+0647  U+0665
let text = "??晴子" + "middle." + "last." + ??
// 最后打印結(jié)果:text = ???last.middle.晴子?
print("text = \(text)")

最后展示在App上的時(shí)候是 ???last.middle.晴子?這個(gè)樣子的,其實(shí)第一眼看到的時(shí)候沒(méi)感覺(jué)到什么異樣,因?yàn)槲覀円膊恢烙脩舻拿质巧稑拥模?dāng)時(shí)發(fā)現(xiàn)這個(gè)問(wèn)題是因?yàn)槲覀冇袀€(gè)場(chǎng)景需要加VIP標(biāo)示,在username后面通過(guò)富文本加了一個(gè)VIP的圖片,最后發(fā)現(xiàn)這個(gè)VIP圖片展示在了最前面,這才發(fā)現(xiàn)了此問(wèn)題。

關(guān)鍵知識(shí)點(diǎn)

  1. Unicode(統(tǒng)一碼、萬(wàn)國(guó)碼、單一碼)是一種在計(jì)算機(jī)上使用的字符編碼。它為每種語(yǔ)言中的每個(gè)字符設(shè)定了統(tǒng)一并且唯一的二進(jìn)制編碼,以滿足跨語(yǔ)言、跨平臺(tái)進(jìn)行文本轉(zhuǎn)換、處理的要求。1990年開(kāi)始研發(fā),1994年正式公布。

如果把各種文字編碼形容為各地的方言,那么Unicode就是世界各國(guó)合作開(kāi)發(fā)的一種語(yǔ)言。

在這種語(yǔ)言環(huán)境下,不會(huì)再有語(yǔ)言的編碼沖突,在同屏下,可以顯示任何語(yǔ)言的內(nèi)容,這就是Unicode的最大好處。 就是將世界上所有的文字用2個(gè)字節(jié)統(tǒng)一進(jìn)行編碼。那樣,像這樣統(tǒng)一編碼,2個(gè)字節(jié)就已經(jīng)足夠容納世界上所有的語(yǔ)言的大部分文字了。

Unicode的學(xué)名是"Universal Multiple-Octet Coded Character Set",簡(jiǎn)稱為UCS。

現(xiàn)在用的是UCS-2,即2個(gè)字節(jié)編碼,而UCS-4是為了防止將來(lái)2個(gè)字節(jié)不夠用才開(kāi)發(fā)的。

  1. 字體的設(shè)計(jì)原理(字符集、編碼、字體三者的關(guān)系)

1)、字符集:就是各種字符的集合,比如Unicode字符集ASCII字符集、GB2312字符集、BIG5字符集、 GB18030字符集等等。

2)、編碼:一個(gè)字符要能被計(jì)算機(jī)所接受,需要進(jìn)行兩次編碼,因?yàn)橛?jì)算機(jī)只能表示二進(jìn)制,對(duì)于人們常使用的10進(jìn)制來(lái)講不是很方便,因此字符的第一次編碼就是把相應(yīng)的字符使用一個(gè)整數(shù)值與其相對(duì)應(yīng),比如ASCII碼字符集把字符'a'編碼為10進(jìn)制的61,就是一次編碼,Unicode字符集也是一次編碼。為了能讓計(jì)算機(jī)的二進(jìn)制識(shí)別,需要把第一次編碼后的整數(shù)值再次編碼為二進(jìn)制值,比如使用一個(gè)字節(jié)來(lái)表示字符'a'一次編碼后的整數(shù)值61,再如中文漢字使用兩個(gè)字節(jié)進(jìn)行表示,再如對(duì)于Unicode字符集有3種不同的二次編碼方案,分別是UTF-8UTF-16UTF-32,目前使用較多的是使用UTF-8來(lái)存儲(chǔ)的Unicode字符集。

3)、字形(glyph):用于表示字符的外形,比如字母aASCII碼為61,但這個(gè)字母可以以多種外形對(duì)其進(jìn)行書(shū)寫(xiě),再如中文字符中的每一筆畫(huà)都是一個(gè)字形。注:glyph也翻譯為圖元,圖像。其實(shí)對(duì)象的外形都是使用圖元進(jìn)行描述的。

4)、字形與字符的關(guān)系:一個(gè)字形可以用于表示多個(gè)字符,一個(gè)字符也可以由多個(gè)字形組成,比如中文字符,就經(jīng)常共享字形,而且是由多個(gè)字形組成的。字符的襯線、粗細(xì)等都是字形設(shè)計(jì)的元素。

5)、字體:是一個(gè)擁有相同設(shè)計(jì)風(fēng)格的字形及從字符到字形映射關(guān)系的集合,字體使字符能被顯示出來(lái),字體是計(jì)算機(jī)顯示文字的一種方式,比如早期電報(bào)就把字符表示為一長(zhǎng)串?dāng)?shù)字,這一長(zhǎng)串?dāng)?shù)字就相當(dāng)于是字符集,當(dāng)接收到電報(bào)后,是使用宋體、草書(shū)或者其他形式顯示出來(lái),就需要使用字體了,每種字體都有一個(gè)相應(yīng)的名字,比如“Times New Roman”、“宋體”等,相同的字體顯示的字符具有相似的風(fēng)格,比如以宋體顯示的字體,其風(fēng)格都是相似的。另外,字體名通常有版權(quán),是受到法律保護(hù)的。

6)、計(jì)算機(jī)顯示字符的原理簡(jiǎn)述:當(dāng)計(jì)算機(jī)接收到一串二進(jìn)制之后,表示的具體是什么字符,需要由字符集來(lái)決定,然后字符需要被顯示出來(lái)(即字體以什么外觀進(jìn)行顯示),這時(shí)就需要尋找相應(yīng)的字體,若字庫(kù)中沒(méi)有相應(yīng)字符的字體,則可能會(huì)被顯示為亂碼,所以要讓計(jì)算機(jī)正確顯示文字,不僅編碼應(yīng)正確,還要有相應(yīng)的字體才行。

文本顯示順序與Unicode雙向算法

1、文本的方向

大多數(shù)語(yǔ)言的文本在水平方向都是按從左到右(Left To Right,簡(jiǎn)稱LTR)的順序顯示字符的,但也有不少語(yǔ)言是按從右到左(Right To Left,簡(jiǎn)稱RTL)的順序顯示字符的(比如阿拉伯語(yǔ)、希伯來(lái)語(yǔ))。當(dāng)然還有按垂直方向書(shū)寫(xiě)的文本,比如中國(guó)古代的漢字、蒙語(yǔ)就是從上到下從右到左書(shū)寫(xiě)的,本文只討論水平方向書(shū)寫(xiě)的文本,垂直方向書(shū)寫(xiě)的文本不予討論。

雙向文本是指一個(gè)字符串中同時(shí)包含LTR和RTL的文文,既包含從左到右的文本又包含從右到左的文本?,F(xiàn)實(shí)中,從右向左書(shū)寫(xiě)的語(yǔ)言通常會(huì)夾雜著從左向右的文本(比如外語(yǔ)、引用、數(shù)字、符號(hào)等),因此,像阿拉伯語(yǔ)、希伯來(lái)語(yǔ)這些語(yǔ)言通常都是雙向文本。另外,當(dāng)在從左向右的文本中插入從右向左的文本時(shí)也會(huì)產(chǎn)生雙向文本的問(wèn)題。

2、邏輯順序與顯示順序

邏輯順序在Unicode標(biāo)準(zhǔn)內(nèi)規(guī)定為文本在內(nèi)存中表示的順序,而顯示順序就是最終顯示在我們面前所看到的文本的順序,文本的邏輯順序和顯示順序并不一定會(huì)一致,比如對(duì)于從右向左顯示的文本,顯示順序應(yīng)是從右向左的,而邏輯順序則可能是從左向右的。邏輯順序是屬于計(jì)算機(jī)底層的問(wèn)題,不屬于本文討論的范圍,我們需要解決的是文本的顯示順序題。

3、Unicode雙向算法(BIDI)與定向格式化字符

1)、對(duì)于雙向文本,若不明確的確定文本的顯示順序,在顯示時(shí)就可能會(huì)出現(xiàn)歧義,為此,需要為雙向文本的顯示定義一種算法(或者一種規(guī)則),用于規(guī)范雙向文本的顯示順序。

2)、通常有一種隱式算法(或稱為隱式雙向排序或隱式布局算法)來(lái)定義雙向文本的顯示,但是隱式算法并不足以產(chǎn)生可供理解的文本,為此,對(duì)某些字符的顯示順序需要明確地進(jìn)行控制,就是使用一系列的控制符(類似于HTML中的元素)來(lái)控制字符的顯示順序,這些控制符被Unicode稱為定向格式化字符。比如,使用RLO控制符來(lái)控制字符從右向左顯示,PDF表示RLO的終止字符,那么ab cd RLO EF GH PDF x,將被顯示為ab cd HG FE x,可見(jiàn),Unicode控制符的原理與HTML的元素是相似的。

3)、定向格式化字符只影響文本的顯示順序,在其它方面會(huì)被忽略,也就是說(shuō)定向格式化字符不會(huì)對(duì)文本的比較、斷句、詞法分析、數(shù)值分析等方面造成影響。

4)、Unicode雙向算法(也稱為BIDI)是對(duì)隱式算法的擴(kuò)展,Unicode雙向算法定義了定向格式化字符(即控制符),并且定義了一套算法,用于規(guī)定這些控制符對(duì)需要顯示的字符產(chǎn)生怎樣的影響。

Unicode字符的分類與定向格式化字符

  • 字符的屬性
    Unicode為字符定義了很多屬性,以用于描述該字符,比如Bidi_Paired_Bracket_Type屬性用于描述該字符是開(kāi)括號(hào)(值為open)還是閉括號(hào)(值為close),再如General_Category描述了該字符的通用類別,比如若該字符是行分隔符,則值為Line_Separator,是控制符,則值為Control等。

  • 字符的類型
    Unicode為每個(gè)Unicode字符都定義了一種類型(稱為雙向字符類型或bidi類型),雙向字符類型被分為:強(qiáng)字符(強(qiáng)類型)、弱字符(弱類型)、中性字符(中性類型)定向格式化字符,詳見(jiàn)表1。

表1 ? - ? 字符的類型

分類 類型 簡(jiǎn)述
強(qiáng)字符(strong) L left to right
R right to left RLM(見(jiàn)表2),希伯來(lái)字母和相關(guān)的標(biāo)點(diǎn)符號(hào)
AL right to left Arabic ALM(見(jiàn)表2),阿拉伯語(yǔ)(Arabic)、它拿字母(Thaana)、敘利亞字母,及大多數(shù)特定于這些文字的標(biāo)點(diǎn)符號(hào)
弱字符(weak) EN 歐洲數(shù)字(European Number) [歐洲數(shù)字、東阿拉伯-印度數(shù)字,經(jīng)常使用的數(shù)字1,2,3等就是屬于EN類型]
ES 歐洲數(shù)字分隔符 加號(hào),減號(hào)
ET 歐洲數(shù)字終止符 度的符號(hào),貨幣符號(hào),比如,$(美元),¥(人民幣)等
AN 阿拉伯?dāng)?shù)字 阿拉伯-印度數(shù)字,阿拉伯小數(shù)和千位分隔符,平時(shí)使用的數(shù)字雖然叫做阿拉伯?dāng)?shù)字,但阿拉伯擁有自已的數(shù)字,比如,4的阿拉伯?dāng)?shù)字字符為?(u+0664)
CS 普通數(shù)字分隔符 冒號(hào),逗號(hào),句點(diǎn)(即小數(shù)點(diǎn)),不間斷空格(no-break space)等,注意:?jiǎn)我?hào)、雙引號(hào)、分號(hào)不屬于該類型,中文的句號(hào)也不屬于該類型
NSM 無(wú)間距標(biāo)記(Nonspacing mark) 屬性General_Category為以下值的字符:Mn(Nonspacing_Mark)和Me(Enclosing_Mark)比如,組合用發(fā)音字符的上左角 ? (u+031A),西非書(shū)面文中的? (u+08FE) 。詳見(jiàn)后文對(duì)組合字符的講解
BN 中性邊界 不是明確給定類型的字符,比如:可忽略的默認(rèn)值,非字符,控制字符等。比如,廣義標(biāo)點(diǎn)中的不可見(jiàn)乘號(hào)(u+2062)就是BN類型
中性字符(neutral) B 段落分隔符[段落分隔符(u+2029),適當(dāng)?shù)膿Q行符函數(shù),高級(jí)別確定段落的協(xié)議]
S 節(jié)分隔符(Segment Separator) Tab
WS 空白(Whitespace) 空格,圖形空格,行分隔符,換頁(yè)符,常用標(biāo)點(diǎn)符號(hào)的空格等
ON 其他中性符 所有其他字符,包括對(duì)象替換字符,比如,[、]、(、)、"、'、@、&、*、、<、>、|、{、}、;(分號(hào))、!、?、~、=。注意:/ 屬于CS類型、%、#屬于ET類型

  • 定向格式化字符的分類
    字向格式化字符分為隱式定向格式化字符顯示定向格式化字符兩大類,顯示定向格式化字符又分為顯示定向嵌入格式化字符、顯示定向重寫(xiě)格式化字符、顯示定向隔離格式化字符,分別簡(jiǎn)稱為嵌入格式化字符、重寫(xiě)格式化字符、隔離格式化字符。其中隔離格式化字符是在Unicode 6.3中引入的。詳見(jiàn)表2。

表2 ? - ? 定向格式化字符的分類

類型 控制符 Unicode代碼 簡(jiǎn)述 說(shuō)明
隱式定向格式化字符 LRM U+200E left to right mark 從左到右的零寬度字符
RLM U+200F right to left mark 從右到左的零寬度非阿拉伯字符
ALM U+061C arabic letter mark 從右到左的零寬度阿拉伯語(yǔ)字符
顯示定向嵌入重寫(xiě)格式化字符 LRE U+202A left to right embedding 嵌入。把后面的文本看作是從左到右(LRE)或從右到左(RLE)的嵌入
RLE U+202B right to left embedding
LRO U+202D left to right override 重寫(xiě)。強(qiáng)制改變其后的文本的方向?yàn)閺淖笙蛴?LRO)或從右向左(RLO)
RLO U+202E right to left override
PDF U+202C pop directional formatting 嵌入和重寫(xiě)終止符。用于終止LRE、RLE、LRO、RLO的作用范圍
顯示定向隔離格式化字符 LRI U+2066 left to right isolate 從左到右(LRI)或從右到左(RLI)的隔離之后的文本
RLI U+2067 right to left isolate
FSI U+2068 first strong isolate 隔離之后的文本,文本的方向由第一個(gè)非嵌套在隔離中的強(qiáng)字符決定
PDI U+2069 pop directional isolate 隔離終止符。用于終止LRI、RLI、FSI的作用范圍,該終止符還會(huì)同時(shí)終止LRE、RLE、LRO、RLO的作用范圍

  • 組合字符
    1)、組合字符(Combining character)是指General_Category屬性的值為Mc (Spacing Combining Mark,間距組合標(biāo)記)、Mn (Nonspacing Mark,無(wú)間距標(biāo)記)、Me(Enclosing Mark,嵌入標(biāo)記)的所有字符。
    2)、組合字符通常用于與它的基本字符組合為一個(gè)字符。
    3)、無(wú)間距標(biāo)記(Mn)通常不單獨(dú)占據(jù)空間位置,其占據(jù)的位置取決于它的基本字符。

  • 零寬度字符 ( LRM、RLM、ALM )
    表2中的LRM、RLM、ALM是一種零寬度字符,可將其理解為在該處插入了一個(gè)相應(yīng)方向的強(qiáng)字符,但該字符是不可見(jiàn)的(寬度為零所以不可見(jiàn))。比如aLRMb,相當(dāng)于在a和b之間插入了一個(gè)從左向右的強(qiáng)字符,但該字符寬度為零且不可見(jiàn)(即不會(huì)被顯示)。

  • 與定向格式化字符對(duì)應(yīng)的HTML5元素和CSS等效項(xiàng)
    1)、HTML5沒(méi)有提供對(duì)LRE、RLE、LRO、RLO的精確等效項(xiàng),可使用CSS來(lái)獲取LRE、RLE、LRO、RLO、LRI、RLI、FSI的精確等效項(xiàng)。表3為HTML和CSS與bidi算法的等效情形。由表3可見(jiàn),在HTML5中,bdi元素更多用于對(duì)字符的隔離,以避免文本被周圍字符方向性所影響,或避免隔離的文本影響周圍字符的方向性,而bdo元素主要用于強(qiáng)制改變文本的方向性。
    2)、注意:HTML5與HTML4.0不同,早期版本的bdi元素與隔離(LRI、RLI)相對(duì)應(yīng),bdo元素與重寫(xiě)(LRO、RLO)相對(duì)應(yīng)。

表3 ? - ? 各平臺(tái)對(duì)BIDI算法的實(shí)現(xiàn)

BIDI算法 HTML5的等效項(xiàng) CSS的等效項(xiàng)
RLI ... PDI dir = "rtl" (任意元素的dir屬性) direction:rtl; unicode-bidi:isoloate;
LRI ... PDI dir = "ltr"(任意元素的dir屬性) direction:ltr; unicode-bidi:isoloate;
FSI ... PDI <bdi>或dir = "auto" unicode-bidi:plaintext;
RLE ... PDF 無(wú)對(duì)應(yīng)元素 direction:rtl; unicode-bidi:embed;
LRE ... PDF 無(wú)對(duì)應(yīng)元素 direction:ltr; unicode-bidi:embed;
RLO ... PDF 無(wú)對(duì)應(yīng)元素 direction:rtl; unicode-bidi:bidi-override;
LRO ... PDF 無(wú)對(duì)應(yīng)元素 direction:ltr; unicode-bidi:bidi-override;
FSI RLO ... PDF PDI <bdo dir = "rtl"> direction:rtl; unicode-bidi:isoloate-override;
FSI LRO ... PDF PDI <bdo dir = "ltr"> direction:ltr; unicode-bidi:isoloate-override;

運(yùn)行等級(jí)與隔離運(yùn)行序列

一、基本名詞
  • 嵌入等級(jí)(或嵌入水平)((level,可翻譯為:等級(jí)、水平、級(jí)別):表示字符的嵌入層次,數(shù)字越大嵌入得越深,需要注意的是,在bidi算法中,字符串中的每個(gè)字符都有一個(gè)嵌入等級(jí)。

  • 基礎(chǔ)方向(base direction):分段的方向被稱為基礎(chǔ)方向,基礎(chǔ)方向決定了該段文本從瀏覽器的左側(cè)還是右側(cè)開(kāi)始書(shū)寫(xiě)。

  • 隔離啟動(dòng)器:是對(duì)LRI、RLI、FSI的統(tǒng)稱,注意:隔離啟動(dòng)器不包括PDI

  • 嵌入啟動(dòng)器:是對(duì)LRE、RLE、LRO、RLO的統(tǒng)稱,注意:嵌入啟動(dòng)器不包括PDF

二、運(yùn)行等級(jí)和隔離運(yùn)行序列

1. 運(yùn)行等級(jí)(level run):也稱為定向運(yùn)行(directional run),是指具有相同嵌入等級(jí)的字符所形成的最大子串,該子串與其直接接觸的前后字符的嵌入等級(jí)不相同,比如ab cd RLE ef gh PDF kk mm,假設(shè)分段的嵌入等級(jí)為0,則字符a、b、c、d(含其中的空格)的嵌入等級(jí)都為0,字符e、f、g、h的嵌入等級(jí)都為1,字符k、k、m、m的嵌入等級(jí)為0,因此,該字符串共有3個(gè)運(yùn)行等級(jí),分別是子串a(chǎn)b cd,子串ef gh,子串kk mm。

2. 隔離運(yùn)行序列(簡(jiǎn)稱為運(yùn)行序列或序列):是由一系列運(yùn)行等級(jí)組成的序列,其規(guī)則如下:

  • 含有隔離啟動(dòng)器時(shí):除最后一個(gè)運(yùn)行等級(jí)外,隔離運(yùn)行序列中運(yùn)行等級(jí)的最后一個(gè)字符是隔離啟動(dòng)器,與該隔離啟動(dòng)器匹配的PDI是序列中下一個(gè)運(yùn)行等級(jí)的第一個(gè)字符,也就是說(shuō),序列中的運(yùn)行等級(jí)是以隔離啟動(dòng)器結(jié)束的(最后一個(gè)運(yùn)行等級(jí)除外),以PDI開(kāi)始的(除第一個(gè)運(yùn)行等級(jí)外)。
  • 無(wú)隔離啟動(dòng)器時(shí):此時(shí)每個(gè)運(yùn)行等級(jí)構(gòu)成一個(gè)獨(dú)立的隔離運(yùn)行序列。

3. 隔離運(yùn)行序列具有如下特點(diǎn):

  • 每個(gè)運(yùn)行等級(jí)只屬于一個(gè)隔離運(yùn)行序列,也就是說(shuō),不存一個(gè)運(yùn)行等級(jí)屬于兩個(gè)序列的情形。
  • 在同一個(gè)隔離運(yùn)行序列中所有的運(yùn)行等級(jí)具有相同的嵌入等級(jí),因?yàn)楦綦x運(yùn)行序列是以隔離啟動(dòng)器開(kāi)始一個(gè)運(yùn)行等級(jí),又以與其匹配的PDI開(kāi)始另一個(gè)運(yùn)行等級(jí),很明顯,這兩個(gè)運(yùn)行等級(jí)具有相同的嵌入等級(jí)。
  • 緊隨著隔離啟動(dòng)器之后的運(yùn)行等級(jí)會(huì)開(kāi)啟一個(gè)新的隔離運(yùn)行序列,與之匹配的PDI之前的運(yùn)行等級(jí)會(huì)結(jié)束它的隔離運(yùn)行序列。

4. 隔離啟動(dòng)器的重要規(guī)則:隔離啟動(dòng)器和與其匹配的PDI擁有的嵌入等級(jí)是提升之前的原始嵌入等級(jí),而不是提升之后的嵌入等級(jí)。

解決問(wèn)題

通過(guò)上面的介紹,我們發(fā)現(xiàn)文章最開(kāi)始提到的問(wèn)題是如何產(chǎn)生的了,就是因?yàn)榘⒗址脑颍瑢?dǎo)致我們的文字方向被改變了。那解決這個(gè)問(wèn)題的方法當(dāng)然就是強(qiáng)制設(shè)定為文字的方向就行,所以整體思路就是:

targetString -> unicodeString -> 插入`Unicode`的`定向格式化字符 -> resultString

相關(guān)代碼(Swift):

    // MARK: - Unicode雙向算法(BIDI算法) -
    /// 強(qiáng)制字符串從左到右(在阿拉伯文環(huán)境中默認(rèn)是從右到左)
    static func unicodeDirectionalRun(string: String) -> String {
        /*
         顯式雙向控制字符 (Explicit Markers)
         顯示定向嵌入和重寫(xiě)格式化字符
         U+202A:   LEFT-TO-RIGHT EMBEDDING      (LRE)
         U+202B:   RIGHT-TO-LEFT EMBEDDING      (RLE)
         U+202D:   LEFT-TO-RIGHT OVERRIDE       (LRO)   重寫(xiě)。強(qiáng)制改變其后的文本的方向?yàn)閺淖笙蛴?LRO)或從右向左(RLO)
         U+202E:   RIGHT-TO-LEFT OVERRIDE       (RLO)
         U+202C:   POP DIRECTIONAL FORMATTING   (PDF)   嵌入和重寫(xiě)終止符。用于終止LRE、RLE、LRO、RLO的作用范圍
         顯式控制字符需要成對(duì)使用,前四個(gè)字符 LER RLE LRO RLO 為開(kāi)始字符,最后一個(gè) PDF 為結(jié)束字符。
         
         LRE & RLE : 接下來(lái)的文字片段內(nèi)的方向變?yōu)?從左至右 / 從右至左。效果類似基礎(chǔ)方向,將一段文本中的基礎(chǔ)方向變更。
         LRO & RLO : 顧名思義 override,接下來(lái)的所有 Unicode 字符的方向性將被覆蓋為 從左至右強(qiáng)字符 / 從右至左強(qiáng)字符。
         
         顯示定向隔離格式化字符 (從左到右(LRI)或從右到左(RLI)的隔離之后的文本)
         U+2066:   LEFT-TO-RIGHT ISOLATE    (LRI)
         U+2067:   RIGHT-TO-LEFT ISOLATE    (RLI)
         U+2068:   FIRST STRONG  ISOLATE    (FSI)   隔離之后的文本,文本的方向由第一個(gè)非嵌套在隔離中的強(qiáng)字符決定
         U+2069:   POP DIRECTIONAL ISOLATE  (PDI)   隔離終止符。用于終止LRI、RLI、FSI的作用范圍,該終止符還會(huì)同時(shí)終止LRE、RLE、LRO、RLO的作用范圍
         */
                
        //eg:牛逼的字符(阿拉伯文) = "??"  會(huì)把字符串翻轉(zhuǎn)
        //let nameStr = "??" + "晴子" + "."
        var unicodeStr = String.utf8ToUnicode(string: string)
        // 這里用 contains 和 hasPrefix 看自己需求
       if unicodeStr.hasPrefix("\\u2066") && !unicodeStr.hasPrefix("\\U2066") {
            return string
        }
        else {
            // 此處使用 LRI
            unicodeStr = "\\u2066" + String.utf8ToUnicode(string: string) + "\\u2069"
            let result = String.replaceUnicode(unicodeStr: unicodeStr)
            
            return result
        }

    /// unicode轉(zhuǎn)中文
    ///
    /// - Parameter unicodeStr: str
    /// - Returns:
    static func replaceUnicode(unicodeStr: String) -> String {
        if unicodeStr.isEmpty {
            return unicodeStr
        }
        let tempStr1 = unicodeStr.replacingOccurrences(of: "\\u", with: "\\U")
        let tempStr2 = tempStr1.replacingOccurrences(of: "\"", with: "\\\"")
        let tempStr3 = "\"".appending(tempStr2).appending("\"")
        let tempData = tempStr3.data(using: String.Encoding.utf8)
        var returnStr: String = ""
        do {
            returnStr = try PropertyListSerialization.propertyList(from: tempData!, options: [.mutableContainers], format: nil) as! String
        } catch {
            print("replaceUnicode(unicodeStr: String) -> String  error = \(error)")
        }
        return returnStr.replacingOccurrences(of: "\\r\\n", with: "\n")
    }

    /// 中文轉(zhuǎn)unicode (utf8)
    ///
    /// - Parameter string: str
    /// - Returns:unicode
    static func utf8ToUnicode(string: String) -> String {
        if string.isEmpty {
            return string
        }
        // 全英文的時(shí)候 .nonLossyASCII 會(huì)在前面加 "\\255" ,導(dǎo)致最后轉(zhuǎn)中文的時(shí)候多了個(gè) ">" 字符
        //let dataEncode = string.data(using: String.Encoding.nonLossyASCII)
        if let dataEncode = string.data(using: String.Encoding.utf8) {
            if let unicodeStr = String(data: dataEncode, encoding: String.Encoding.utf8) {
                return unicodeStr
            }
            else {
                return string
            }
        }
        else {
            return string
        }
    }

通過(guò)上面的案例,突然想到翻轉(zhuǎn)字符串又多了一種新的思路,可以通過(guò)BIDI算法靈活的控制按子串或者整串翻轉(zhuǎn)。

有興趣的同學(xué)可以試試 LRE 和 LRO 算法會(huì)是怎樣的效果

突然發(fā)現(xiàn)這個(gè)符號(hào)"?",單個(gè)和連起來(lái)多個(gè)顯示還不一樣,有點(diǎn)意思??????
1個(gè):?
2個(gè):??
3個(gè):???
4個(gè):????
5個(gè):?????
6個(gè):??????
????晴子.???????

最后推薦大家看 Unicode官網(wǎng) ,上面有關(guān) Unicode 的知識(shí)點(diǎn)都很全面。
今天就分享這么多,希望能解決各位客官的問(wèn)題^_^

參考資料

  1. Unicode官網(wǎng)
  2. Understanding Bidirectional (BIDI) Text in Unicode
  3. UNICODE BIDIRECTIONAL ALGORITHM
  4. Unicode 控制字符及其有關(guān)的雙向算法
  5. Unicode雙向算法(bidi算法)詳解(一)
  6. 符號(hào)字工具網(wǎng)站
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容