成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

看破字符 %20 之謎,百分號編碼以及其背后

開發 前端
提到這個 %20,想必大家都見過,熟悉一點編碼的人,還會知道這玩意就是空格轉換而來!那么我們一起破解, 如何編碼而來?

[[439153]]

前言

提到這個 %20,想必大家都見過,熟悉一點編碼的人,還會知道這玩意就是空格轉換而來!那么我們一起破解, 如何編碼而來?

我們今天繼續學習前端編碼知識, 其他編碼文章:

  • 前端Base64編碼知識,一文打盡,探索起源,追求真相
  • localStorage靈魂五問。 5M空間?? 10M !!!
  • 字母a的6種表示方法,以及其背后的編碼知識

之后再補上

  • UTF-16 編碼
  • UTF-8 編碼

前端所需要的基本編碼知識體系就基本形成。

Unicode基礎知識

Unicode 只是一個字符集, 其為每個字符提供了一個編號,我們稱之為碼點。

Unicode 可以使用的編碼有三種,分別是:

UFT-8:一種 變長的編碼方案,使用 1~6 個字節來存儲。

UTF-16:對于碼點小于0xFFFF(65535)的字符,兩個字節存儲,反之采用 4個字節來存儲。

UFT-32:一種 固定長度的編碼方案,不管字符編號大小,始終使用 4 個字節來存儲。

所以UTF-8個UTF-16都屬于變長編碼方案,而UTF-32屬于固定長度編碼方案。

固定長度編碼方案優點當然是簡單啊,缺點嘛,費空間, 這就是為嘛還要有UTF-16和UTF-8。

我們網絡傳輸常用 UTF-8, 而javascript運行時的字符編碼是 UTF-16.

%20怎么來的

