Python小技巧:bytes与str的区别

news2024/11/17 19:50:39

嗨喽~大家好呀,这里是魔王呐 ❤ ~!

python更多源码/资料/解答/教程等 点击此处跳转文末名片免费获取

bytes实例包含的是原始数据,即8位的无符号值(通常按照ASCII编码标准来显示)。

a = b'h\x65llo' 
print(list(a)) 
print(a) 
>>> 
[104, 101, 108, 108, 111] 
b'hello' 

str实例包含的是Unicode码点(code point,也叫作代码点),这些码点与人类语言之中的文本字符相对应。

a = 'a\u0300 propos' 
print(list(a)) 
print(a) 
>>> 
['a', '`', ' ', 'p', 'r', 'o', 'p', 'o', 's'] 
à propos 

大家一定要记住:str实例不一定非要用某一种固定的方案编码成二进制数据,bytes实例也不一定非要按照某一种固定的方案解码成字符串。

  • 要把Unicode数据转换成二进制数据,必须调用str的encode方法。

  • 要把二进制数据转换成Unicode数据,必须调用bytes的decode方法。

调用这些方法的时候,可以明确指出自己要使用的编码方案,也可以采用系统默认的方案,通常是指UTF-8(但有时也不一定,下面就会讲到这个问题)。

编写Python程序的时候,一定要把解码和编码操作放在界面最外层来做,让程序的核心部分可以使用Unicode数据来运作,这种办法通常叫作Unicode三明治(Unicode sandwich)。

程序的核心部分,应该用str类型来表示Unicode数据,并且不要锁定到某种字符编码上面。

这样可以让程序接受许多种文本编码(例如Latin-1、Shift JIS及Big5),并把它们都转化成Unicode,也能保证输出的文本信息都是用同一种标准(最好是UTF-8)编码的。

两种不同的字符类型与Python中两种常见的使用情况相对应:

  • 开发者需要操作原始的8位值序列,序列里面的这些8位值合起来表示一个应该按UTF-8或其他标准编码的字符串。

  • 开发者需要操作通用的Unicode字符串,而不是操作某种特定编码的字符串。

我们通常需要编写两个辅助函数(helper function),以便在这两种情况之间转换,确保输入值类型符合开发者的预期形式。

第一个辅助函数接受bytes或str实例,并返回str:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:926207505
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
def to_str(bytes_or_str): 
    if isinstance(bytes_or_str, bytes): 
        value = bytes_or_str.decode('utf-8') 
    else: 
        value = bytes_or_str 
    return value  # Instance of str 
 
print(repr(to_str(b'foo'))) 
print(repr(to_str('bar'))) 
>>> 
'foo' 
'bar' 

第二个辅助函数也接受bytes或str实例,但它返回的是bytes:

def to_bytes(bytes_or_str): 
    if isinstance(bytes_or_str, str): 
        value = bytes_or_str.encode('utf-8') 
    else: 
        value = bytes_or_str 
    return value  # Instance of bytes 
 
print(repr(to_bytes(b'foo'))) 
print(repr(to_bytes('bar'))) 

在Python中使用原始的8位值与Unicode字符串时,有两个问题要注意。

第一个问题是,bytes与str这两种类型似乎是以相同的方式工作的,但其实例并不相互兼容,所以在传递字符序列的时候必须考虑好其类型。

可以用+操作符将bytes添加到bytes,str也可以这样。

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:926207505
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
print(b'one' + b'two') 
print('one' + 'two') 
>>> 
b'onetwo' 
onetwo 

但是不能将str实例添加到bytes实例:

b'one' + 'two' 
>>> 
Traceback ... 
TypeError: can't concat str to bytes 

也不能将bytes实例添加到str实例:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:926207505
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
'one' + b'two' 
>>> 
Traceback ... 
TypeError: can only concatenate str (not "bytes") to str 

bytes与bytes之间可以用二元操作符(binary operator)来比较大小,str与str之间也可以:

assert b'red' > b'blue' 
assert 'red' > 'blue' 

但是str实例不能与bytes实例比较:

assert 'red' > b'blue' 

反过来也一样,也就是说bytes实例不能与str实例比较:

assert b'blue' < 'red' 

判断bytes与str实例是否相等,总是会评估为假(False),即便这两个实例表示的字符完全相同,它们也不相等。

