h2:first-child, body>h1:first-child, body>h1:first-child+h2, body>h3:first-child, body>h4:first-child, body>h5:first-child, body>h6:first-child {
margin-top: 0;
padding-top: 0;
}

a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
margin-top: 0;
padding-top: 0;
}

h1+p, h2+p, h3+p, h4+p, h5+p, h6+p {
margin-top: 10px;
}

/* LINKS
=============================================================================*/

a {
color: #4183C4;
text-decoration: none;
}

a:hover {
text-decoration: underline;
}

/* LISTS
=============================================================================*/

ul, ol {
padding-left: 30px;
}

ul li > :first-child,
ol li > :first-child,
ul li ul:first-of-type,
ol li ol:first-of-type,
ul li ol:first-of-type,
ol li ul:first-of-type {
margin-top: 0px;
}

ul ul, ul ol, ol ol, ol ul {
margin-bottom: 0;
}

dl {
padding: 0;
}

dl dt {
font-size: 14px;
font-weight: bold;
font-style: italic;
padding: 0;
margin: 15px 0 5px;
}

dl dt:first-child {
padding: 0;
}

dl dt>:first-child {
margin-top: 0px;
}

dl dt>:last-child {
margin-bottom: 0px;
}

dl dd {
margin: 0 0 15px;
padding: 0 15px;
}

dl dd>:first-child {
margin-top: 0px;
}

dl dd>:last-child {
margin-bottom: 0px;
}

/* CODE
=============================================================================*/

pre, code, tt {
font-size: 12px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}

code, tt {
margin: 0 0px;
padding: 0px 0px;
white-space: nowrap;
border: 1px solid #eaeaea;
background-color: #f8f8f8;
border-radius: 3px;
}

pre>code {
margin: 0;
padding: 0;
white-space: pre;
border: none;
background: transparent;
}

pre {
background-color: #f8f8f8;
border: 1px solid #ccc;
font-size: 13px;
line-height: 19px;
overflow: auto;
padding: 6px 10px;
border-radius: 3px;
}

pre code, pre tt {
background-color: transparent;
border: none;
}