我們看看,我們怎么樣可以得到這個%20:

  1. escape(" ""%20" 
  2.  
  3. encodeURI(" ""%20" 
  4.  
  5. encodeURIComponent(" ""%20" 

其是字符的16進制格式值, 是百分號編碼,之后會細說。

怎么獲得這個編碼,寫一個簡單的方法你就懂了

  1. function to16Format(ch){ 
  2.  
  3. return '%' + ch.codePointAt(0).toString(16) 
  4.  
  5.  
  6. to16Format(" ") // "%20" 

雖然3個方法都能獲得同樣的值,很少有人告訴你 esacpe是基于UTF-16,而另外兩個是基于 UTF-8, 看個例子:

0-0xFF碼點范圍編碼結果是一致的,

0xFF以上,結果就不一樣了, 原理我們后面說。

  1. escape("")         //%20 
  2. encodeURI("")      //%20 
  3.  
  4. escape("人")       // "%u4EBA"  
  5. encodeURI("人")    // "%E4%BA%BA" 
  6.  
  7. escape("𣑕")       //  %uD84D%uDC55 
  8. encodeURI("𣑕")    //  "%F0%A3%91%95" 

小結一下:

  • escape,encodeURI和encodeURIComponent 對空格編碼 " "均能得到 20%
  • escape進行的是UTF-16編碼,后兩者是UTF-8編碼,只是碼點0xFF以下的編碼結果一致罷了

當然,不是所有的字符都會被編碼,接下來一起看哪些字符不會被編碼。

哪些字符不會被編碼

%20, 就不得不提到我們的常用編碼的三對姊妹:

  • escape (unescape) 已過時
  • encodeURI (decodeURI)
  • encodeURIComponent (decodeURIComponent)

我們先把A-Z a-z 0-9單獨列出來,因為都是不會被編碼的, 看看哪些字符不會被編碼。

系列 保留字符 編碼
escape @ * _ + - . / UTF-16
encodeURI - _ . ! ~ * ' ( ) ; , / ? : @ & = + $ # UTF-8
encodeURIComponent - _ . ! ~ * ' ( ) UTF-8

編碼之 escape

簡單來說,escape是生成新的由十六進制轉義序列替換的字符串,作用是讓它們在所有電腦上可讀。

編碼之后的效果是%XX或者%uXXXX這種形式。

當你需要對URL編碼時,請使用 encodeURI 或者 encodeURIComponent。

劃重點:基于UTF-16進行編碼

UTF-16字符編碼,對于碼點大于0xFFFF的字符,其編碼結果是分高低位的, charCodeAt(0)可以獲得高位, charCodeAt(1)可以獲得低位。

escape之碼點大于0xFFFF的字符

轉義為兩個%uXXXX

先直接看代碼結果:

  1. var ch = String.fromCodePoint(0x23455);  // "𣑕" 
  2. escape(ch)  // '%uD84D%uDC55'  碼點大于 0xFFFF 
  3. unescape(escape(ch)) // "𣑕" 
  4.  
  5. ch.charCodeAt(0).toString(16).toUpperCase();  // 高位 
  6. // 'D84D' 
  7. ch.charCodeAt(1).toString(16).toUpperCase();  // 低位 
  8. // 'DC55' 

看著結論就知道了,和charCodeAt的邏輯處理一致。都是返回UTF-16編碼的高低位編碼。

編碼之 encodeURI

由于 URL 只能由標準 ASCII 字符組成,因此必須對其他特殊字符進行編碼。它們將被代表 utf-8編碼的一系列不同字符所取代。encodeURI 和 encodeURIComponent 用于此目的。

劃重點,encodeURI 和 encodeURIComponent 采用的是UTF-8編碼。

先看看碼點和UTF-8編碼格式,以及需要的字節數。

Unicode 碼點范圍(十六進制) 十進制范圍 UTF-8 編碼方式(二進制) 字節數
0000 0000 ~ 0000 007F 0 ~ 127 0xxxxxxx 1
0000 0080 ~ 0000 07FF 128 ~ 2047 110xxxxx 10xxxxxx 2
0000 0800 ~ 0000 FFFF 2048 ~ 65535 1110xxxx 10xxxxxx 10xxxxxx 3
0001 0000 ~ 0010 FFFF 65536 ~ 1114111 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 4

我們先看看 人 字:

  • 獲取其碼點 4eba
  1. var codePoint = "人".codePointAt(0).toString(16) // `4eba` 
  • 其位于 0000 0800 ~ 0000 FFFF, 格式為1110xxxx 10xxxxxx 10xxxxxx, 需要三個字節
  • encodeURI, 可以看到是三個 %XX
  1. encodeURI("人") // %E4%BA%BA 

這里我們省略了具體的編碼過程, 具體的編碼結果驗證可以去 Convert UTF8 to Binary Bits - Online UTF8 Tools[5] 驗證

最終編碼結果: 11100100 10111010 10111010

  1. (0b11100100).toString(16).toUpperCase() // E4 
  2.  
  3. (0b10111010).toString(16).toUpperCase() // BA 
  4.  
  5. (0b10111010).toString(16).toUpperCase() // BA 
  6.  
  7. encodeURI("人") // %E4%BA%BA => E4 BA BA 

再推導一下??字

  • 碼點是 0x23455
  • 0001 0000 ~ 0010 FFFF之間,格式為 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx, 需四個字節
  • encodeURI, 其由四個%XX組成
  1. encodeURI("??") // "%F0%A3%91%95" 

編碼之 encodeURIComponent

既然有encodeURI為嘛還要來一個encodeURIComponent呢?

其用于對地址后的 參數值進行編碼, 我們通常稱呼為queryString。

看個例子:

  1. var param = "http://www.yyy.com"; //param為參數 
  2.  
  3. param = encodeURIComponent(param); 
  4.  
  5. var url = "http://www.xxxx.com?target=" + param; 

同理下面的?之后的部分空 鍵=啊 哈&type=x,鍵值對均需要encodeURIComponent進行編碼。

  1. http://wwww.xxxyyy.com/哈 哈?空 鍵=啊 哈&type=x 

其實吧,現代瀏覽器,默認都會自行進行編碼,你不妨把上面的地址貼到瀏覽器:

image.png

application/x-www-form-urlencoded

對于 `application/x-www-form-urlencoded`[6] (POST) 這種數據方式, 也是需要編碼的。

其編碼規則:

數據被編碼成以 '&' 分隔的鍵-值對, 同時以 '=' 分隔鍵和值.

非字母或數字的字符會被 percent-encoding[7]

我們先一起看看 percent-encoding(百分號編碼)。

percent-encoding

百分比編碼(也有叫百分號編碼的) 是一種擁有8位字符編碼的編碼機制,這些編碼在URL[8]的上下文中具有特定的含義。它有時被稱為URL編碼。編碼由英文字母替換組成:“%” 后跟替換字符的ASCII的十六進制表示。

它廣泛地應用于主統一資源標志符/統一資源定位符集(URI) ,其中包括 URL 和統一資源名(URN)。它還用于準備應用 application/x-www-form-urlencoded 媒體類型的數據,這通常用于在 HTTP 請求中提交 HTML 表單數據。

URI所允許的字符分作保留與未保留。保留字符是那些具有特殊含義的字符,例如:斜線[9]字符用于URL(或URI)不同部分的分界符;未保留字符沒有這些特殊含義。百分號編碼把保留字符表示為特殊字符序列。

保留字符

保留字符需要編碼,其有:':','/','?','#','[',']','@','!','$','&',"'",'(',')','*','+',',',';','=',以及,'%' 本身, 以及一個空格 " "。

percent-encoding編碼對照表請參見:percent-encoding | MDN [10]

非保留字符

不需要被編碼,直接使用就行。

  • A-Z
  • a-z
  • 0-9
  • - _ . ~

特殊的字符 " ",

其在作為URL的時候,編碼是轉為 %20

post提交(application/x-www-form-urlencoded)替換為 +

那么,我們這里直接使用 encodeURLComponent編碼值和鍵,能行嗎?

答案是不行:

百分比編碼需要編碼特殊字符的是 20個(加上 ' ')

  1. : / ? # [ ] @ ! $ & ' ( ) * + , ; = % 

encodeURLComponent不編碼的字符是 9 個:

  1. - _ . ! ~ * ' ( ) 

所以還需要額外編碼為:['!', "'", '(', ')', '*'], 怎么計算而得,參見下面代碼:

  1. var percentChars = [':''/''?''#''['']''@''!''$''&'"'", '(', ')', '*', '+', ',', ';', '=', '%', ' ']; 
  2. var eURICChars =   ['-''_''.''!''~''*'"'", '(', ')']; 
  3.  
  4. var notInPChars = percentChars.filter(c=> eURICChars.includes(c)); 
  5.  
  6. console.log("notInPChars:", notInPChars); 
  7. // notInPChars: (5) ['!'"'", '(', ')', '*'] 

所以,完整的編碼應該如下:

  1. function encodeValue(val) 
  2.    var eVal = encodeURIComponent(val); 
  3.   
  4.    // 單獨處理encodeURIComponent不編碼的字符 
  5.    eVal = eVal.replace(/\*/g, '%2A'); 
  6.    eVal = eVal.replace(/!/g, '%21'); 
  7.    eVal = eVal.replace(/\(/g, '%28'); 
  8.    eVal = eVal.replace(/\)/g, '%29'); 
  9.    eVal = eVal.replace(/'/g, '%27'); 
  10.   
  11.    // 特殊處理空格字符 
  12.    return eVal.replace(/\%20/g,'+'); 

Content-Disposition: attachment; filename

我們后臺返回文件的時候,如果指定Content-Disposition: attachment并設定好filename, 客戶端收到請求后是可以直接進行文件下載的。 問題就在于這個filename,其也是需要被編碼的,我們了解一下即可:

參考MDN:

  1. var fileName = 'my file(2).txt'
  2. var header = "Content-Disposition: attachment; filename*=UTF-8''" 
  3.              + encodeRFC5987ValueChars(fileName); 
  4.  
  5. console.log(header); 
  6. // 輸出 "Content-Disposition: attachment; filename*=UTF-8''my%20file%282%29.txt" 
  7.  
  8.  
  9. function encodeRFC5987ValueChars (str) { 
  10.     return encodeURIComponent(str). 
  11.         // 注意,盡管 RFC3986 保留 "!",但 RFC5987 并沒有 
  12.         // 所以我們并不需要過濾它 
  13.         replace(/['()]/g, escape). // i.e., %27 %28 %29 
  14.         replace(/\*/g, '%2A'). 
  15.             // 下面的并不是 RFC5987 中 URI 編碼必須的 
  16.             // 所以對于 |`^ 這3個字符我們可以稍稍提高一點可讀性 
  17.             replace(/%(?:7C|60|5E)/g, unescape); 

其比 percent-encoding又還有些區別,注釋里面寫得很清楚。我真想說,搞那么多協議不累嗎?

看到注冊,我們可以看到 RFC3986, RFC5987等協議,我們一起了解一下。

RFC3986[11] ,RFC1738[12] ,RFC5987[13]

RFC3986, RFC1738是關于URI的編碼規范,RFC5987是關于http協議文件頭字段的規范。

  • RFC3986[14]

2005年發布,現行標準。文檔對URL的編解碼問題做出了詳細的建議,指出了哪些字符需要被編碼才不會引起Url語義的轉變,以及對為什么這些字符需要編碼做出了相應的解釋

  • RFC 1738[15]

94年發布。同上。

  • RFC5987[16]

Character Set and Language Encoding for Hypertext Transfer Protocol (HTTP) Header Field Parameters。 翻譯:超文本傳輸協議文件頭字段參數的字符集和語言編碼, 對http傳輸頭部字符串編碼的規范。

你會發現很多代碼還會處理~符號,雖然RFC3986文檔規定,對于波浪符號~,不需要進行Url編碼,但是還是有很多老的網關或者傳輸代理。

兼容性好的代碼,會兼容處理 RFC1738, 比如著名的qs庫的 formats.js[17]

image.png

window.btoa 和window.atob

window.btoa可以進字符進行base64編碼, window.atob可以解碼。

  1. window.btoa("abcd") // "YWJjZA==" 
  2.  
  3. window.atob("YWJjZA==") // "abcd" 

但是其職能編碼ASCII 字符串, 試試中文:

  1. window.btoa("人"
  2.  
  3. // Uncaught DOMException: Failed to execute 'btoa' on 'Window'
  4.  
  5. // The string to be encoded contains characters outside of the Latin1 range. 

怎么解決呢?

  1. // ucs-2 string to base64 encoded ascii 
  2. function utoa(str) { 
  3.     return window.btoa(unescape(encodeURIComponent(str))); 
  4. // base64 encoded ascii to ucs-2 string 
  5. function atou(str) { 
  6.     return decodeURIComponent(escape(window.atob(str))); 

驗證一下, 完美。

  1. utoa("人")     //5Lq6 
  2. atou("5Lq6")   //人 

那么這是什么思路呢???

encodeURIComponent 將字符轉為百分比utf-8字節存儲為% XX 之后,unescape 將它們轉換為 btoa 所要求的單個代碼點。因此,btoa (unescape (encodeURIComponent (str)))都將文本編碼為 utf-8字節,然后將其編碼為 Base64。

雖然,你去掉中間的unescape和escape也可以正常使用,但是必須搭配使用啦。但是,已經不是標準的utf-8轉為Base64了。

自己玩:

  1. window.btoa(encodeURIComponent("我是人a"))  
  2. // JUU2JTg4JTkxJUU2JTk4JUFGJUU0JUJBJUJBYQ== 
  3. decodeURIComponent(window.atob("JUU2JTg4JTkxJUU2JTk4JUFGJUU0JUJBJUJBYQ==")) 
  4. // 我是人a 

標準base解碼,已經得不到正確結果:

總結

  • %20 是escape或者URL編碼得到的結果,對應著空字符 " "。也可是說是百分號編碼。
  • escape是把字符串轉為十六進制轉義序列,作用是讓它們在所有電腦上可讀。已過時,現在也沒啥用。
  • encodeURI 是URL編碼,不處理參數部分
  • encodeURIComponent 也是URL編碼 主要用于
    • url的參數部分
    • post 數據類型為application/x-www-form-urlencoded
    • 附件文件名 filename
  • RFC3986[18] ,RFC1738[19] 是URL編碼協議
  • RFC5987[20] 是http傳輸頭部字符串編碼的規范
  • window.btoa 和window.atob 默認只能處理ASCII碼字符,在encodeURIComponent和escape的配合下,可以處理任意字符。

最后提一個問題:

 

百分號編碼 和 escape, encodeURI, encodeURIComponent是什么關系?

 

責任編輯:武曉燕 來源: 云的程序世界
相關推薦

2009-12-17 15:11:47

Ruby百分號表示法

2009-09-11 10:51:33

C#保留小數位

2011-04-06 10:57:11

Cacti監控

2011-03-31 16:16:43

Cacti監控

2016-09-22 09:12:45

Windows 10優化Cortana

2021-05-20 07:58:02

Appium環境搭建Windows10

2021-09-05 11:20:04

帶寬網絡排查

2016-08-12 16:20:30

2011-03-22 17:01:10

百分通聯未來之星

2021-04-26 05:45:43

字符串制表符PyCharm

2011-06-22 15:54:47

2014-06-16 14:14:45

wifi

2024-05-11 08:11:19

CSS百分比開發

2017-03-16 09:10:41

魚缸式信息圖表計算

2021-07-08 06:47:21

Python

2013-11-06 14:49:38

百分之百百度

2010-09-10 11:02:35

敏捷宣言

2017-01-19 07:59:17

實名制手機實名制電話實名制

2025-05-12 04:20:00

Linux系統epoll

2022-09-09 08:32:14

SQLMySQL數據庫
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 免费a v网站 | 日韩在线欧美 | 成人一区二区三区在线 | 色资源av| 精品综合 | 黄色大片免费网站 | 久久久精品一区 | 欧美精品在线一区二区三区 | 国产一区中文字幕 | 精品国产乱码一区二区三区 | 亚洲国产日韩一区 | 国产成人精品久久二区二区 | 亚洲精品一区二区网址 | 日本黄色不卡视频 | 午夜理伦三级理论三级在线观看 | 精品综合视频 | 国产黄色在线观看 | 国产亚洲精品综合一区 | 国产精品久久久久久久免费大片 | 午夜av电影 | 国产精品福利网站 | 久艹网站 | 国产精品亚洲成在人线 | a在线视频观看 | 懂色一区二区三区免费观看 | 都市激情亚洲 | 久久一级 | 黑人精品欧美一区二区蜜桃 | 亚洲一区二区免费视频 | 亚洲综合免费 | 国产在线精品一区二区三区 | 欧美久久久久久久 | 韩国欧洲一级毛片 | 男人天堂免费在线 | 黄色三级在线播放 | 亚洲一区二区免费 | 日韩不卡一区二区 | 久久久久久91 | 亚洲精品一区二区三区蜜桃久 | 国产成人综合在线 | 操操日 |