例如,在下面这个例子里,它们表示的字符串都相当于ASCII编码之中的foo。

print(b'foo' == 'foo') 
>>> 
False 

两种类型的实例都可以出现在%操作符的右侧,用来替换左侧那个格式字符串(format string)里面的%s。

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:926207505
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
print(b'red %s' % b'blue') 
print('red %s' % 'blue') 
>>> 
b'red blue' 
red blue 

如果格式字符串是bytes类型,那么不能用str实例来替换其中的%s,因为Python不知道这个str应该按照什么方案来编码。

print(b'red %s' % 'blue') 

但反过来却可以,也就是说如果格式字符串是str类型,则可以用bytes实例来替换其中的%s,问题是,这可能跟你想要的结果不一样。

print('red %s' % b'blue') 
>>> 
red b'blue' 

这样做,会让系统在bytes实例上面调用__repr__方法,然后用这次调用所得到的结果替换格式字符串里的%s,因此程序会直接输出b’blue’,而不是像你想的那样,输出blue本身。

第二个问题发生在操作文件句柄的时候,这里的句柄指由内置的open函数返回的句柄。

这样的句柄默认需要使用Unicode字符串操作,而不能采用原始的bytes。

习惯了Python 2的开发者,尤其容易碰到这个问题,进而导致程序出现奇怪的错误。

例如,向文件写入二进制数据的时候,下面这种写法其实是错误的。

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:926207505
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
with open('data.bin', 'w') as f: 
    f.write(b'\xf1\xf2\xf3\xf4\xf5') 
>>> 
Traceback ... 
TypeError: write() argument must be str, not bytes 

程序发生异常是因为在调用open函数时,指定的是’w’模式,所以系统要求必须以文本模式写入。

如果想用二进制模式,那应该指定’wb’才对。

在文本模式下,write方法接受的是包含Unicode数据的str实例,不是包含二进制数据的bytes实例。

所以,我们得把模式改成’wb’来解决该问题。

with open('data.bin', 'wb') as f: 
    f.write(b'\xf1\xf2\xf3\xf4\xf5') 

读取文件的时候也有类似的问题。例如,如果要把刚才写入的二进制文件读出来,那么不能用下面这种写法。

with open('data.bin', 'r') as f: 
    data = f.read() 

程序出错,是因为在调用open函数时指定的是’r’模式,所以系统要求必须以文本模式来读取。

若要用二进制格式读取,应该指定’rb’。

以文本模式操纵句柄时,系统会采用默认的文本编码方案处理二进制数据。

所以,上面那种写法会让系统通过bytes.decode把这份数据解码成str字符串,再用str.encode把字符串编码成二进制值。

然而对于大多数系统来说,默认的文本编码方案是UTF-8,所以系统很可能会b’\xf1\xf2\xf3\xf4\xf5’当成UTF-8格式的字符串去解码,于是就会出现上面那样的错误。

为了修正错误,需要把模式改成’rb’。

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:926207505
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
with open('data.bin', 'rb') as f: 
    data = f.read() 
 
assert data == b'\xf1\xf2\xf3\xf4\xf5' 

另一种改法是在调用open函数的时候,通过encoding参数明确指定编码标准,以确保平台特有的一些行为不会干扰代码的运行效果。

例如,假设刚才写到文件里的那些二进制数据表示的是一个采用’cp1252’标准(cp1252是一种老式的Windows编码方案)来编码的字符串,则可以这样写:

with open('data.bin', 'r', encoding='cp1252') as f: 
    data = f.read() 
 
assert data == 'ñòóôõ' 

这样程序就不会出现异常了,但返回的字符串也与读取原始字节数据所返回的有很大区别。

通过这个例子,我们要提醒自己注意当前操作系统默认的编码标准(可以执行python3 -c 'import locale; print(locale.getpreferredencoding())'命令查看),了解它与你所期望的是否一致。

如果不确定,那就在调用open时明确指定encoding参数。

要点

  1. bytes包含的是由8位值所组成的序列,str包含的是由Unicode码点所组成的序列。

  2. 我们可以编写辅助函数来确保程序收到的字符序列确实是期望要操作的类型(要知道自己想操作的到底是Unicode码点,还是原始的8位值。用UTF-8标准给字符串编码,得到的就是这样的一系列8位值)。

  3. bytes与str这两种实例不能在某些操作符(例如>、==、+、%操作符)上面混用。

  4. 从文件中读取二进制数据(或者把二进制数据写入文件)时,应该用’rb’(‘wb’)这样的二进制模式打开文件。

  5. 如果要从文件中读取(或者要写入文件之中)的是Unicode数据,那么必须注意系统默认的文本编码方案。若无法肯定,可通过encoding参数明确指定。

