zoukankan      html  css  js  c++  java
  • 关于encodeURIComponent编码非UTF-8字符时出现的怪异情况

    现在有个test.html文件,这个文件的编码是UTF-8,其中“你好”的UTF-8编码是:E4 BD A0 E5 A5 BD,文件代码如下:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <script>
    	console.log(encodeURIComponent("你好")); //%E4%BD%A0%E5%A5%BD
    </script>
    </head>
    <body>
    	<p>hello</p>
    </body>
    </html>
    

    可以看见,enencodeURIComponent就是将“你好”以UTF-8编码输出(这也正是encodeURIComponent方法的定义:将非URI字符都以UTF-8编码的格式输出为字符串),现在来看一个怪异的情况,还是上面的test.html(所以文件的编码依旧是UTF-8),只不过这次手动把charset改为了GBK编码,用来误导encodeURIComponent方法,代码如下:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="GBK">
    <script>
    	console.log(encodeURIComponent("你好")); //%E6%B5%A3%E7%8A%B2%E3%82%BD
    </script>
    </head>
    <body>
    	<p>hello</p>
    </body>
    </html>
    

    什么,输出的是什么鬼!不着急,我们慢慢分析:
    encodeURIComponent认为需要encode的字符的编码是charset指定的编码,这里就是GBK,而encodeURIComponent需要的是UTF-8编码的字符,这样它才会进行encode,所以必须发生编码之间的转换,具体如下,
    “你好”的UTF-8:E4 BD A0 E5 A5 BD
    所以“你好”实际上是以上面的6字节存储在文件test.html里面(因为文件的实际编码是UTF-8)
    但是charset指定的GBK是2字节编码(除128个ASCII外,都是2字节编码),所以它把E4 BD认为是1个GBK字符,尝试把它转为UTF-8,其中E4 BD在GBK中对应的是“浣”字,而这个字在UTF-8则是E6 B5 A3
    由此问题解决!

    结论:如果网页文件的文件编码是UTF-8,而charset不小心指定成了其他的编码,那么会发生编码转换,第一次是将原本以UTF-8编码保存的字符当作charset指定的字符来读取,然后再把它转为UTF-8编码。

    再来看一个更加离奇的情况,现在有个test2.html文件,它的文件编码是GBK,代码如下:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <script>
    	console.log(encodeURIComponent("你")); //%EF%BF%BD%EF%BF%BD
    	console.log(encodeURIComponent("你你")); //%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD
    	console.log(encodeURIComponent("你你你")); //%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD%EF%BF%BD
    </script>
    </head>
    <body>
    	<p>hello</p>
    </body>
    </html>

    “你”的GBK编码是C4E3,UTF-8的EFBFDB表示这些字节信息无法转为UTF-8对应的字符,现在我们来分析这个异常情况出现的本质原因:

    C4的二进制是1100 0100,查看UTF-8编码格式转换表(百度百科的UTF-8词条内就有这个表格),确实存在以110开头的格式,以110开头的字节会和它的下个字节组合为一个字符,而下个E3的二进制是11100 011,而UTF-8第二个字节开始都是10开始,显然这时不匹配了,也就是说C4E3这个编码是非法的UTF-8编码,那么就会返回EFBFDB,由此问题解决。

     对于文件本身是GBK编码(或者其他非UTF-8编码),且charset指定的编码不是文件实际的编码,那么这个方法会出现很多意料之外的行为,再比如,如下代码:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <script>
    	console.log(encodeURIComponent("小")); //%D0%A1
    	console.log(encodeURIComponent("小小")); //%D0%A1%D0%A1
    	console.log(encodeURIComponent("小小小")); //%D0%A1%D0%A1%D0%A1
    </script>
    </head>
    <body>
    	<p>hello</p>
    </body>
    </html>
    

    “小”的GBK编码是D0A1,同理,D0的二进制是1101 0000,也存在以110开头的UTF-8编码,下个A1的二进制是1010 0001,和上个“你”字不同,这个确实是符合UTF-8第二字节均以10开头的标准,那么这个字符是可以转为UTF-8的,由此可以正常输出。

    最后再来看一个案例,文件依旧是GBK编码,代码如下:

    <!DOCTYPE html>
    <html>
    <head>
    <meta charset="UTF-8">
    <script>
    	console.log(encodeURIComponent("高")); //%EF%BF%BD%EF%BF%BD
    	console.log(encodeURIComponent("高高")); //%EF%BF%BD%DF%B8%EF%BF%BD
    	console.log(encodeURIComponent("高高高")); //%EF%BF%BD%DF%B8%DF%B8%EF%BF%BD
    </script>
    </head>
    <body>
    	<p>hello</p>
    </body>
    </html>
    

    “高”的GBK编码是B8DF,而B8的二进制是1011 1000,呀,这个字节对于不了任何UTF-8起始字节,随即返回EFBFBD编码,示意这个字符无法对应(转换)为UTF-8编码,继续看下个DF的二进制,它是1101 1111,110可以对应,而UTF-8中起始字节是110会和它的下个字节组合为一个字符,对于只有一个“高”来说,没有下个字符了,单个的起始字符是不对应任何的UTF-8编码,随即也输出EFBFBD,这就是console.log第一行输出的结果,来看两个“高”,刚才讲到DF没有下个字符了,所以出错,现在有了第二个字符,是B8,那么就能组合了,因为B0的二进制以10开头是符合的,剩下的字节同理啦。

    总结:通常来说,网页都采用UTF-8编码,即文件编码和charset指定的编码要相同,enencodeURIComponent才能封装和解析成功。

    其实,只要文件编码和给出的charset编码相同,enencodeURIComponent就能正常工作,输出为UTF-8字符序列。

  • 相关阅读:
    KMP的next
    关于codeblocks左边文件栏不见的问题
    数据结构第二章内容
    设置notepad++ 的 tab 设为4个空格和设置为中文语言
    字面量
    ..没什么
    今天做的HTML练习
    DAY 145 django的聚合函数和aggregate、annotate方法使用
    DAY 144 Math.round()/Math.ceil()/Math.floor()差异
    DAY 143 DRF-Django rest framework
  • 原文地址:https://www.cnblogs.com/ryzz/p/12606447.html
Copyright © 2011-2022 走看看