Python 编码最全梳理

为什么要写这篇文章呢?这里就要提到某一天,工作的时候,突然发现自己在编码方面,一窍不通。实在惭愧

字符编码是计算机技术的基石,对于程序员来说尤其重要,字符编码的知识是必须要懂的

编码入门知识

ASCII 码:计算机内部,所有信息最终都是一个二进制值。每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从00000000到11111111。上个世纪60年代,美国制定了一套字符编码,对英语字符与二进制位之间的关系,做了统一规定。这被称为 ASCII 码,一直沿用至今。ASCII 码一共规定了128个字符的编码,比如空格SPACE是32(二进制00100000),大写的字母A是65(二进制01000001)。这128个符号(包括32个不能打印出来的控制符号),只占用了一个字节的后面7位,最前面的一位统一规定为0
在这里插入图片描述

非ASCII 码编码:英语用128个符号编码就够了,但是用来表示其他语言,128个符号是不够的。比如,在法语中,字母上方有注音符号,它就无法用 ASCII 码表示。于是,一些欧洲国家就决定,利用字节中闲置的最高位编入新的符号。比如,法语中的é的编码为130(二进制10000010)。这样一来,这些欧洲国家使用的编码体系,可以表示最多256个符号
至于亚洲国家的文字,使用的符号就更多了,汉字就多达10万左右。一个字节只能表示256种符号,肯定是不够的,就必须使用多个字节表达一个符号。比如,简体中文常见的编码方式是 GB2312,使用两个字节表示一个汉字,所以理论上最多可以表示 256 x 256 = 65536 个符号
世界上存在着多种编码方式,同一个二进制数字可以被解释成不同的符号。因此,要想打开一个文本文件,就必须知道它的编码方式,否则用错误的编码方式解读,就会出现乱码。为什么电子邮件常常出现乱码?就是因为发信人和收信人使用的编码方式不一样

Unicode:Unicode 当然是一个很大的集合,现在的规模可以容纳100多万个符号。每个符号的编码都不一样,比如,U+0639表示阿拉伯字母Ain,U+0041表示英语的大写字母A,U+4E25表示汉字严。具体的符号对应表,可以查询unicode.org,或者专门的汉字对应表。Unicode 只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。
比如,汉字严的 Unicode 是十六进制数4E25,转换成二进制数足足有15位(100111000100101),也就是说,这个符号的表示至少需要2个字节。表示其他更大的符号,可能需要3个字节或者4个字节,甚至更多
这里就有两个严重的问题,第一个问题是,如何才能区别 Unicode 和 ASCII ?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果 Unicode 统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。
它们造成的结果是:1)出现了 Unicode 的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示 Unicode。2)Unicode 在很长一段时间内无法推广,直到互联网的出现

UTF-8/16/32:UTF-8 就是在互联网上使用最广的一种 Unicode 的实现方式。其他实现方式还包括 UTF-16(字符用两个字节或四个字节表示)和 UTF-32(字符用四个字节表示),不过在互联网上基本不用。重复一遍,这里的关系是,UTF-8 是 Unicode 的实现方式之一。UTF-8 最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度

UTF-8 的编码规则很简单,只有二条:
1)对于单字节的符号:字节的第一位设为0,后面7位为这个符号的 Unicode 码。因此对于英语字母,UTF-8 编码和 ASCII 码是相同的;
2)对于n字节的符号(n > 1):第一个字节的前n位都设为1,第n + 1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的 Unicode 码。
在这里插入图片描述

跟据上表,解读 UTF-8 编码非常简单。如果一个字节的第一位是0,则这个字节单独就是一个字符;如果第一位是1,则连续有多少个1,就表示当前字符占用多少个字节。
下面,还是以汉字严为例,演示如何实现 UTF-8 编码。
严的 Unicode 是4E25(100111000100101),根据上表,可以发现4E25处在第三行的范围内(0000 0800 - 0000 FFFF),因此严的 UTF-8 编码需要三个字节,即格式是1110xxxx 10xxxxxx 10xxxxxx。然后,从严的最后一个二进制位开始,依次从后向前填入格式中的x,多出的位补0。这样就得到了,严的 UTF-8 编码是11100100 10111000 10100101,转换成十六进制就是E4B8A5

GB 18030(系列):与UTF-8相同,采用多字节编码,每个字可以由1个、2个或4个字节组成;
编码空间庞大,最多可定义161万个字符;
支持中国国内少数民族的文字,不需要动用造字区;
汉字收录范围包含繁体汉字以及日韩汉字

BIG5字符集&编码:Big5,又称为大五码或五大码,是使用繁体中文(正体中文)社区中最常用的电脑汉字字符集标准,共收录13,060个汉字

