沒想到 Unicode 字符還能這樣玩?

2021-02-14 菜鳥教程

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

圖片來自網絡

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

圖片來自網絡

後面越來越多人開來他們的直升飛機,盤旋在朋友圈上方。於是很多朋友開來他們的坦克,專打直升飛機,一轟一個準。

圖片來自網絡

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

ps:推薦一個網站,可以根據符號搜對應的 Unicode 碼:https://unicode.yunser.com/unicode

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

奇葩字符

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

這咋讀?某少?世代?恩?超出認知範圍

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

例如下面一整套的麻將牌:

三缺一?

一整套的撲克牌:

對三?要不起!

一整套的西洋棋:

不會玩--!

除了這些,通過組合符合,我們還可以造出各種各樣的顏文字(๑•̀ㅂ•́)و✧、

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

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

組合字符

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

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

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

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

上面實現原理其實是利用以下兩個組合字符:

上翻字符下翻字符

只要複製這兩個字符相應的 HTML 代碼,跟在正常的字符後面,就可以使這兩個字符附加在普通字符上,比如下面實現效果為

黑̮̑

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

在 HTML 中 Unicode 可以使用 &#N;(十進位,N 代表碼值)表示。

在 JS 中 Unicode 中需要使用] \uN(16 進位N 代表碼值)表示。

只要我們在普通字符多複製幾個這類附加字符,就可以形成上述「擊穿」效果。

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

往左噴、往右噴、左右互噴

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

不過現在這個效果貌似已經沒辦法再復現了,現在我們只能看到這樣的效果:

在一些老版本的系統/瀏覽器可能還能看到這種效果,知道的小夥伴留言區可以告知一下。

零寬字符

Unicode 中還有一類格式字符,不可見,不可列印,主要作用於調整字符的顯示格式,所以我們將其稱為零寬字符。

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

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

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

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

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

左至右符 (left-to-right mark) U+200E : 用於在混合文字方向的多種語言文本中(例:混合左至右書寫的英語與右至左書寫的希伯來語),規定排版文字書寫方向為左至右。

右至左符 (right-to-left mark) U+200F : 用於在混合文字方向的多種語言文本中,規定排版文字書寫方向為右至左。

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

空白微博

發布微博的時候,如果內容都是空格,將沒辦法發布。

但是如果我們將零寬字符,比如說「零寬度空格符 U+200B」複製到微博,這樣我們就可以發布空白微博。

我們可以利用 Chrome 瀏覽器的控制臺複製零寬字符,操作方式如下:

發布效果如下:

真的沒有改 HTML 導致的.jpg隱形水印

對於一些內部論壇或者說小說網站來說,可以通過零寬字符在帖子或小說內容中嵌入隱形水印。

當這些內容被一些爬蟲複製到其他網站時,我們就可以通過隱形水印,輕鬆查找是哪位用戶洩漏內容。

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

如果內容被複製到其他網站,隱形水印也被複製,只要找到這個水印,將這些零寬字符反轉成用戶名即可。

下面展示一種轉換方法,JS 代碼主要參考以下 Github 項目:

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

隱形水印生成方法

第一步我們需要將明文字符串中每個字符都轉成二進位串。

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

示例如下:

第二步,將二進位串轉為零度字符串,轉換規則如下:

1 轉換為 \u200b 零寬度字符(zero-width space)0 轉換為 \u200c 零寬度斷字符(zero-width non-joiner)其他(剩餘就是空格) 轉換為 \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;
};

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

實際上,如果我們將加密之後字符串複製到 👉BEJSON 網站,就可以看到字符。


另外你還可以把加密字符串複製到 IDEA 中,可以看到相應的 Unicode 編碼值。

解密隱形水印

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

第一步,將隱形水印按照以下規則轉換為二進位串。轉換規則如下:

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('')
);

調用該方法,隱形水印轉成二進位串。

第二步,將二進位再轉為相應的字符。

const binaryToText = string => (
  // fromCharCode 二進位轉化
  string.split(' ').map(num => String.fromCharCode(parseInt(num, 2))).join('')
);

最終解密方法如下:

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

解密示例如下:

短網址

我們常用的短網址,域名後面會跟上一串隨機串,從而實現短網址到長網址的映射。比如以下網址:

https://sourl.cn/iLyn9S