尾语

最后感谢你观看我的文章呐~本次航班到这里就结束啦 🛬

希望本篇文章有对你带来帮助 🎉,有学习到一点知识~

躲起来的星星🍥也在努力发光,你也要努力加油(让我们一起努力叭)。

最后,宣传一下呀~👇👇👇更多源码、资料、素材、解答、交流皆点击下方名片获取呀👇👇

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【1107】有关环境的学习记录。。。

有关python环境&#xff01;&#xff01;&#xff01; 1、python解释器就是 python 3.7.2 之类的。 VSCode 是代码编辑器。 下图的每一个都是可选的python环境&#xff0c;Python 3.8.3&#xff08;‘base’&#xff09;是下载在电脑上的python环境&#xff08;下载miniConda时…

深入解析 Redis 分布式锁原理

一、实现原理 1.1 基本原理 JDK 原生的锁可以让不同线程之间以互斥的方式来访问共享资源&#xff0c;但如果想要在不同进程之间以互斥的方式来访问共享资源&#xff0c;JDK 原生的锁就无能为力了。此时可以使用 Redis 来实现分布式锁。 Redis 实现分布式锁的核心命令如下&am…

chrome v3开发插件实现所有网站允许跨域

场景&#xff1a; chrome 插件 升级到v3后&#xff0c;原来修改请求响应都变成异步&#xff0c;即无法同步拦截来修改请求响应。 在v3中也不支持修改请求响应内容。 问题&#xff1a;如何在chrome v3中允许其他网站跨域呢。 方式一&#xff1a;禁用chrome跨域&#xff0c;禁…

JPA编程中自定义SQL语句使用case/when语句实现分页查询和分类排序的示例

一、需求背景 查询我发起的以及被邀请的工单列表&#xff0c;要求分页查询&#xff0c;排序的具体要求是&#xff1a; 先按状态排序&#xff0c;未处理的排前面再按处理人排序&#xff0c;被邀请的排前面&#xff0c;自己发起的排后面最后按修改时间倒序 处理状态包括三种&a…

云尘靶场-Tr0ll-vulhub

直接fscan扫描 发现这里有一个ftp 我们等等看 首先去nmap扫描端口 nmap -A -p- 172.25.0.13 --unprivileged 这里使用wsl ftp ssh 和80 然后我们继续继续目录扫描 dirb 出来没什么用处 所以我们继续去看 流量包 流量包分析 首先看tcp ->分析->追踪流 这里是f…

什么是Kubernetes RBAC?为什么需要它?

目录 什么是Kubernetes RBAC? 如何启用Kubernetes RBAC 1.服务帐户 2.角色和集群角色 3.角色绑定和集群角色绑定 Kubernetes RBAC的好处 1.适当的授权 2.职责分离 3.100%遵守法规 Kubernetes RBAC的缺点 企业软件开发工具 什么是Kubernetes RBAC? 当组织开始走上…

英国最值得参观的十大博物馆介绍

目前&#xff0c;到英国从事访问交流及博士后研究的学者越来越多&#xff0c;而英国一向以厚重的历史人文以及精彩绝伦的艺术著称&#xff0c;在这样一个有着浓厚历史沉淀的国家&#xff0c;自然最不缺的就是博物馆了&#xff0c;在学习工作的闲暇&#xff0c;可以去参观体验一…

C语言求解有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?

完整代码&#xff1a; /*有一对兔子&#xff0c;从出生后第3个月起每个月都生一对兔子&#xff0c;小兔子长到第三个月后每个月 又生一对兔子&#xff0c;假如兔子都不死&#xff0c;问每个月的兔子总数为多少&#xff1f; 程序分析&#xff1a; 兔子的规律为数列1,1,2,3,5,8…

AI智能公文写作助手“文山会海“