1)ANSI是默认的编码方式:对于英文文件是ASCII编码,对于简体中文文件是GB2312编码(只针对 Windows 简体中文版,如果是繁体中文版会采用 Big5 码);
2)Unicode编码这里指的是notepad.exe使用的 UCS-2 编码方式:即直接用两个字节存入字符的 Unicode 码,这个选项用的 little endian 格式
3)Unicode big endian编码与上一个选项相对应
4)UTF-8编码

即GBK、GB2312等与UTF8之间都必须通过Unicode编码才能相互转换:

1)GBK、GB2312 --先转–> Unicode --再转–> UTF8

2)UTF8 --先转–> Unicode --再转–> GBK、GB2312

Base64 编码

现有的字符集非常多, 常用的有 UTF-8 / GBK 等,这里面的某些字节在某些传输渠道不支持,比如邮件传输不支持上面ASCII码的控制字符,Base64 的创建就是为了解决此问题

Base64就是为了解决各系统以及传输协议中二进制不兼容的问题而生的

Base64有64个字符, 2^6 = 64, 所以每个Base64编码字符可以用一个6位的二进制来表示.
在这里插入图片描述

使用 Base64 进行编码,大致可以分为 4 步:

  1. 将原始数据每三个字节作为一组,每个字节是8个bit,所以一共是 24 个 bit
  2. 将 24 个 bit 分为四组,每组 6 个 bit
  3. 在每组前面加补 00,将其补全成四组8个bit
    到此步,原生数据的3个字节已经变成4个字节了,增大了将近30%
  4. 根据Base64码表得到扩展后每个字节的对应符号

在这里插入图片描述
在这里插入图片描述
有时我们会在Base64字符末尾会看到=,有时1个,有时2个,这是为啥?

通过上面的我们知道了Base64编码过程是3个字符一组的进行,如果原文长度不是3的倍数怎么办呢? 例如我们的原文为Ma,它不够3个,那么只能在编码后的字符串中补=了。缺一个字符补一个,缺两个补两个即可,所以有时候你会看见base64字符串结尾有1个或者2个=。

Python 2.x

Python 2.x 版本中,默认的编码方式是 ASCII

所以在Python 2.x 版本当中,如果需要加中文注释,都需要添加以下代码,以进行编码声明

# -*- coding: utf-8 -*-

json 编码注意事项:需要显示的添加encoding='utf-8',并且设置添加ensure_ascii=False参数

import json
all_res = {}
write_path = "test.json"
all_res["名字"] = "段誉"

with open(write_path, "w", encoding='utf-8') as f:
    json.dump(all_res, f, ensure_ascii=False)

# test.json 内容
{"名字": "段誉"}

Python 3.x

Python 3.x 版本中,字符串默认使用unicode 编码

和前面提到一样,unicode是中间编码,任何字符编码之前的转换都必须解码成unicode,再编码成目标字符编码
在这里插入图片描述

json 编码注意事项:需要显示的设置添加ensure_ascii=False参数

如果dict包含有汉字,一定加上ensure_ascii=False。否则按参数默认值True,意思是保证dumps之后的结果里所有的字符都能够被ascii表示,汉字在ascii的字符集里面,因此经过dumps以后的str里,汉字会变成对应的unicode

import json
all_res = {}
write_path = "test.json"
all_res["名字"] = "段誉"

with open(write_path, "w") as f:
    json.dump(all_res, f, ensure_ascii=False)

# test.json 内容
{"名字": "段誉"}

总结

任何编码转换之前都要解码成unicode,再转换到目标编码

u 开头的是unicode 编码

b 开头的是byte

Python3的str 默认不是bytes,所以不能decode,只能先encode转为bytes,再decode

Python2的str 默认是bytes,所以能decode

Python 2.x 的编码注意规则

# -*- coding: utf-8 -*-
utf_8_a = '中文'
gbk_a = utf_8_a.decode('utf-8').encode('gbk')
print(gbk_a.decode('gbk'))
 
#输出结果: 中文

疑问例子

>>> a = {"data" : "你是谁\035我是段誉"}
>>> a # # 在输出字典 a 时,Python 使用\x1d 来表示\035,这是十六进制表示
{'data': '你是谁\x1d我是段誉'} 
>>> b = json.dumps(a)
>>> b # 其中 \035 => unicode 编码为 \\u001d
'{"data": "\\u4f60\\u662f\\u8c01\\u001d\\u6211\\u662f\\u6bb5\\u8a89"}'
>>> c = json.dumps(a, ensure_ascii=False)
>>> c
'{"data": "你是谁\\u001d我是段誉"}'
>>> e.encode("utf-8").decode("unicode_escape")
'{"data": "你是谁4我是段誉"}'

