小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

一次性搞清楚unicode、codepoint、代碼點、UTF

 蘭亭文藝 2019-12-13

最近在處理字符過濾,重新研究了下字符、unicode和代碼點的相關知識,首先要說一下編碼的基本知識unicode

unicode

unicode是計算機科學領域里的一項業(yè)界標準,包括字符集、編碼方案等。計算機采用八比特一個字節(jié),一個字節(jié)最大整數(shù)是255,還要表示中文一個字也是不夠的,至少需要兩個字節(jié),為了統(tǒng)一所有的文字編碼,unicode為每種語言中的每個字符設定了統(tǒng)一并且唯一的二進制編碼,通常用兩個字節(jié)表示一個字符,所以unicode每個平面可以組合出65535種不同的字符,一共17個平面。

由于英文符號只需要用到低8位,所以其高8位永遠是0,因此保存英文文本時會多浪費一倍的空間。

比如漢子“漢”的unicode,在java中輸出

System.out.println('\u5B57');

UTF-8

unicode在計算機中如何存儲呢,就是用unicode字符集轉換格式,即我們常見的UTF-8、UTF-16等。

UTF-8就是以字節(jié)為單位對unicode進行編碼,對不同范圍的字符使用不同長度的編碼。

Unicode Utf-8
000000-00007F 0xxxxxxx
000080-0007FF 110xxxxx 10xxxxxx
000800-00FFFF 1110xxxx 10xxxxxx 10xxxxxx
010000-10FFFF 11110xxx10xxxxxx10xxxxxx10xxxxxx

Java中的String對象就是一個unicode編碼的字符串。

java中想知道一個字符的unicode編碼我們可以通過Integer.toHexString()方法

  1. String str = '編';
  2. StringBuffer sb = new StringBuffer();
  3. char [] source_char = str.toCharArray();
  4. String unicode = null;
  5. for (int i=0;i<source_char.length;i++) {
  6. unicode = Integer.toHexString(source_char[i]);
  7. if (unicode.length() <= 2) {
  8. unicode = '00' + unicode;
  9. }
  10. sb.append('\\u' + unicode);
  11. }
  12. System.out.println(sb);
  13. 輸出\u7f16

對應的utf-8編碼是什么呢?

7f16在0800-FFFF之間,所以要用3字節(jié)模板:1110xxxx 10xxxxxx 10xxxxxx。
7f16寫成二進制是:0111 1111 0001 0110
按三字節(jié)模板分段方法分為0111 111100 010110,代替模板中的x,得到11100111 10111100 10010110,即“編”對應的utf-8的編碼是e7 bc 96,占3個字節(jié)

codepoint

unicode的范圍從000000 - 10FFFF,char的范圍只能是在\u0000到\uffff,也就是標準的 2 字節(jié)形式通常稱作 UCS-2,在Java中,char類型用UTF-16編碼描述一個代碼單元,但unicode大于0x10000的部分如何用char表示呢,比如一些emoji:?

java的char類型占兩個字節(jié),想要表示?這個表情就需要2個char,看如下代碼

  1. String testCode = 'ab\uD83D\uDE03cd';
  2. int length = testCode.length();
  3. int count = testCode.codePointCount(0, testCode.length());
  4. //length=6
  5. //count=5

第三個和第四個字符合起來代表?,是一個代碼點,
如果我們想取到每個代碼點做一些判斷可以這么寫

  1. String testCode = 'ab\uD83D\uDE03cd';
  2. int cpCount = testCode.codePointCount(0, testCode.length());
  3. for(int index = 0; index < cpCount; ++index) {
  4. //這里的i是字符的位置
  5. int i = testCode.offsetByCodePoints(0, index);
  6. int codepoint = testCode.codePointAt(i);
  7. }
  8. //輸出
  9. i:0 index: 0 codePoint: 97
  10. i:1 index: 1 codePoint: 98
  11. i:2 index: 2 codePoint: 128515
  12. i:4 index: 3 codePoint: 99
  13. i:5 index: 4 codePoint: 100

