整合營銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          了不起的 Unicode

          了不起的 Unicode

          文精心挑選了許多優(yōu)秀的Unicode小技巧、軟件包和資源。

          譯者 | 彎月,責(zé)編 | 郭芮

          出品 | CSDN(ID:CSDNnews)

          以下為譯文:

          Unicode非常了不起!在Unicode出現(xiàn)之前,國際交流是一團(tuán)糟——每個人都在ASCII碼表的后半部分區(qū)域(稱為“代碼頁”)定義了自己的擴(kuò)展和字符集,從而導(dǎo)致各種沖突。想想就知道,德國人要與韓國人只使用127個字符組成的代碼頁進(jìn)行交流會有多么困難。

          ——幸虧有了Unicode標(biāo)準(zhǔn)和統(tǒng)一的交流規(guī)范。

          Unicode 8.0根據(jù)129多種書寫體系,標(biāo)準(zhǔn)化了超過120,000個字符,其中包括現(xiàn)代字符、古代字符,甚至還包括人類尚未解密的文字。Unicode能處理從左到右和從右到左兩種書寫方式,支持組合標(biāo)記,還支持多種文化、政治、宗教方面的字符,甚至還有表情符號。

          Unicode太了不起了,我們對它的崇拜猶如滔滔江水綿綿不絕。

          Unicode的背景

          Unicode標(biāo)準(zhǔn)支持什么字符?

          Unicode標(biāo)準(zhǔn)定義了今日所有主流的書寫語言中用到的字符。Unicode支持的書寫體系包括歐洲的語系、中東的從右至左書寫的語系,以及亞洲的多種語系。

          Unicode標(biāo)準(zhǔn)還包含了標(biāo)點(diǎn)符號、聲調(diào)符號、數(shù)學(xué)符號、科技符號、箭頭、各種圖形符號、表情符號,等等。Unicode為聲調(diào)符號(用來改變其他字符的符號,如波浪線~)單獨(dú)提供了代碼,這些代碼可以與基礎(chǔ)字符組合使用,來表示有聲調(diào)的字符(如?)。Unicode標(biāo)準(zhǔn)9.0版總共提供了128,172個字符的代碼,其中包括了全世界的字符、圖形和符號。

          絕大部分的常用字符都能映射到最前面的64K個代碼點(diǎn)上,這一區(qū)域叫做基本多文種平面(basic multilingual plane,簡稱為BMP)。還有十六個補(bǔ)充平面用來編碼其他字符,目前尚有850,000個未使用的代碼點(diǎn)。人們還在考慮在以后的版本中添加更多的字符。

          Unicode標(biāo)準(zhǔn)還保留了一些代碼點(diǎn)供私人使用。供應(yīng)商或最終用戶可以在內(nèi)部利用這些代碼點(diǎn)表示他們自己的字符和符號,或者通過特殊的字體來使用。BMP上有6,400個私有代碼點(diǎn),如果不夠的話,補(bǔ)充平面上還有131,068個私有代碼點(diǎn)可供使用。

          Unicode字符編碼

          字符編碼標(biāo)準(zhǔn)不僅定義了每個字符的唯一標(biāo)識(即字符的數(shù)字值,或者叫做代碼點(diǎn)),也定義了怎樣用比特來表示這個值。

          Unicode標(biāo)準(zhǔn)定義了三種編碼形式,允許同一個數(shù)據(jù)以一字節(jié)、兩字節(jié)或四字節(jié)的格式來傳輸(即每個代碼單元可以是8比特、16比特或32比特)。同一個字符集可以使用所有三種編碼形式,它們之間可以互相轉(zhuǎn)換,而不會丟失數(shù)據(jù)。Unicode聯(lián)盟建議根據(jù)實(shí)際需要,選擇任何一種方便的編碼方式來實(shí)現(xiàn)Unicode標(biāo)準(zhǔn)。

          UTF-8在HTML和類似協(xié)議上非常常用。UTF-8使用變長編碼。它的優(yōu)點(diǎn)是,對應(yīng)于ASCII字符集的那些Unicode字符的字節(jié)值與它們在ASCII中的值完全相同,因此使用UTF-8編碼的Unicode字符可以在絕大多數(shù)已有軟件上使用,無需對軟件做出任何修改。

          UTF-16在許多需要平衡性能和存儲效率的環(huán)境中非常常用。它足夠緊湊,所有常用的字符都可以用一個16比特的代碼單元來表示,其他字符可以使用一對16比特代碼單元來表示。

          UTF-32在無需顧慮內(nèi)存空間的情況下使用,它是定長編碼,每個字符只有一個代碼單元。每個Unicode字符編碼成一個32比特代碼單元。

          在所有三種編碼中,每個字符最多需要4個字節(jié)(32比特)表示。

          數(shù)字問題

          Unicode字符集被分成17個核心段,稱為“平面”,每個平面又被分成若干區(qū)塊。每個平面的空間足夠容納65,536(216)個代碼點(diǎn),因此總共有1,114,112個代碼點(diǎn)。還有兩個“私有區(qū)域”平面(#16和#17),可以按照使用者的意愿定義。這兩個私有平面共包含131,072個代碼點(diǎn)。

          第一個平面叫做“基本多文種平面”,或者稱為BMP。它包含代碼點(diǎn)U+0000到U+FFFF,這個范圍內(nèi)包含了絕大部分常用字符。另外16個平面(U+010000到U+10FFFF)稱為補(bǔ)充平面。

          UTF-16代理對

          “BMP之外的字符,例如U+1D306 tetragram for centre (),在UTF-16編碼中只能編碼成兩個16比特代碼單元:0xD834 0xDF06。這種情況稱為代理對(surrogate pair)。注意代理對只表示一個字符。

          “代理對的第一個字符永遠(yuǎn)在0xD800到0xDBFF的范圍內(nèi),稱為高位代理,或者叫起始字節(jié)代理。代理對的第二個代碼單元永遠(yuǎn)在0xDC00到0xDFFF的范圍內(nèi),稱為低位代理,或者叫末端代理。”

          ——Mathias Bynens

          “代理對:一種表示方式,用于表示由兩個16比特代碼單元組成的單個抽象字符,其中第一個值稱為高位代理代碼單元,第二個值稱為低位代理單位。代理對僅在UTF-16中使用。”

          ——Unicode 8.0 第3.9章,代理對(參見Unicode編碼)

          計(jì)算代理對

          代理字符 Pile of Poo (U+1F4A9) 在UTF-16中必須編碼成代理對,即兩個代理。要將代碼點(diǎn)轉(zhuǎn)換成代理對,可以使用以下算法(用JavaScript編寫)。注意我們使用的是十六進(jìn)制表示。

          var High_Surrogate=function(Code_Point){ return Math.floor((Code_Point - 0x10000) / 0x400) + 0xD800 };
          var Low_Surrogate=function(Code_Point){ return (Code_Point - 0x10000) % 0x400 + 0xDC00 };

          // Reverses The Conversion
          var Code_Point=function(High_Surrogate, Low_Surrogate){
          return (High_Surrogate - 0xD800) * 0x400 + Low_Surrogate - 0xDC00 + 0x10000;
          };
           > var codepoint=0x1F4A9; // 0x1F4A9==128169
          > High_Surrogate(codepoint).toString(16)
          "d83d" // 0xD83D==55357
          > Low_Surrogate(codepoint).toString(16)
          "dca9" // 0xDCA9==56489

          > String.fromCharCode( High_Surrogate(codepoint) , Low_Surrogate(codepoint) );
          ""
          > String.fromCodePoint(0x1F4A9)
          ""
          > '\ud83d\udca9'
          ""

          組合和解組合

          Unicode包括了一種修改字符形狀的機(jī)制,大幅擴(kuò)展了Unicode支持的字符量。使用聲調(diào)符號進(jìn)行組合就是其中一種方式。聲調(diào)符號寫在主字符的后面。多個聲調(diào)符號可以疊在同一個字符上。對于絕大部分常用的字母聲調(diào)組合,Unicode還包括了預(yù)先組合好的版本。

          特定的字符序列也可以用單個字符表示,稱為“預(yù)組合字符”(或者叫組合字符,可以解組合的字符)。例如,字符“ü”可以編碼成單個代碼單元U+00FC “ü”,也可以編碼成基本字符U+0075 “u”后接無空白字符U+0308 “¨”。Unicode標(biāo)準(zhǔn)中設(shè)置的預(yù)組合字符是為了兼容Latin 1等標(biāo)準(zhǔn),后者包含了許多預(yù)組合字符,如“ü”和“?”。

          預(yù)組合字符可以進(jìn)行接組合,以保持一致性,或用于分析。例如,需要將一組名稱轉(zhuǎn)換為英文字母時,可以將字符“ü”解組合為“u”后接非空白字符“¨”。解組合后的結(jié)果很容易處理,因?yàn)樵摻M合字符可以處理成“u”后接一個修飾字符。這樣很容易進(jìn)行按字母順序排序等,因?yàn)樾揎椬址粫绊懽帜疙樞颉nicode標(biāo)準(zhǔn)為所有預(yù)組合字符定義了解組合方式(https://unicode.org/versions/Unicode8.0.0/ch03.pdf#page=44)。它還定義了正規(guī)化的方式,以便為字符提供唯一的表示方法。

          Unicode之謎

          來自Mark Davis的《Unicode之謎》幻燈片(https://macchiato.com/slides/UnicodeMyths.pdf)。

          • Unicode只不過是16比特編碼。一些人誤認(rèn)為Unicode只不過是16比特編碼,每個字符占用16比特,因此一共有65,536個可能的字符。實(shí)際上這是不正確的。這樣是關(guān)于Unicode的最大誤解,所以也難怪一些人會這么想。

          • 任何未分配的代碼點(diǎn)都可以用于內(nèi)部用途?錯。最終,那些未分配的地方都會被某個字符使用。你應(yīng)該使用私有用途代碼點(diǎn),或非字符代碼點(diǎn)。

          • 每個Unicode代碼點(diǎn)都表示一個字符?錯。有許多非字符代碼點(diǎn)(FFFE,F(xiàn)FFF,1FFFE,……)還有許多代理代碼點(diǎn)、私有代碼點(diǎn)和未分配的代碼點(diǎn),還有控制和格式“字符(RLM,ZWNJ,……)

          • 字符映射是一對一的?錯。映射關(guān)系也可能是:

          • 一對多:(? → SS )

          • 上下文相關(guān):(…Σ ? …? 和 …ΣΤ… ? …στ… )

          • 語言相關(guān):( I ? ? 和 ? ? i )

          實(shí)用Unicode編碼手冊

          編碼類型編碼

          神奇的字符列表

          特殊字符

          詳情可以參照Unicode聯(lián)盟發(fā)布的《通用標(biāo)點(diǎn)符號表》(https://www.unicode.org/charts/PDF/U2000.pdf)。

          等等,你說什么?

          變量標(biāo)識符可以包含空白!

          U+3164 HANGUL FILLER 字符顯示為占據(jù)空間的空白字符。如果渲染器不支持,則會渲染成完全不可見(也不會占據(jù)任何空間,即“零寬度”)。這就是說,永遠(yuǎn)不會看到丑陋的字符替代符號。

          我不知道為什么U+3164被設(shè)計(jì)成這種行為。有意思的是,U+3164是在Unicode 1.1版本(1993年)加入的,所以聯(lián)盟一定是花了很多時間思考它。下面是幾個例子:

          > var ?='foo';
          undefined
          > ?
          'foo'


          > var ?=alert;
          undefined
          > var foo='bar'
          undefined
          > if ( foo===?`baz` ){} // alert
          undefined


          > var var?foo?\u{A60C}?π='bar';
          undefined
          > var?foo???π
          'bar'

          注意:我在Ubuntu和OSX下測試了下述程序的渲染結(jié)果:Node,PHP,Ruby,Python3.5,Scala,Vim,Cat,Chrome+GitHub gist。Atom是唯一無法正確渲染,將其顯示成空方塊的程序。我還沒有測試Emacs和Sublime。據(jù)我的理解,Unicode聯(lián)盟不會改變或重命名字符或代碼點(diǎn),但有可能會改變字符屬性,如ID_Start或ID_Continue等。

          修飾符

          零寬度連接符(ZWJ)是個不可打印字符,用于某些復(fù)雜語系的計(jì)算機(jī)排版系統(tǒng)中,如阿拉伯語系、印度語系等。將ZWJ放在兩個本來不會連接的字符之間,將會導(dǎo)致它們以連接的形式打印。

          零寬度不連接符(ZWNJ)是個不可打印字符,那些使用連接的書寫系統(tǒng)的計(jì)算機(jī)化。將ZWNJ放在兩個本來會連接在一起的字符之間,會導(dǎo)致它們以本來的形式打印。空格字符也有同樣的效果,但ZWNJ的作用是它能保證輸出的兩個字符盡可能靠近,或者連接一個詞及其語素。

          > 'a'
          "a"

          > 'a\u{0308}'
          "a?"

          > 'a\u{20DE}\u{0308}'
          "a??"

          > 'a\u{20DE}\u{0308}\u{20DD}'
          "a???"

          // Modifying Invisible Characters
          > '\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}'
          " "

          > '\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}\u{200E}'.length
          10

          大寫變換沖突

          小寫變換沖突

          奇怪現(xiàn)象和排查方法

          • 字符串長度通常由統(tǒng)計(jì)代碼點(diǎn)的個數(shù)來計(jì)算。這就是說,代理對會被統(tǒng)計(jì)成兩個字符。多個聲調(diào)符號會疊放在同一個字符上,例如a + ?==?a,從而增加長度,但它們只會產(chǎn)生一個字符。

          • 類似地,字符串翻轉(zhuǎn)通常非常困難。同樣,代理對和帶有聲調(diào)符號的字符必須作為整體進(jìn)行翻轉(zhuǎn)。ES Reverser提供了一個非常好的解決方法。

          • 字符映射是一對一的?錯。映射關(guān)系也可能是:

          • 一對多:(? → SS )

          • 上下文相關(guān):(…Σ ? …? 和 …ΣΤ… ? …στ… )

          • 語言相關(guān):( I ? ? 和 ? ? i )

          一對多映射

          絕大多數(shù)字符,在大寫的時候表示一對多關(guān)系;另一些字符小寫的時候表示一對多關(guān)系。

          優(yōu)秀的軟件包和庫

          • PhantomScript::ghost: :flashlight: 不可見的JavaScript代碼執(zhí)行和社會工程工具。

          • ESReverser:用JavaScript編寫的支持Unicode的字符串翻轉(zhuǎn)。

          • mimic:Unicode的惡作劇。

          • python-ftfy:輸入U(xiǎn)nicode文本,輸出更一致、更不容易出現(xiàn)顯示錯誤的表現(xiàn)形式。

          • vim-troll-stopper:防止Unicode的搗亂字符搞亂你的代碼。

          表情符號

          • Unicode聯(lián)盟的表情符號表(https://www.unicode.org/emoji/charts/full-emoji-list.html)

          • Emojipedia(https://emojipedia.org/):關(guān)于特定表情符號的信息,新聞博客。

          • World Translation Foundation(https://www.emojifoundation.com/):宣傳、探索,還可以將文本翻譯成用表情符號表示的形式。

          • Can I Emoji? (https://caniemoji.com/android-2/):顯示當(dāng)前iOS、Android和Windows對于表情符號的原生支持情況。

          • 怎樣注冊一個表情符號URL(https://www.name.com/blog/how-tos/2015/12/want-an-emoji-url-this-is-how-you-register-one/)

          多樣性

          Unicode聯(lián)盟在支持人類的多樣性和多元文化方面做出了很多努力。這里是聯(lián)盟提供的多樣性報(bào)告(https://unicode.org/reports/tr51/#Diversity)。

          現(xiàn)在表情符號已經(jīng)支持混合型別,比如同性家庭、握手、接吻等。真正轟動的是表情符號組合序列。基本上來說:

          此外,表情符號現(xiàn)在還支持膚色修飾字符了。

          “有五個符號修飾字符可以為Unicode 8.0版(2015年中期)中發(fā)布的人類的表情符號提供一系列的膚色。這些字符基于Fitzpatrick度量(皮膚學(xué)上的著名標(biāo)準(zhǔn),網(wǎng)上也有許多例子,比如FitzpatrickSkinType.pdf)定義的六種膚色。不同的實(shí)現(xiàn)的精確顏色可能不同。”

          ——Unicode聯(lián)盟的多樣性報(bào)告

          只需要在所需的表情符號后面接上膚色修飾字符 \u{1F466}\u{1F3FE} 即可。

          有創(chuàng)意的變量名和方法名

          示例采用JavaScript(ES6)編寫。

          一般而言,帶有ID_START屬性的字符可以用在變量名開頭,而帶有ID_CONTINUE屬性的字符可以用在變量名中除了首字符之外的其他位置。

          function rand(μ,σ){ ... };

          String.prototype.reverse?=function{..};

          Number.prototype.isTrue?=function{..};

          var WhatDoesThisDo????=42

          下面是Mathias Bynes(https://mathiasbynens.be/notes/javascript-identifiers#examples)提供的一些極富創(chuàng)意的變量名:

          // How convenient!
          var π=Math.PI;

          // Sometimes, you just have to use the Bad Parts of JavaScript:
          var ?_?=eval;

          // Code, Y U NO WORK?!
          var ?_?益?_?=42;

          // How about a JavaScript library for functional programming?
          var λ=function {};

          // Obfuscate boring variable names for great justice
          var \u006C\u006F\u006C\u0077\u0061\u0074='heh';

          // …or just make up random ones
          var ????='huh';

          // While perfectly valid, this doesn’t work in most browsers:
          var foo\u200Cbar=42;

          // This is *not* a bitwise left shift (`<<`):
          var ??=2;
          // This is, though:
          ?? << ??; // 8

          // Give yourself a discount:
          var price_9?9?_89='cheap';

          // Fun with Roman numerals
          var Ⅳ=4;
          var Ⅴ=5;
          Ⅳ + Ⅴ; // 9

          // Cthulhu was here
          var H??????????????????E????????????????_?????????????????????????????O?????????????M????????????????E????????????T?????????????????????????????????='Zalgo';

          下面是David Walsh提供的一些Unicode CSS類名(https://davidwalsh.name/unicode-css-classes):

          <!-- place this within the document head -->
          <meta charset="UTF-8" />

          <!-- error message -->
          <div class="?_?">You do not have access to this page.</div>

          <!-- success message -->
          <div class="?">Your changes have been saved successfully!</div>
          .?_? {
          border: 1px solid #f00;
          }

          .? {
          background: lightgreen;
          }

          遞歸的HTML標(biāo)簽重命名腳本

          如果你想把所有HTML標(biāo)簽重命名,使之看上去像什么都沒有,那么可以使用以下的腳本。

          但要注意,HTML并不會支持所有的Unicode字符。

          // U+1160 HANGUL JUNGSEONG FILLER
          transformAllTags('?');

          // An actual HTML element node designed to look like a comment node, using the U+01C3 LATIN LETTER RETROFLEX CLICK
          // <?-- name="viewport" content="width=device-width"></?-->
          transformAllTags('?--');

          // or even <??
          transformAllTags('\u{1160}\u{20dd}');

          // and for a bonus, all existing tag names will have each character ensquared. h?t?m?l?
          transformAllTags;


          function transformAllTags(newName){
          // querySelectorAll doesn't actually return an array.
          Array.from(document.querySelectorAll('*'))
          .forEach(function(x){
          transformTag(x, newName);
          });
          }

          functionwonky(str){
          return str.split('').join('\u{20de}') + '\u{20de}';
          }

          functiontransformTag(tagIdOrElem, tagType){
          var elem=(tagIdOrElem instanceof HTMLElement) ? tagIdOrElem : document.getElementById(tagIdOrElem);
          if(!elem || !(elem instanceof HTMLElement))return;
          var children=elem.childNodes;
          var parent=elem.parentNode;
          var newNode=document.createElement(tagType||wonky(elem.tagName));
          for(var a=0;a<elem.attributes.length;a++){
          newNode.setAttribute(elem.attributes[a].nodeName, elem.attributes[a].value);
          }
          for(var i=0,clen=children.length;i<clen;i++){
          newNode.appendChild(children[0]); //0...always point to the first non-moved element
          }
          newNode.style.cssText=elem.style.cssText;
          parent.replaceChild(newNode,elem);
          }

          下面是確定能夠支持的字符:

          function testBegin(str){
          try{
          eval(`document.createElement( '${str}' );`)
          return true;
          }
          catch(e){ return false; }
          }

          function testContinue(str){
          try{
          eval(`document.createElement( 'a${str}' );`)
          return true;
          }
          catch(e){ return false; }
          }

          下面是一些基本的結(jié)果:

          // Test if dashes can start an HTML Tag
          > testBegin('-')
          < false

          > testContinue('-')
          < true

          > testBegin('?-') // Prepend dash with U+1160 HANGUL JUNGSEONG FILLER
          < true

          Unicode字體

          單一的TrueType / OpenType 字體格式無法支持所有UTF-8字符,因?yàn)樽煮w文件有最大65535個字形的限制。UTF-8的字形超過了110萬,因此你需要一個font-family才能覆蓋所有字體。

          • https://en.wikipedia.org/wiki/Unicode_font#List_of_Unicode_fonts

          • https://www.unifont.org/fontguide/

          Unicode標(biāo)準(zhǔn)的原則

          Unicode標(biāo)準(zhǔn)設(shè)定了下述基本原則:

          • 通用原則——曾經(jīng)出現(xiàn)過的一切書寫系統(tǒng)都應(yīng)該在標(biāo)準(zhǔn)中體現(xiàn)。

          • 邏輯順序——在雙向文本中,字符以邏輯順序存儲,而不是表現(xiàn)順序存儲。

          • 效率——文檔必須是高效的、完整的。

          • 統(tǒng)一——不同文化或語言使用同一個字符時,應(yīng)該僅存儲一次。

          • 記錄字符而不是字形——應(yīng)當(dāng)對字符進(jìn)行編碼,而不是字形。字形就是字符的實(shí)際圖形表示。

          • 動態(tài)組合——新的字符可以與已有的標(biāo)準(zhǔn)化后的字符進(jìn)行組合。例如,字符“?”可以用字符“A”和分音符“¨”組合而成。

          • 語義——包含的字符必須有明確定義,必須與其他字符有明確的區(qū)別。

          • 穩(wěn)定——字符一旦被定義,就永遠(yuǎn)不能被移除,其代碼點(diǎn)也不能被挪作他用。如果出錯,則應(yīng)該將代碼點(diǎn)標(biāo)記為棄用。

          • 純文本——標(biāo)準(zhǔn)中的字符應(yīng)當(dāng)是純文本,永遠(yuǎn)不應(yīng)該包含標(biāo)記或元字符。

          • 可轉(zhuǎn)換——每一種編碼都應(yīng)該可以用Unicode編碼表示。

          原文:https://wisdom.engineering/awesome-unicode/

          本文為 CSDN 翻譯,轉(zhuǎn)載請注明來源出處。

          【End】

          Python系列學(xué)習(xí)成長課來了!15年經(jīng)驗(yàn)專家、CSDN特級講師親自授課,還等什么?立即掃碼報(bào)名學(xué)習(xí):

          nicode 聯(lián)盟(Unicode Consortium)

          Unicode 聯(lián)盟(Unicode Consortium)開發(fā)了 Unicode 標(biāo)準(zhǔn)(Unicode Standard)。他們的目標(biāo)是使用標(biāo)準(zhǔn)的 Unicode 轉(zhuǎn)換格式(即 UTF,全稱 Unicode Transformation Format)取代現(xiàn)有的字符集。

          Unicode 標(biāo)準(zhǔn)是一個成功的創(chuàng)舉,在 HTML、XML、Java、JavaScript、E-mail、ASP、PHP 中都得到實(shí)現(xiàn)。Unicode 標(biāo)準(zhǔn)也得到許多操作系統(tǒng)和所有現(xiàn)代瀏覽器的支持。

          Unicode 聯(lián)盟與領(lǐng)先的標(biāo)準(zhǔn)開發(fā)組織合作,這些組織有 ISO、W3C 和 ECMA。


          Unicode 字符集

          Unicode 可以由不同的字符集實(shí)現(xiàn)。最常用的編碼是 UTF-8 和 UTF-16:

          字符集描述
          UTF-8UTF8 中的字符可以是 1 到 4 字節(jié)長。UTF-8 可以代表 Unicode 標(biāo)準(zhǔn)中的任何字符。UTF-8 向后兼容 ASCII。UTF-8 是電子郵件和網(wǎng)頁的首選編碼。
          UTF-1616 位 Unicode 轉(zhuǎn)換格式是一種可變長度的 Unicode 字符編碼,能夠編碼整個 Unicode 指令表。UTF-16 主要用于操作系統(tǒng)和環(huán)境,如 Microsoft Windows、Java 和 .NET。

          提示:Unicode 的前 128 個字符(與 ASCII 一一對應(yīng))使用一個與 ASCII二進(jìn)制值相同的八位組進(jìn)行編碼,使有效的 ASCII 文本在進(jìn)行 UTF-8 編碼時也是有效的。

          提示:所有的 HTML 4 處理器支持 UTF-8,所有的 HTML 5 和 XML 處理器支持 UTF-8 和 UTF-16!


          HTML5 標(biāo)準(zhǔn):Unicode UTF-8

          因?yàn)?ISO-8859 中字符集大小是有限的,且在多語言環(huán)境中不兼容,所以 Unicode 聯(lián)盟開發(fā)了 Unicode 標(biāo)準(zhǔn)。

          Unicode 標(biāo)準(zhǔn)覆蓋了(幾乎)所有的字符、標(biāo)點(diǎn)符號和符號。

          Unicode 使文本的處理、存儲和運(yùn)輸,獨(dú)立于平臺和語言。

          HTML-5 中默認(rèn)的字符編碼是 UTF-8。

          下面列出了一些 HTML5 支持的 UTF-8 字符集:

          字符集十進(jìn)制十六進(jìn)制
          C0 控制與基本的 Latin(C0 Controls and Basic Latin)0-1270000-007F
          C1 控制與 Latin-1 的補(bǔ)充(C1 Controls and Latin-1 Supplement)128-2550080-00FF
          Latin 擴(kuò)展 A(Latin Extended-A)256-3830100-017F
          Latin 擴(kuò)展 B(Latin Extended-B)384-5910180-024F

          如果 HTML5 網(wǎng)頁使用不同于 UTF-8 的字符,則需要在 <meta> 標(biāo)簽中指定,如下:

          實(shí)例

          <meta charset="ISO-8859-1">

          如您還有不明白的可以在下面與我留言或是與我探討QQ群308855039,我們一起飛!

          周的時候,朋友圈的直升飛機(jī)不知道為什么就火了,很多朋友開著各種花式飛機(jī)帶著起飛。

          圖片來自網(wǎng)絡(luò)

          還沒來得及了解咋回事來著,這個直升飛機(jī)就到的微博熱搜。

          圖片來自網(wǎng)絡(luò)

          后面越來越多人開來他們的直升飛機(jī),盤旋在朋友圈上方。于是很多朋友開來他們的坦克,專打直升飛機(jī),一轟一個準(zhǔn)。

          圖片來自網(wǎng)絡(luò)

          好了,說回正題!

          程序員朋友應(yīng)該都很熟悉 Unicode (萬國碼),它幾乎包含世界上所有符號,比如組成直升飛機(jī)這幾個特殊符號對應(yīng)的 Unicode 碼分別為:

          ps:推薦一個網(wǎng)站,可以根據(jù)符號搜對應(yīng)的 Unicode 碼:https://unicode.yunser.com/unicode

          除了這些正常字符以外,Unicode 還包含著各種各樣的奇葩字符。

          奇葩字符

          除了正常的我們熟知的文字以外,Unicode 中還有一些奇怪的文字,比如下面這些文字

          這咋讀?某少?

          世代?

          恩?超出認(rèn)知范圍

          除了這些奇怪文字以外,Unicode 還有一些奇葩的的符號。

          例如下面一整套麻將牌:

          一整套的撲克牌:

          一整套國際象棋:

          image-20200725215319183

          除了這些,通過組合符合,我們還可以造出各種各樣的顏文字(??????)??、

          另外 Unicode 還收錄著我們常用的 Emoji

          除了這些之外,Unicode 中還有一些特殊字符的,利用這些字符,我們還可以玩出很多有趣的騷操作。

          組合字符

          Unicode 有一類字符稱為組合字符,它可以附加在前一個非組合字符上,從而使整體看起來像是一個字符。

          組合字符原來目的是為了解決一些地區(qū)語言、文字特殊的需要,比如說泰文聲調(diào)符號與母音符號。

          正常使用的情況下,這些組合字符數(shù)量都會有一些限制。但是在 Unicode 組合字符設(shè)計(jì)上,并沒有加這種限制,這樣使我們可以無限加這類組合字符。

          利用這個特性,可以達(dá)到一些惡搞效果,比如「擊穿天花板」與「鑿穿地板」的效果。

          上面實(shí)現(xiàn)原理其是利用以下兩個組合字符:

          上翻字符

          下翻字符

          只要復(fù)制這兩個字符相應(yīng)的 HTML 代碼,跟在正常的字符后面,就可以使這兩個字符附加在普通字符上,比如下面實(shí)現(xiàn)效果為

          黑??

          Unicode 碼值通常使用 U+N(16 進(jìn)制N 代表碼值),比如 A 的碼值為 U+0041。

          在 HTML 中 Unicode 可以使用 &#N;(十進(jìn)制,N 代表碼值)表示

          在 JS 中 Unicode 中需要使用] \uN(16 進(jìn)制N 代表碼值)表示

          只要我們在普通字符多復(fù)制幾個這類附加字符,就可以形成上述「擊穿」效果。

          還記得上面說的泰文嗎,曾經(jīng)有一段時間貼吧,很流行一種噴射文,比如下面的效果。

          向左噴

          向右噴

          左右互噴

          這種噴射文實(shí)際原理就是利用泰文中聲調(diào)符號附加在其他正常符號上。

          不過現(xiàn)在這個效果貌似已經(jīng)沒辦法再復(fù)現(xiàn)了,現(xiàn)在我們只能看到這樣的效果:

          在一些老版本的系統(tǒng)/瀏覽器可能還能看到這種效果,知道的小伙伴留言區(qū)可以告知一下。

          字符

          Unicode 中還有一類格式字符,不可見,不可打印,主要作用于調(diào)整字符的顯示格式,所以我們將其稱為零寬字符。

          零寬字符主要有以下幾類:

          零寬度空格符 (zero-width space) U+200B : 用于較長單詞的換行分隔

          零寬度非斷空格符 (zero width no-break space) U+FEFF : 用于阻止特定位置的換行分隔

          零寬度連字符 (zero-width joiner) U+200D : 用于阿拉伯文與印度語系等文字中,使不會發(fā)生連字的字符間產(chǎn)生連字效果

          零寬度斷字符 (zero-width non-joiner) U+200C : 用于阿拉伯文,德文,印度語系等文字中,阻止會發(fā)生連字的字符間的連字效果

          左至右符 (left-to-right mark) U+200E : 用于在混合文字方向的多種語言文本中(例:混合左至右書寫的英語與右至左書寫的希伯來語),規(guī)定排版文字書寫方向?yàn)樽笾劣?/p>

          右至左符 (right-to-left mark) U+200F : 用于在混合文字方向的多種語言文本中,規(guī)定排版文字書寫方向?yàn)橛抑磷?/p>

          利用零寬字符不不可見的特性,我們也可以玩出一些騷效果。

          空白微博

          發(fā)布微博的時候,如果內(nèi)容都是空格,將沒辦法發(fā)布。

          但是如果我們將零寬字符,比如說「零寬度空格符 U+200B」復(fù)制到微博,這樣我們就可以發(fā)布空白微博。

          我們可以利用 Chrome 瀏覽器的控制臺復(fù)制零寬字符,操作方式如下:

          發(fā)布效果如下:

          真的沒有改 HTML 導(dǎo)致的.jpg

          隱形水印

          對于一些內(nèi)部論壇或者說小說網(wǎng)站來說,可以通過零寬字符在帖子或小說內(nèi)容嵌入隱形水印。

          當(dāng)這些內(nèi)容被一些爬蟲復(fù)制到其他網(wǎng)站時,我們就可以通過隱形水印,輕松查找時那位用戶泄漏內(nèi)容。

          隱形水印主要原理就是將用戶信息比如用戶名,通過一定算法轉(zhuǎn)成零寬字符,這樣普通用戶瀏覽時完全看不到這個水印。

          如果內(nèi)容被復(fù)制到其他網(wǎng)站,隱形誰贏也被復(fù)制,只要找到這個水印,將這些零寬字符反轉(zhuǎn)成用戶名即可。

          下面展示一種轉(zhuǎn)換方法,JS 代碼主要參考以下 Github 項(xiàng)目:

          https://github.com/umpox/zero-width-detection

          隱形水印生成方法

          第一步我們需要將明文字符串每個字符都轉(zhuǎn)成二進(jìn)制串。

              // 每個字符轉(zhuǎn)為二進(jìn)制,用空格分隔
              const textToBinary = username => (
                username
                .split('')
                // charCodeAt 將字符轉(zhuǎn)成相應(yīng)的 Unicode 碼值
                .map(char => char.charCodeAt(0).toString(2))
                .join(' ')
              );

          示例如下:

          第二步,將二進(jìn)制串轉(zhuǎn)為零度字符串,轉(zhuǎn)換規(guī)則如下:

          • 1 轉(zhuǎn)換為 \u200b 零寬度字符(zero-width space)
          • 0 轉(zhuǎn)換為 \u200c 零寬度斷字符(zero-width non-joiner)
          • 其他(剩余就是空格) 轉(zhuǎn)換為 \u200d 零寬度連字符 (zero-width joiner)
          • 最后使用 \ufeff 零寬度非斷空格符 (zero width no-break space) 作為分隔符
          const binaryToZeroWidth = binary => (
            binary.split('').map((binaryNum) => {
              const num = parseInt(binaryNum, 10);
              if (num === 1) {
                return '\u200b'; // \u200b 零寬度字符(zero-width space)
              } else if(num===0) {
                return '\u200c'; // \u200c 零寬度斷字符(zero-width non-joiner)
              }
              return '\u200d'; // \u200d 零寬度連字符 (zero-width joiner)
          
            }).join('\ufeff') // \ufeff 零寬度非斷空格符 (zero width no-break space)
          );

          最終加密方法如下:

          const encode = username => {
            const binaryUsername = textToBinary(username);
            const zeroWidthUsername = binaryToZeroWidth(binaryUsername);
            return zeroWidthUsername;
          };

          使用加密方法將明文字符串加密之后,加密字符串肉眼是看不到了,但是實(shí)際還是存在的。

          實(shí)際上,如果我們將加密之后字符串復(fù)制到 BEJSON 網(wǎng)站,就可以看到字符。

          image-20200722083507869

          另外你還可以把加密字符串復(fù)制到 IDEA 中,可以看到相應(yīng)的 Unicode 編碼值。

          解密隱形水印

          知道了加密的方式,解密其實(shí)就很簡單,我們只要按照相反步驟的來就可以了。

          第一步,將隱形水印按照以下規(guī)則轉(zhuǎn)換為二進(jìn)制串。轉(zhuǎn)換規(guī)則如下:

          • 使用 \ufeff 分隔字符串
          • \u200b 轉(zhuǎn)為 1
          • \u200c 轉(zhuǎn)為 0
          • 其他字符使用空格
          const zeroWidthToBinary = string => (
            string.split('\ufeff').map((char) => { // \ufeff 零寬度非斷空格符 (zero width no-break space)
              if (char === '\u200b') { // \u200b 零寬度字符(zero-width space)
                return '1';
              } else if(char === '\u200c') { // \u200c 零寬度斷字符(zero-width non-joiner)
                return '0';
              }
              return ' ';
            }).join('')
          );

          調(diào)用該方法,隱形水印轉(zhuǎn)成二進(jìn)制串。

          第二步,將二進(jìn)制再轉(zhuǎn)為相應(yīng)的字符。

          const binaryToText = string => (
            // fromCharCode 二進(jìn)制轉(zhuǎn)化
            string.split(' ').map(num => String.fromCharCode(parseInt(num, 2))).join('')
          );

          最終解密方法如下:

          const decode = zeroWidthUsername => {
            const binaryUsername = zeroWidthToBinary(zeroWidthUsername);
            const textUsername = binaryToText(binaryUsername);
            return textUsername;
          };

          解密示例如下:

          ?

          短網(wǎng)址

          我們常用的短網(wǎng)址,域名后面會跟上一串隨機(jī)串,從而實(shí)現(xiàn)短網(wǎng)址到長網(wǎng)址的映射。比如以下網(wǎng)址:

          https://sourl.cn/iLyn9S

          然而我們可以利用零寬字符也可以實(shí)現(xiàn)短網(wǎng)址的效果,,比如下面這個網(wǎng)站,就可以生成這類短網(wǎng)址。

          https://zws.im/

          可以看到這個短網(wǎng)址后面看不到任何字符,實(shí)際上這后面跟著一串零寬字符。當(dāng)瀏覽器訪問該短網(wǎng)址時,后端程序只要反解密的后面零寬字符,拿到相應(yīng)的網(wǎng)址,然后在做跳轉(zhuǎn)就可以到指定的網(wǎng)站。

          反解密的原理可以參考上面隱形水印的代碼

          小心零寬字符

          日常開發(fā)過程中,我們有時需要從一些文件中讀取文本內(nèi)容,然后做相應(yīng)的處理。

          有時候我們可能會碰到一些詭異的現(xiàn)象,比如我們之前碰到的例子。

          后臺程序從 Excel 讀取文本內(nèi)容,然后程序中判斷是讀取的文本內(nèi)容是否與指定的字符串相等。

          然后當(dāng)我們讀取一份 Excel 內(nèi)容后,返現(xiàn)這段比較邏輯怎么也通過不了。本來以為是 Excel 內(nèi)容存在空格什么的,但是打開 Excel 仔細(xì)一看,跟指定字符串一模一樣,并沒有什么其他字符。

          第一次碰到這種例子,沒有什么經(jīng)驗(yàn),真的排查了很久,到最后都有點(diǎn)懷疑人生了。最后無意間將文本內(nèi)容復(fù)制到了 IDEA 中,才發(fā)現(xiàn)整理混雜著零寬字符!

          如果各位小伙伴也碰到這類問題,不妨將復(fù)制文本內(nèi)容,然后到 IDEA 中查看是否存在某些看不見字符~

          最后(點(diǎn)個贊唄!)

          這兩個星期一直很忙,一直都在 9106 的節(jié)奏,真的是累,所以斷更了一周!

          所幸最近項(xiàng)目提測,稍微輕松了一點(diǎn),能有點(diǎn)劃水時間來寫寫文章。不過再提起筆來寫文章,就有點(diǎn)斷節(jié)奏了!

          這篇文章墨跡了很久才水出來,下周開始再次恢復(fù)周更的節(jié)奏,再忙再累,每周都來一篇。

          歡迎各位小伙伴,每周來這里蹲我,Gank 我!!!

          好了,我是樓下小黑哥,下周見!!!

          參考鏈接

          1. https://juejin.im/post/5d3f01e7f265da03c23ead69
          2. http://zero.rovelast.com/
          3. https://zws.im/
          4. https://imweb.io/topic/5a08a5c7ef79bc941c30d8dd

          主站蜘蛛池模板: 丰满人妻一区二区三区免费视频| 无码播放一区二区三区| 国产成人精品日本亚洲专一区| 日韩久久精品一区二区三区| 99精品久久精品一区二区| 国产在线精品观看一区| 亚洲福利一区二区| 久久精品一区二区| av无码人妻一区二区三区牛牛 | 中文字幕一区二区三区人妻少妇| 亚洲爽爽一区二区三区| 日本一区二区三区在线观看视频 | 久久人做人爽一区二区三区 | 国产主播一区二区三区在线观看| 无码精品人妻一区二区三区漫画 | 精品人妻少妇一区二区三区不卡 | 精品三级AV无码一区| 中文字幕人妻无码一区二区三区 | 制服美女视频一区| 激情无码亚洲一区二区三区| 中文字幕精品一区二区| 日韩一区二区视频| 无码人妻精品一区二区三区99性| 亚洲视频在线一区二区三区| 91福利国产在线观一区二区| 精品一区二区三区免费毛片爱| 一区二区在线观看视频| 少妇精品无码一区二区三区| 久久久无码精品人妻一区| 中文字幕一区视频| 国产主播福利一区二区| 久久精品国内一区二区三区| 欧洲精品免费一区二区三区| 色综合久久一区二区三区| 免费一区二区三区四区五区| 国产99精品一区二区三区免费| 制服中文字幕一区二区| 久久久一区二区三区| 在线观看一区二区三区av| 日韩美一区二区三区| 国产精品免费一区二区三区|