数字营销 · Web开发 · 基础设施

文本模式与二进制模式打开文件对比

通过 Python 下载图片的实际案例,分析文本模式与二进制模式打开文件的区别,以及 Windows 下换行符转换对二进制文件造成的影响。

问题是在用 Python 抓取网站图片时发现的。代码如下:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import urllib2

img = "https://www.baidu.com/img/bdlogo.png"

imgdata = urllib2.urlopen(img).read()

png = open("logo.png", "w")
png.write(imgdata)
png.close()

代码看起来没有什么问题,但下载下来的图片却已经损坏,无法打开。

对原始图片和下载后的图片进行 hash 对比,发现两者的内容确实发生了变化:原文件大小为 5331 字节,保存后的文件变成了 5345 字节。进一步用二进制查看器分析,发现原文件中的部分 0x0a 前面被自动增加了一个 0x0d。

熟悉 ASCII 控制字符的可能知道:

  • 0x0a:换行(LF)
  • 0x0d 0x0a:回车换行(CRLF)

Windows 和 Unix/Linux 对文本换行符的处理方式不同:

Windows:CRLF  → 0x0d 0x0a
Unix/Linux:LF → 0x0a

因此推测,问题并不是图片下载过程中发生的,而是在保存文件时,Python 按照文本模式处理了数据。在 Windows 下,文本模式会自动进行换行符转换,把 0x0a 转换为 0x0d 0x0a。

对于普通文本来说,这种转换通常没有问题;但 PNG、JPEG、EXE 等二进制文件中的 0x0a 本身就是正常的数据内容,一旦被自动修改,文件自然就可能损坏。

那么,对于二进制数据应该如何处理呢?

Python 的文件打开模式提供了 b 参数,用于表示以二进制模式打开文件,例如:

png = open("logo.png", "wb")
png.write(imgdata)
png.close()

常见的二进制文件模式包括:

rb   以二进制方式读取
wb 以二进制方式写入
ab 以二进制方式追加
rb+ 以二进制方式读写
wb+ 以二进制方式读写并清空原文件

Python 官方文档对 b 模式的解释大意是:在 Windows 上,文本文件和二进制文件存在区别,文本模式下读取或写入文件时,行结束符可能会被自动转换。这种转换对于普通 ASCII 文本通常没有问题,但可能会破坏 JPEG、EXE 等二进制数据。因此,读写二进制文件时应明确使用二进制模式。

上面的例子是写入文件时的问题。如果读取文件,也存在类似情况。

例如,一个使用 Windows 换行符的文本文件:

0d 0a

在 Windows 下使用普通的 "r" 文本模式读取时,运行环境可能会将其转换为:

0a

而在二进制模式下读取:

f=open("test.txt", "rb")

则可以直接获得文件原始的字节数据,不进行换行符转换。

因此可以简单理解为:

文本模式处理的是“文本内容”,运行环境可能会参与编码或换行符转换;二进制模式处理的是“原始字节”,文件中的数据会尽可能保持原样。

对于图片、音频、视频、压缩包、可执行文件等二进制数据,应始终使用 b 模式打开。

虽然在 Unix/Linux 平台上,文本模式和二进制模式通常不会产生 Windows 下这种换行符转换问题,但为了保证程序的跨平台兼容性,处理二进制文件时仍然应该明确使用 rb、wb 等模式。

评论0

欢迎分享你的看法,也欢迎补充不同的实践经验。