也就是按照codePointindex取字符,0取到a,1取到b,2取到\uD83D\uDE03也就是?,3取到c,4取到d;
按照String的index取字符,0取到a,1取到b,2取到\uD83D,3取到\uDE03,4取到c,5取到d。
這就是codePointIndex和char的index的區(qū)別。

取到codePoint就可以按照unicode值進行字符的過濾等操作。

如果有個需求是既可以按照unicode值過濾字符,也能按照正則表達式過濾字符,并且還有白名單,應該如何實現(xiàn)呢。

其實unicode過濾和正則表達式過濾并不沖突,自己實現(xiàn)自己的過濾就好了,如果需求加入了過濾白名單就會復雜一些,不能直接過濾,需要先檢驗是否是白名單的index。

我的思路是記錄白名單char的index,正則表達式或其他過濾方式可以獲得違規(guī)char的index,unicode黑名單的codepointIndex可以轉換成char的index,在獲取codePont的index時可以判斷當前字符是單char字符還是雙char字符,雙char字符需要添加2個下標,方法如下

  1. //取到unicode值
  2. int codepoint = testCode.codePointAt(i);
  3. //將unicode值轉換成char數(shù)組
  4. char[] chars = Character.toChars(codepoint);
  5. charIndexs.add(pointIndex);
  6. if (chars.length > 1) {
  7. //表示不是單char字符,記錄index時同時添加i+1
  8. charIndexs.add(pointIndex + 1);
  9. }

//例
String str = 'ab\uD83D\uDE03漢字';
想處理emoji,那記錄的下標就是2、3,最后和白名單下標比較后統(tǒng)一刪除

如何區(qū)別char是一對還是單個

就之前的例子ab\uD83D\uDE03cd,換種寫法\u0061\u0062\uD83D\uDE0\u0063\u0064
程序是如何將\uD83D\uDE03解析成一個字符的呢。這就需要Surrogate這個概念,來自UTF-16。

UTF-16是16bit最多編碼65536,那大于65536如何編碼?Unicode 標準制定組想出的辦法是,從這65536個編碼里,拿出2048個,規(guī)定他們是「Surrogates」,讓他們兩個為一組,來代表編號大于65536的那些字符。
編號為 U+D800 至 U+DBFF 的規(guī)定為「High Surrogates」,共1024個。
編號為 U+DC00 至 U+DFFF 的規(guī)定為「Low Surrogates」,也是1024個。
他們組合出現(xiàn),就又可以多表示1048576中字符。

看一下String.codePointAt這個方法,

  1. static int codePointAtImpl(char[] a, int index, int limit) {
  2. char c1 = a[index];
  3. if (isHighSurrogate(c1) && ++index < limit) {
  4. char c2 = a[index];
  5. if (isLowSurrogate(c2)) {
  6. return toCodePoint(c1, c2);
  7. }
  8. }
  9. return c1;
  10. }

其中有兩個方法isHighSurrogate、isLowSurrogate。
第一個方法判斷是否為高代理項代碼單元,即在'\uD800'與'\uDBFF'之間,
第二個方法判斷是否為低代理項代碼單元,即在'\uDC00'與'\uDFFF'之間。

codePointAtImpl方法判斷當前char是高代理項代碼單元,下一個是低代理項代碼單元,則這兩個char是一個codepoint。

再來看一下unicode轉UTF-16的方法

如果U<0x10000,U的UTF-16編碼就是U對應的16位無符號整數(shù)(為書寫簡便,下文將16位無符號整數(shù)記作WORD)。
如果U≥0x10000,我們先計算U'=U-0x10000,然后將U'寫成二進制形式:yyyy yyyy yyxx xxxx xxxx,U的UTF-16編碼(二進制)就是:110110yyyyyyyyyy 110111xxxxxxxxxx。

還是以U+1F603這個?為例子,U'=U-0x10000=F603
寫成2進制就是1111011000000011,不足20位前面補0,
變成0000111101-1000000011,替換y和x就是1101100000111101,1101111000000011,最后UTF-16編碼就是[d83d,de03] 和上面一樣。

    本站是提供個人知識管理的網(wǎng)絡存儲空間,所有內容均由用戶發(fā)布,不代表本站觀點。請注意甄別內容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權內容,請點擊一鍵舉報。
    轉藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多