kbd {
-moz-border-bottom-colors: none;
-moz-border-left-colors: none;
-moz-border-right-colors: none;
-moz-border-top-colors: none;
background-color: #DDDDDD;
background-image: linear-gradient(#F1F1F1, #DDDDDD);
background-repeat: repeat-x;
border-color: #DDDDDD #CCCCCC #CCCCCC #DDDDDD;
border-image: none;
border-radius: 2px 2px 2px 2px;
border-style: solid;
border-width: 1px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
line-height: 10px;
padding: 1px 4px;
}

/* QUOTES
=============================================================================*/

blockquote {
border-left: 4px solid #DDD;
padding: 0 15px;
color: #777;
}

blockquote>:first-child {
margin-top: 0px;
}

blockquote>:last-child {
margin-bottom: 0px;
}

/* HORIZONTAL RULES
=============================================================================*/

hr {
clear: both;
margin: 15px 0;
height: 0px;
overflow: hidden;
border: none;
background: transparent;
border-bottom: 4px solid #ddd;
padding: 0;
}

/* TABLES
=============================================================================*/

table th {
font-weight: bold;
}

table th, table td {
border: 1px solid #ccc;
padding: 6px 13px;
}

table tr {
border-top: 1px solid #ccc;
background-color: #fff;
}

table tr:nth-child(2n) {
background-color: #f8f8f8;
}

/* IMAGES
=============================================================================*/

img {
max-width: 100%
}
-->

引言

我相信很多人在初接触编程时,都被字符集狠狠地虐过,特别是数据库的中文乱码问题,那么乱码是怎么产生的呢? 我们都知道计算机是以二进制存储和运行的,那么它是怎么把二进制数据转换为各种文字的呢? 还有我们常用的各种字符集,常用的编码转换,都是怎么进行的呢?

本博文所写的内容不是技术干货,只是对我们常用的字符集和编码的一个小总结,小科普。我相信读完本文,您应该对 字符集和常见编码方式 有个差不多的认识了。


ASCII码

ASCII码(American Standard Code for Information Interchange,美国信息交换标准代码)应该是我们最初接触过的编码方式了,编程最常用的字符都被它包括在内。它使用7bit来表示 128(2e7)个字符,最高位固定为 0,共占用一个字节。其中:

  • 0~31 及 127(共33个)是控制字符或通信专用字符(其余为可显示字符),如控制符:TAB(制表符)、CR(回车)、DEL(删除)、BS(退格)等,常用的ASCII值为 8、9、10 和13 分别转换为退格、制表、换行和回车字符。

  • 48~57 为 0 到 9 十个阿拉伯数字。

  • 65~90 为 26 个大写英文字母,97~122 号为 26 个小写英文字母,其余为一些标点符号、运算符号等。

  • 32~47,58~64,123~126 代表常用标点符号(:‘等);

我们会发现这些中很多都可以在键盘上可以找得到。

tips:

  • PHP中我们可以使用ord($char)来得到一个字符的ASCII码;
  • 可以用chr($int) 来得到得到对应ASCII数值的字符;

ANSI编码

美国人发明了计算机,并将他们最常用的字符以一个字节存入了计算机,可是世界上这么多的语言都要用计算机来表示怎么办呢?

为了使计算机支持多种语言,不同的国家和地区制定了不同的标准。而对于汉字,产生了 GB2312、 BIG5、 JIS 等各自的编码标准。这些使用 1 个字节表示一个英文字符, 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码。

我们在使用window系统保存文件选择编码方式时,会看到有这个ANSI编码这个选项,在不同的windows系统中,ANSI代表着不同的编码。不同ANSI编码之间互不兼容,当信息在国际间交流时,无法将属于两种语言的文字,存储在同一段 ANSI 编码的文本中。


Unicode编码

来源

既然ANSI编码有着不同编码之间互不兼容不能共存的缺点,而现代网络中又会频繁出现多语言交互,如果在多语言网络传播时,一个 '11011011' 到底代表着什么字符呢?

这时,Unicode 应运而生,它是一个足够大的字符编码映射表,将所有字符都囊括其中,每一个都对应唯一一个 Unicode 数值。如汉字 '好' 对应的 unicode 数值为 '0x597d', 转为二进制为 '0101 1001 0111 1101',表示它需要 16 bit,两个字节,当然还有需要更多字节来保存的字符(原谅我举起不来粟子)。

最新的UCS-4标准是一个尚未填充完全的31位 Unicode 字符集,它使用 31 位来保存字符,加上恒为 0 的首位,共需占据 32 位,4 字节。这样,Unicode 便能保存 2e31 个字符,已经完全足够存储世界上所有的字符了。

tips:

  • 在网络传输中,中文字符会被转换为 Unicode 来传输,用正则匹配一个中文字符为:\x{4e00}-\x{9fa5}

  • PHP中想查看一个中文字符的 Unicode 码,可以使用json_encode($str)

  • 想 json_encode 保持原中文不自动转为 Unicode 可以使用json_encode($str, JSON_UNESCAPED_UNICODE);添加一个 option 常量。

  • PHP 中各种编码方式的转换可以看一下我的这篇博客:PHP用mb_string函数库处理与windows相关中文字符

  • 乱码的产生就是因为对数据编码和解码的方式不同: windows中使用 ANSI 标准的 GBK 编码,数据库中使用 Unicode 的不同的编码方式存储,网页浏览器又以不同编码来解析,统一为 UTF-8 进行数据编码即可解决这类问题。

注意 Unicode 只是一种符号集,字符存储的具体实现方式看下面

UTF-8

我们知道了按照 Unicode 的标准,存储一个字符最多要使用 4 个字节。如果所有的字符都按照这个标准来存储,那么欧美国家可能要哭了,因为他们本来可以用一个字节轻松存储文档的,因为国际化,所有的存储空间要增大三倍。为了解决这个问题,UTF-8(8-bit Unicode Transformation Format)出现了。

UTF-8采用变长的编码方式,使用 1~4 个字节来表示一个符号:

  • 对于单字节的符号,字节的第一位设为 0,后面 7 位为这个符号的 unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的。
  • 对于 n 字节的符号(n>1),第一个字节的前 n 位都设为 1,第 n+1 位设为0,后面字节的前两位一律设为 10。剩下的没有提及的二进制位,全部为这个符号的 unicode 码。

于是,皆大欢喜,UTF-8 成为了互联网使用最广泛的 Unicode 编码实现方式。

除此之外,Unicode 还有UTF-7、Punycode、CESU-8、SCSU、UTF-32、GB18030 等实现方式;

UTF8MB4

utf8mb4 并不是 Unicode 的实现方式之一,它是 mysql 的编码方式,在最新的 mysql 中,utf8mb4 已经可以代替 utf8,并具有 utf8 不具有的特点。

mb4, 即 most bytes 4, mysql 的 utf8 编码最多使用 3 个字节存储一个字符,在存储 4 字节字符的时候会报错,而 utf8mb4 最多可以使用4个字节来存储一个字符。所以它可以用来存储更多的 Unicode 字符,包括一些 Emoji 表情(Emoji 是一种特殊的 Unicode 编码,常见于 ios 和 android 手机上),和很多不常用的汉字,以及任何新增的 Unicode 字符。

由于 utf8mb4 为 utf8 的超集,所以 utf8 编码的 mysql 数据库可以平滑过渡到 utf8mb4。


Url编码

url 编码是 web 开发中最常用的编码了。由于 url 中一些字符有特殊的作用,那么它被称为保留字符(reserved purpose),如 = 用来赋值, ? 用来表示 query_string 的开始, # 用来标识锚点。当我们仅仅想把这些字符当作普通字符串传输该怎么办呢,这就需要使用 url 编码。

URL编码(URL encoding),由于其使用 % 为前缀来替代特殊字符,也被称作百分号编码,是特定上下文的统一资源定位符 (URL)的编码机制。也用于为 "application/x-www-form-urlencoded" MIME 准备数据, 因为它用于通过 HTTP 的请求操作 (request) 提交 HTML 表单数据。

转换规则:

首先需要把该字符的 ASCII 的值表示为两个十六进制的数字,然后在其前面放置转义字符( % ),置入 URI 中的相应位置;对于非 ASCII 字符(如中文等), 需要转换为 UTF-8 字节序, 然后每个字节按照上述方式表示。

下表是常见的字符和 urlencode 之后的 标识:

char url char url char url char url char url
! %21 # %23 $ %24 & %26 ' %27
( %28 ) %29 * %2A + %2B , %2C
/ %2F : %3A ; %3B = %3D ? %3F
@ %40 [ %5B ] %5D        

tips: PHP中使用 urlencode()urldecode() 进行 url 的编码和解码。


Base64编码

base64 也是一种 web 开发中的常用编码,它能实现简单的可逆加密,同时在系统之间传输二进制等字符使用 base64 编码也很方便。

它使用 A-Z a-z 0-9 + / 等 64 (2e6) 个字符来表示字符。严格来说,还有用来标识结尾处分组的字节数的 = , 它只会出现在编码串的最后。

编码规则:

将一个字符串以分为三个字节(3 * 8 = 24 bit)为一个分组, 将此 24 个 bit 分为四组,每组 6 bit, 然后使用 其 6 bit 对应的十进制数来映射出一个 base64 字符;

如 UTF-8(三个字节表示一个中文) 中文 ‘琪’ 转 base64 的过程为

  • 转换为十六进制表示为 e790aa
  • 每个十六进制字符转换为4个二进制bit为 11100111 10010000 10101010
  • 拆分为四个 6 bit 分组为 111001 111001 000010 101010
  • 对应的十进制数字为57 57 2 42
  • 对应 base64 编码 为 55Cq;

十进制对应 base64 编码的 映射表如下:

那么一个字符串拆分到最后不足三字节了怎么办呢?

  • 二个字节的情况:将这二个字节的 16 bit 分为三组,那么最后一组只有 4 bit (16 % 6 = 4); 在这 4 个 bit 末尾添加 2 个 0 同样凑成 6 bit;再在末尾补上一个=号标识补位,以便于解码;
  • 一个字节的情况:将这一个字节一共 8 bit 分为两组,那么最后一组只有 2 bit (8 % 6 = 2); 在这 2 个 bit 末尾添加 4 个 0 同样凑成 6 bit;再在末尾补上==号标识补位,以便于解码;

由于原来三个字节的字符最后转换成四个字节来表示,base64 编码后字符串长度一般为原来 的 3/4。

以下是我为了完全了解 base64 编码自己用 PHP 实现的一个 base64 编码类(写完编码犯懒了。。。):

<?php

class Base64 {

    private $mapping = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r',
's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
'4', '5', '6', '7', '8', '9', '+', '/',]; /**
* base64 主方法
*
* @param $str
*
* @return string
*/
public function encode($str) {
// 将字符串unpack成为十六进制
$unpacked = unpack('H*', $str);
$hex = str_split($unpacked[1]);
$bin_str = $this->HexToBin($hex); return $this->binToBase64($bin_str);
} /**
* 将二进制字符串分组后映射为对应的base64字符串
*
* @param $bin_str
*
* @return string
*/
private function binToBase64($bin_str) {
$base64_str = '';
$bin_list = str_split($bin_str, 6);
foreach ($bin_list as $bin) {
$append = '';
switch (strlen($bin)) {
// $bin为6位的不用特殊处理
case 6:
break;
// $bin为4位的是二字节字符串 2*8%6 = 4
case 4:
$bin = str_pad($bin, 6, '0', STR_PAD_RIGHT);
$append = '=';
break;
// $bin为2位的是一字节字符串 1*8%6 = 2
case 2:
$append = '==';
$bin = str_pad($bin, 6, '0', STR_PAD_RIGHT);
break;
}
$order = base_convert($bin, 2, 10);
$char = $this->mapping[$order];
$base64_str .= $char . $append;
} return $base64_str;
} /**
* 将十六进制字符串转换为二进制字符串
*
* @param $hex
*
* @return string
*/
private function hexToBin($hex) {
$bin_str = '';
foreach ($hex as $char) {
// 将十六进制转为二进制字符串,每个十六进制字符转为4位二进制,不足的以0补充
$bin = base_convert($char, 16, 2);
if (strlen($bin) < 4) {
$bin = str_pad($bin, 4, '0', STR_PAD_LEFT);
}
$bin_str .= $bin;
} return $bin_str;
}
} $encoder = new Base64();
var_dump($encoder->encode('枕边书blog')); // 5p6V6L655LmmYmxvZw==
var_dump(base64_encode('枕边书blog')); // 5p6V6L655LmmYmxvZw==