我們利用零寬字符也可以實現短網址的效果,比如下面這個網站,就可以生成這類短網址。

https://zws.im/

可以看到這個短網址後面看不到任何字符,實際上這後面跟著一串零寬字符。當瀏覽器訪問該短網址時,後端程序只要反解密後面的零寬字符,拿到相應的網址,然後再做跳轉就可以到指定的網站。

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

小心零寬字符

日常開發過程中,我們有時需要從一些文件中讀取文本內容,然後做相應的處理。

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

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

然後當我們讀取一份 Excel 內容後,發現這段比較邏輯怎麼也通過不了。本來以為是 Excel 內容存在空格什麼的,但是打開 Excel 仔細一看,跟指定字符串一模一樣,並沒有什麼其他字符。

第一次碰到這種例子,沒有什麼經驗,真的排查了很久,到最後都有點懷疑人生了。最後無意間將文本內容複製到了 IDEA 中,才發現這裡混雜著零寬字符!

如果各位小夥伴也碰到這類問題,不妨複製文本內容,然後到 IDEA 中查看是否存在某些看不見的字符~

參考連結https://juejin.im/post/5d3f01e7f265da03c23ead69http://zero.rovelast.com/https://imweb.io/topic/5a08a5c7ef79bc941c30d8dd

相關焦點

  • Unicode 字符到底能玩出什麼花樣
    圖片來自網絡還沒來得及了解咋回事來著,這個直升飛機就🔥到的微博熱搜。ps:推薦一個網站,可以根據符號搜對應的 Unicode 碼:https://unicode.yunser.com/unicode除了這些正常字符以外,Unicode
  • [亂碼必看]深度長文--聊聊Unicode字符編碼
    從此之後,貪婪的人類再沒有新的狀態可以用了,因為一個字節總共只有256中狀態,也就是說最多只能有256個不同的字符。美帝國主義可能沒有想到還有第三世界國家的人們也希望可以用到計算機吧!等中國人們得到計算機時,已經沒有可以用的字節狀態來表示漢字,況且有6000多個常用漢字需要保存呢。但是這難不倒智慧的中國人民。
  • Emoji.prototype.length —— Unicode 字符那些事兒
    這樣的回覆,以免將一些無聊的笑話信以為真。後來並沒有花多久的時間,大家都明白了,單純靠文字來理解那些幽默與調戲並不那麼容易(但不管怎麼說,這種套路確實應該少一些)。世界上首個 emoji 誕生之後不久,emoji 很快成為文字交流中不可或缺的要素。日用之而不覺,我從未思考過 emoji 在技術層面上是如何工作的。
  • 深入剖析go中字符串的編碼問題——特殊字符的string怎麼轉byte?
    直到實際運行的時候才發現上圖的特殊字符是『』(如果無法展示,記住該特殊字符的unicode是\u0081),並不是英文中的句號。unicode和utf-8的恩怨糾葛百度百科已經把unicode和utf-8介紹的很詳細了,所以這裡就不做過多的闡述,僅摘抄部分和本文相關的定義:Unicode為每個字符設定了統一併且唯一的二進位編碼,通常用兩個字節表示一個字符。
  • Unicode中的一個神奇特性: Emoji.prototype.length
    當我無意中關注到Wes Bos的一個tweet,他分享了一些關於字符串的JavaScript操作,包括表情符號家族,這一切都改變了。好吧 - 使用這樣一個字符串的擴展運算符並沒有讓我興奮,事實上,使我困惑的是這個可見的符號將被分為三個符號以及兩個空字符串。 並且看到字符串屬性長度返回8,更增加了我的迷惑,因為擴展數組中有五個條目,而不是八個。
  • 魔獸世界:六串神秘「字符」,玩了多年魔獸,還是沒搞懂啥意思
    這些「字符」普通玩家根本不知道什麼意思,可能只有暴雪知道這些字符有什麼用意,沒錯今天我們要說的就是這些神秘的字符,話不多說,開始我們今天的BB!隨後又有許多玩家開始尋找,暴雪為什麼要在這把武器上印刻「AL」這樣的字符,最後依舊是眾說紛紜,這串神秘的字符,就成為了這把武器最形象的代表!
  • Py(102)Python/C API 參考手冊:​Unicode Objects and Codecs
    Unicode字符屬性Unicode provides many different character properties.The following format characters are allowed:格式字符類型注釋%%不適用文字%字符。%cint單個字符,表示為C 語言的整型。
  • 沒想到還能看到這樣一個劉詩詩
    打耳光習慣於劉詩詩溫柔嫻靜的形象,沒想到,居然還能在熒幕上看到這麼一個帶感的她。她這部新劇《如果可以這樣愛》,大家有在看嗎?吐槽無良記者:「我可以為我那天的衝動道歉,但你為什麼挨揍自己心裡沒點acd數嗎?」
  • 利用C語言編寫列印彩色字符,還能閃爍!
    escape  sequence code 全稱叫做 escape sequence code,即 Escape 序列屏幕控制碼,其實就是一些特殊的字符,將這些字符加入到printf列印的內容中,即可輕鬆實現在終端軟體中以各種顏色、各種背景色顯示內容,還可以高亮、加粗、閃爍,非常好玩! 2.
  • QQ群幸運字符怎麼抽才能有 群幸運字符在哪獲得方法
    最近很多小夥伴看到了有的QQ群中是有群幸運字符功能的,每天都能抽到不同的字符。那麼QQ群幸運字符在哪搞?QQ群怎麼抽才能有字符呢?下面小編就為大家帶來了相關介紹,還不知道的小夥伴快跟小編一起來看看吧!
  • 電視劇搞笑穿幫鏡頭,沒想到銀行卡還能這樣用,又讓我們長知識了
    電視劇搞笑穿幫鏡頭,沒想到銀行卡還能這樣用,又讓我們長知識了!下面我們一起來看一下吧!先來看看第一張穿幫鏡頭,話說迪麗熱巴和張雲龍的吻戲為什麼會在中間加個這麼明顯的銀行卡呢?難道電視劇中的吻戲都是這樣拍出來的嗎?有網友看了說:沒想到銀行卡還能這樣用,又讓我們長知識了!
  • 二戰策略手遊《我的戰爭》居然還能這樣玩
    今天小編就收集了一些最近遊戲中出現的趣事和囧事,你肯定也沒想到一款戰爭題材的手遊,居然還能這樣玩。1. 戰爭遊戲裡也能……虐狗?但沒想到,《我的戰爭》裡居然也能看到這樣的畫面。不過根據此前官方透露的消息,由於卡通化的美術風格,《我的戰爭》裡有不少的女玩家,借這樣一個平臺表白,倒也算是別出心裁。2. 囧,就差一槍!
  • 「卍」字符到底隱藏著什麼秘密?
    :「卍字符,不知道……代表永生吧。」如果是這樣,那麼它顯然超出了一般宗教對它的認識,更不應該是納粹或反猶太的象徵。我們應該如何看待這個神秘的符號?它是否深藏著什麼人類迄今還未知的奧秘呢?有的經書提到在佛的頭髮、腰間,甚至手、足上都有這樣的標記,所以佛教中的卍字代表了佛陀。當然,它也被廣泛運用到佛教儀式和廟宇的裝飾上。然而,卍字符卻不是佛教特有的標誌。在印度,卍字符也是印度教和耆那教廣泛使用的符號。
  • 讓iPhone手機崩潰的神秘字符又出現了!
    讓iPhone手機崩潰的神秘字符又出現了!關注科技新聞的差友們應該知道,根據推特帳號EverythingApplePro的爆料,iPhone又又又被爆出一個能夠讓系統奔潰的字符。這次的神秘字符是由義大利國旗Emoji和一串信德文(南亞巴基斯坦信德人語言)組成的。
  • Java 字符編碼
    計算機要準確的處理各種字符集文字,就需要進行字符編碼,以便計算機能夠識別和存儲各種文字。字符編碼字符編碼(英語:Character encoding)也稱字集碼,是把字符集中的字符編碼為指定集合中某一對象(例如:比特模式、自然數序列、8 位組或者電脈衝),以便文本在計算機中存儲和通過通信網絡的傳遞。
  • 「沒想到吧!|還能這樣拍ootd 2.0」
    她到達這個城市的原因就是來調查這起事件,這也是我們的一點小心思~當時想到要拍攝「案發現場」的時候,我就想要比較復古的場景,所以在決定要穿什麼的時候,當下就覺得Gucci的橙黃色襯衫非常合適。眼尖的朋友也發現,就是那件掉披薩的Gucci襯衫沒錯!