公文写作痛点 没思路&#xff0c;公文写作无从下手公文类型繁多&#xff0c;一时难以全面掌握公文内容组织难度大&#xff0c;不易清晰、有逻辑的进行表达时间紧任务急&#xff0c;往往需要在有限的时间内完成大量写作工作反复修改优化&#xff0c;需满足更多新要求&#xff0…

WordPress 企业一号wp主题企业建站模板V1.2.2开心版

模板简介&#xff1a; 企业一号是一款由主题巴巴团队原创设计开发的WordPress企业主题。这款主题配备了强大的可视化、模块化的页面设计功能&#xff08;页面构建器&#xff09;&#xff0c;让您通过添加不同的设计模块和配置模块选项就可以设计出各种丰富多彩的页面。主题还集…

信号发送与处理-上

问题 按下 Ctrl C 后&#xff0c;命令行中的前台进程会被终止。为什么&#xff1f;&#xff1f;&#xff1f; 什么是信号&#xff1f; 信号是一种 "软件中断"&#xff0c;用来处理异步事件 内核发送信号到某个进程&#xff0c;通知进程事件的发送事件可能来自硬件…

③【操作表数据】MySQL添加数据、修改数据、删除数据

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ MySQL添加数据、修改数据、删除数据 &#x1f…

渗透测试学习day2

文章目录 连接靶机靶机&#xff1a;Fawn 解题过程Task 1Task 2Task 3Task 4Task 5Task 6Task 7Task 8Task 9Task 10Task 11Task 12 总结 连接靶机 详细过程可参考day1 靶机&#xff1a;Fawn 难度&#xff1a;very easy &#xff08;ftp服务的靶机&#xff09; 解题过程 T…

单链表(1)

前面的顺序表是顺序存储&#xff08;类似于数组&#xff09;&#xff0c;下面的链表是链式存储。物理是否相邻就看其存储地址&#xff08;格子&#xff09;是否相邻挨在一起。逻辑是否相邻看其前驱后继。 例如上面3个数据 如果存在顺序表中&#xff0c;那就是100的后继是200,2…

网络测试工具—— iperf2 安卓APK 下载 及简单使用

网络测试工具—— iperf2 安卓APK 下载 及简单使用 前言一、iperf2是什么&#xff1f;二、使用步骤附上help中命令截图翻译总结 前言 项目上有一款安卓车机加载局域网图片加载非常慢&#xff0c;所以需要测试一个安卓车机设备的带宽&#xff0c;经过调研后使用到了iperf2。 一…

【Android】Debug时禁用主线程ANR限制

ANR全称Application Not Response&#xff0c;指主线程超过5s无响应&#xff0c;应用会自动退出 由于这个线程&#xff0c;如果我们给主线程加了断点&#xff0c;就会触发ANR&#xff0c;导致调试时应用退出 这样调试起来会非常麻烦&#xff0c;其实对于Debug应用&#xff0c…

解决springboot整合websocket、redis、openfeign,redisTemplate,openfeign的类无法注入的问题

在部分业务中&#xff0c;我们需要使用长连接&#xff0c;我们可以使用http长连接或者websocket&#xff0c;在springboot作为后端的框架中&#xff0c; 可以借用的技术是&#xff08;netty&#xff0c;websocket&#xff09; 版本如下 软件版本号jdk21springboot 3.1.5 spri…

企业微信开启接收消息+验证URL有效性

企业微信开启接收消息验证URL有效性 &#x1f4d4; 千寻简笔记介绍 千寻简笔记已开源&#xff0c;Gitee与GitHub搜索chihiro-notes&#xff0c;包含笔记源文件.md&#xff0c;以及PDF版本方便阅读&#xff0c;且是用了精美主题&#xff0c;阅读体验更佳&#xff0c;如果文章对…

[极客大挑战 2019]Upload 1

题目环境&#xff1a; 根据题目和环境可知此题目是一道文件上传漏洞 编写一句话木马脚本<?php eval($_POST[shell]);?>将脚本文件更改为jpg图片文件我这里是flag.jpg上传文件并burpsuite抓包Repeater重放 报错一句话木马里面有<?字符 换一种一句话木马继续编写木马…

iOS实现代码混淆

​ 目录 前言 ipaguard界面概览 ipaguard启动界面 ipaguard代码混淆界面 资源文件混淆界面 重签名界面 前言 本文章向大家介绍iOS实现代码混淆&#xff0c;主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项&#xff0c;具有一定的参考价值&#xff0c;需…