解答一:变量b,json.dumps 成str 之后,无法转回中文

unicode_escape 将unicode 的内存编码进行存储,读取文件的时候反向转换回来(直接将unicode 编码进行转换)

解答二:\035 的问题

对于 \035 编码问题的解释,\035 在 ASCII 码中如图所示,8 进制表示\035,\x1d 表示16 进制,属于不可见字符( < 32 都属于不可见字符)

当你使用 json.dumps(a) 对字典进行 JSON 编码时,默认情况下,Python 将特殊字符进行 Unicode 转义,以确保生成的 JSON 字符串是 ASCII 可读的。这就是为什么在输出的 JSON 字符串中看到 \u001d,它是 Unicode 编码的表示形式,对应分组符
在这里插入图片描述

但是为什么以上例子中c变量还是输出\u001d,因为特殊字符’\035’属于不可见字符(ASCII 码 < 32)所以仍然会转成unicode编码\u001d,这是json 规范的一部分,确保生成的json 字符串是有效的可移植的。

如果这个符号是\064->4,的话那就是c=“{“data”: “你是谁4我是段誉”}”

即以为在media_type 为json 时候:用来告诉服务端,消息主体是序列化后的JSON字符串(默认会转unicode 编码)

media_type 为 plain 时候:纯文本格式,空格转换为 “+” 加号,但不对特殊字符编码

参考
https://zhuanlan.zhihu.com/p/38333902
https://zhuanlan.zhihu.com/p/384238870
https://blog.csdn.net/j550341130/article/details/100046887

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/130195.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot 集成 ElasticSearch

1 加入依赖 首先创建一个项目&#xff0c;在项目中加入 ES 相关依赖&#xff0c;具体依赖如下所示&#xff1a; <dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.1.0</version&g…

leetcode 62