tips: 在 PHP 中使用 base64_encode()base64_decode() 进行 base64 编码和解码。


小结

字符集和编码一般不是 web 开发中的重点,但了解一下也挺有意思的,既能增长见识,还能预防哪一天突然踩了其中的坑。

如果您觉得本文对您有帮助,可以帮忙点一下推荐,也可以关注我。如有错漏之处,烦请指出,谢谢。

参考:

阮一峰:字符编码笔记:ASCII,Unicode和UTF-8

维基百科:Unicode

Base64笔记

WEB开发中的字符集和编码的更多相关文章

  1. java中的字符集和编码

    前言 上次对计算机中的“字符集”和“编码”分别进行了总结,并指出二者之间的区别,不要搞混了,不清楚的再回到上一章看一下.今天再总结下java中是如何使用字符集(主要是Unicode字符集,其他常用字符 ...

  2. Ruby:字符集和编码学习总结

    背景 Ruby直到1.9版本才很好的支持了多字节编码,本文简单总结了今天学习的关于Ruby编码方面的知识. 字符串可以使用不同的编码 在.NET中字符串的编码是一致的,Ruby允许字符串有不同的编码, ...

  3. .NET:字符集和编码学习总结

    背景 一直没有深入的学习字符集和编码的知识(现在也没有深入),今天查阅了一些资料,弄明白了一些事情,本文就简单记录一下. 字符集和编码 字符集是指一些符号组成的集合,编码是对指定字符集如何表示为字节的 ...

  4. WEB开发中常用的正则表达式

    在计算机科学中,正则表达式用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串.在WEB开发中,正则表达式通常用来检测.查找替换某些符合规则的字符串,如检测用户输入E-mai格式是否正确,采集符 ...

  5. WEB开发中常用的正则表达式集合

    在计算机科学中,正则表达式用来描述或者匹配一系列符合某个句法规则的字符串的单个字符串.在WEB开发中,正则表达式通常用来检测.查找替换某些符合规则的字符串,如检测用户输入E-mai格式是否正确,采集符 ...

  6. [译]如何在Web开发中使用Python

    [译]如何在Web开发中使用Python 原文:HOWTO Use Python in the Web 摘要 这篇文档展示了Python如何融入到web中.它介绍了几种Python结合web服务器的方 ...

  7. 第123天:移动web开发中的常见问题

    一.函数库 underscoreJS _.template: <ol class="carousel-indicators"> <!--渲染的HTML字符串--& ...

  8. Web 开发中 Blob 与 FileAPI 使用简述

    本文节选自 Awesome CheatSheet/DOM CheatSheet,主要是对 DOM 操作中常见的 Blob.File API 相关概念进行简要描述. Web 开发中 Blob 与 Fil ...

  9. 【初码干货】使用阿里云对Web开发中的资源文件进行CDN加速的深入研究和实践

    提示:阅读本文需提前了解的相关知识 1.阿里云(https://www.aliyun.com) 2.阿里云CDN(https://www.aliyun.com/product/cdn) 3.阿里云OS ...

随机推荐

  1. Mybatis-mapper-xml-基础

    今天学习http://www.mybatis.org/mybatis-3/zh/sqlmap-xml.html.关于mapper.xml的sql语句的使用. 项目路径:https://github.c ...

  2. jQuery-1.9.1源码分析系列(六) 延时对象应用——jQuery.ready

    还记不记得jQuery初始化函数jQuery.fn.init中有这样是一个分支 //document ready简便写法$(function(){…}) } else if ( jQuery.isFu ...

  3. [开源ORM] SqliteSugar 3.x .net Core版本成功上线

    SqliteSqlSugar 3.X API 作为支持.NET CORE 为数不多的ORM之一,除了具有优越的性能外,还拥有强大的功能,不只是满足你的增,删,查和改.实质上拥有更多你想像不到的功能,当 ...

  4. iOS中获取cell中webview的内容尺寸

    最近项目中遇到在cell中获取webView的内容的尺寸的需求 实现的思路其实很简单 就是通过执行js 获取尺寸即可 为了后面用着方便我直接封装了一个HTML的cell 起名就叫 STHTMLBase ...

  5. Github团队开发示例(一)

    Github团队开发示例(二) 作者:Grey 原文地址:http://www.cnblogs.com/greyzeng/p/6044837.html 我们可以在Github上管理自己的团队项目.团队 ...

  6. MySQL数据库中delimiter的作用概述

    以下的文章主要是向大家描述的是MySQL数据库中delimiter的作用是什么?我们一般都认为这个命令和存储过程关系不大,到底是不是这样的呢?以下的文章将会给你相关的知识,望你会有所收获. 其实就是告 ...

  7. Netbeans 8.2关于PHP的新特性

    Netbeans 8.2在这个国庆期间终于发布了,其与PHP相关的新特性主要有: 支持PHP 7 详见前面翻译的一篇文章:Netbeans 8.2将支持PHP 7 编辑器功能增强 文档好像没有明确说明 ...

  8. linux 下 systemd-udevd 服务解析

    最近在看linux下重定向的时候看到 的这个系统的服务,所以记下来备忘. 描述:systemd-udevd是监听内核发出的设备事件,并根据udev规则处理每个事件. 选项: --daemon 脱离控制 ...

  9. python基础之文件处理

    读和写文件 读写文件是最常见的IO操作.Python内置了读写文件的函数,用法和C是兼容的. 读写文件前,我们先必须了解一下,在磁盘上读写文件的功能都是由操作系统提供的,现代操作系统不允许普通的程序直 ...

  10. 在 Visual Studio 等编辑器/IDE中自动切换输入法,不需要手动的有没有?

    使用Visual Studio写代码,经常遇到的一个问题就是切换中文输入法麻烦,输入完注释//,要切换到中文,输入完引号,要输入中文,然后还需要切换回来,有没有? 有时候中文输入法忽然失效有没有?明明 ...