leetcode 62 题目 解题思路 class Solution { public:int uniquePaths(int m, int n) {vector<vector<int>> f(m, vector<int>(n));for(int i0; i<m; i){f[i][0] 1;}for(int j0; j<n; j){f[0][j] 1;}for(int i1; i<m; i){for(int j1; j<n; j){…

C语言:对于宏的一些概念及技巧

一、前言 宏在C语言中是一段有名称的代码段&#xff0c;在程序编译过程中&#xff0c;会将宏的内容被这段代码进行替换&#xff0c;常常用于定义一些常量、函数、代码块等&#xff0c;由于近年来发现许多公司进行面试时对于宏的面试题尤为多&#xff0c;故本文将对C语言中的宏的…

说说React render方法的原理?在什么时候会被触发?

一、原理 首先&#xff0c;render函数在react中有两种形式&#xff1a; 在类组件中&#xff0c;指的是render方法&#xff1a; class Foo extends React.Component { render() { return <h1> Foo </h1>; } } 在函数组件中&#xff0c;指的是函…

C语言—统计从键盘输入的一行英文句子的字符个数

流程图 代码 #include <stdio.h>int main() {int count0;printf("请输入英文字符&#xff0c;回车确认&#xff1a;");while (getchar()!\n){count count 1;}printf("共输入%d个字符\n", count);system("pause");return 0; }请输入英文字…

一文入门Springboot+actuator+Prometheus+Grafana

环境介绍 技术栈 springbootmybatis-plusmysqloracleactuatorPrometheusGrafana 软件 版本 mysql 8 IDEA IntelliJ IDEA 2022.2.1 JDK 1.8 Spring Boot 2.7.13 mybatis-plus 3.5.3.2 本地主机应用 192.168.1.9:8007 PrometheusGrafana安装在同一台主机 http://…

【有限元方法】Newton-Raphson Method

Newton-Raphson Method Linear vs Nonlinear Analysis: At this point, we can conduct a linear analysis no problem ∫ ∑ i , j 1 3 σ i j ε i j ∗ d v ∫ t n ⋅ u ∗ d s ∫ ρ b ⋅ u ∗ d v ⇒ ∫ e [ B ] T [ C ] [ B ] d x ⏟ k e u e ∫ ∂ e [ N ] T t n …

CSS3 分页、框大小、弹性盒子

一、CSS3分页&#xff1a; 网站有很多个页面&#xff0c;需要使用分页来为每个页面做导航。示例&#xff1a; <style> ul.pagination { display: inline-block; padding: 0; margin: 0; } ul.pagination li {display: inline;} ul.pagination li a { color: black; f…

[工业自动化-9]:西门子S7-15xxx编程 - PLC主站 - 信号量:模拟量

目录 前言&#xff1a; 一、模拟量模块 1.1 概述 1.2 安装 1.3 模拟量链接线 二、模拟量常见问题 2.1 两线制、四线制&#xff08;电流&#xff09; 2.2 模拟量模块的参数 2.3 差分信号与单端信号 三、如何防止电磁干扰 3.1 概述 3.2 工业现场的电磁干扰源来源 3.…

3D物理模拟和视觉特效软件SideFX Houdini mac中文介绍

SideFX Houdini for mac是一款3D物理模拟和视觉特效软件&#xff0c;几乎所有好莱坞特效电影里的物理模拟&#xff0c;包括碎裂&#xff0c;烟尘&#xff0c;碰撞&#xff0c;火焰&#xff0c;流体等模拟&#xff0c;都看得到它的身影。其独特的节点式操作方式&#xff0c;尤其…

Anaconda Powershell Prompt和Anaconda Prompt的区别

先说结论&#xff1a;主要功能应该一样。区别在于powershell支持的命令更多。比如查询路径的命令pwd和列表命令ls。 Anaconda PowerShell Prompt和Anaconda Prompt是Anaconda发行版中两个不同的命令提示符工具。 Anaconda Prompt是Anaconda发布的默认命令提示符工具&#xff0…

前端开发神器之 VsCode AI 辅助插件 DevChat

目录 前言DevChat介绍DevChat 独特优势注册账号安装插件设置密钥访问指令AI 解疑 最后 #AI编程助手哪家好&#xff1f;DevChat“真”好用 # 前言 我们都有过写代码时反复看了半天也不知道bug在哪&#xff0c;大大浪费了时间。一些基础的代码可能看一会儿能够解决&#xff0c;但…

DDoS攻击剧增,深入解析抗DDoS防护方案

当下DDoS攻击规模不断突破上限&#xff0c;攻击方式越发复杂。面对复杂的攻击形式&#xff0c;对于企业和组织来说无疑需要更完备的抗DDoS方案&#xff0c;依靠传统的解决方法并不能做到一劳永逸。在服务器抵抗DDoS防护上&#xff0c;你不会忽略F5的产品&#xff0c;让我们一起…

【Git】gui图形化界面的使用、ssh协议以及idea集成Git

目录 gui图形化界面的使用 介绍 特点 gui图形的使用 ssh协议 介绍 步骤及概念 ssh协议的使用 配置公钥 idea集成Git idea配置git IDEA安装gitee IDEA中登入Git ​编辑 项目分享 克隆分享的项目 ​编辑 ​编辑 idea上传远程 gui图形化界面的使用 介绍 GUI&#xff08…

4 Paimon数据湖之Hive Catalog的使用

更多Paimon数据湖内容请关注&#xff1a;https://edu.51cto.com/course/35051.html Paimon提供了两种类型的Catalog&#xff1a;Filesystem Catalog和Hive Catalog。 Filesystem Catalog&#xff1a;会把元数据信息存储到文件系统里面。Hive Catalog&#xff1a;则会把元数据…

什么是进程等待?

什么是进程等待 在了解进程等待之前&#xff0c;我们要回顾一下什么是僵尸进程&#xff1a;是指一个已经终止执行的进程&#xff0c;但其父进程还没有通过 wait() 系统调用来获取该进程的退出状态信息。当一个进程正常退出或者被终止时&#xff0c;其所占用的系统资源会被操作…

rust实现quic服务端和客户端

演示如何使用 Quinn 库实现一个简单的 QUIC 客户端和服务器。QUIC 是一种基于 UDP 的协议&#xff0c;用于在互联网上进行快速和安全的通信。 在程序中&#xff0c;使用了 Rust 的标准库中的 error、net 和 sync 模块&#xff0c;以及第三方库 tokio 和 quinn。程序使用了 asy…

C# OpenCvSharp DNN HybridNets 同时处理车辆检测、可驾驶区域分割、车道线分割

效果 项目 代码 using OpenCvSharp; using OpenCvSharp.Dnn; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; using System.Numerics; using System.Text; using System.Windows.Forms;namespace OpenCvSharp_D…

如何使用CORS和CSP保护前端应用程序安全

前端应用在提供无缝用户体验方面起着核心作用。在当今互联网的环境中&#xff0c;第三方集成和API的普及使得确保强大的安全性至关重要。安全漏洞可能导致数据盗窃、未经授权访问以及品牌声誉受损。本文将向您展示如何使用CORS和CSP为您的网页增加安全性。 嗨&#xff0c;大家好…

使用Redis实现热搜功能

Redis热搜 原理数据类型redis操作简单实现 实操封装方法执行方法最后使用springboot的定时任务对热搜榜单进行维护 原理 使用redis实现热搜的原理就是维护一个zset集合&#xff0c;然后使用score作为当前搜索词的搜索量&#xff0c;score越高的搜索词就说明该搜索词热度越高。…