Python 中的中文相关处理的深入解析(中文报错、中文路径找不到等情况分析)

ps:这片文章写得很细,很基础,适合没有基础的python学者阅览。都是些个人开发过程中遇到的实际问题,如有错误的地方请指出,O(∩_∩)O谢谢

补一下python环境
在这里插入图片描述

python实现的功能:

1.python1:对比两个本地文件夹里的内容,输出差异文件(新增、删除、差异)信息
2.python2:根据差异文件和远端的本地文件进行对比解析,更新资源文件

开发过程中遇到的问题

其实我就是要写一个基于bsdiff的差量更新的python脚本。因为在这里用到了很多路径、中文字符串、编码等问题,搞的我一头雾水。现在功能虽然完成了,但是过程很痛苦,希望将这些从痛苦中得到的收益记录下来,以便日后不会重蹈覆辙。具体问题如下:
我会通过以下几个具体的代码案例去进行说明:

  1. 中文字符串的赋值报错(为什么要在首行声明为utf-8格式?)
  2. os.path.exists()函数传入的路径明明存在,返回的结果却差强人意
  3. os.path.join()函数进行路径拼接的时候经常乱码或者报错
  4. print(str); print str; print(str1, str2)预期的结果不一样

(1) 中文字符串赋值报错(为什么要在首行声明为utf-8格式?)

temp = "中文"

果你的代码只有这一行的话,那么肯定会报错
File “D:/PycharmProject/untitled/test.py”, line 1
SyntaxError: Non-ASCII character ‘\xe8’ in file D:/PycharmProject/untitled/test.py on line 1, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details

这里系统提示’\xe4’不是ASCII字符集,至于为什么报这个错,在解决这个疑惑之前我们首先要了解一下,python字符串运行的机制。

概括如下:
在python中,编码解码其实是不同编码系统间的转换,默认情况下,转换目标是Unicode(至于为什么点这里),即编码unicode→str,解码str→unicode,其中str指的是字节流,而str.decode是将字节流str按给定的解码方式解码,并转换成utf-8形式,u.encode是将unicode类按给定的编码方式转换成字节流str。注意调用encode方法的是unicode对象,生成的是字节流;调用decode方法的是str对象(字节流),生成的是unicode对象。若str对象调用encode会默认先按系统默认编码方式decode成unicode对象再encode,忽视了中间默认的decode往往导致报错

回过头来我们再来看’\xe4’这个字符,就可以理解了:这里python是把"中文"这个字符串用mbcs(mbsc是一个字符集的集合,想了解的话自行百度,或者点这里mbsc是什么)中的一种ascii进行decode,然而ascii里并没有找到字符串映射的数值,所以报错。

然而,这里引出的mbsc是怎么来的呢?答案是python默认的选择:

import sys
sys_code = sys.getfilesystemencoding()
print (sys_code)   

打印的结果就是mbsc,也就是说我们如果不指定decode的编码格式,python内部就会自己指定一个字符集用来decode到unicode,然而其结果一般不是我们想要的,所以我想当然的认为应该把代码修改成这样:

temp = "中文".decode('utf-8')

但是!结果依然报错!而且报错打印一模一样。这是因为python在还没有执行到我们新加的decode(‘utf-8’)时就已经检测到了中文字符串!为了解决这个问题, # -*- coding:utf-8 -*-(写法不拘于这一种)应运而生,此代码就是告诉python:Hi python! 你在运行代码的时候,要把这个文件里的内容当成utf-8的格式来进行decode;有了这行代码python就会把默认的ascii格式修改成utf-8,所以我们也没有必要显示的告诉python进行decode(‘utf-8’)操作了:

# -*- coding:utf-8 -*-
temp = "中文"

到此一个python的中文赋值讲解结束。

额外说明(于本章内容关系不大):申明了UTF-8编码并不意味着你的.py文件就是UTF-8编码的,必须并且要确保文本编辑器正在使用UTF-8 without BOM编码:

在这里插入图片描述

这是我从网上找的一个个人觉得比较能诠释python字符集流程的图了

(其实还看到一个更合适的忘记保存了。。。)
在这里插入图片描述

(2) os.path.exists(path)路径明明存在,得到的结果却是False

首先我们来看一个例子:

# -*- coding:utf-8 -*-
import os

path1 = "E:\\中文路径测试"
if os.path.exists(path1):
    print("path1文件存在")
else:
    print("path1文件不存在")

path2 = "E:\\中文路径测试".decode('utf-8')
if os.path.exists(path2):
    print("path2文件存在")
else:
    print("path2文件不存在")
#打印结果:
path1文件不存在
path2文件存在

对于这个现象我的理解是python把’path1’解释成了一个非unicode的编码,所以找不到这个路径,下面是我的验证

print(repr(path1))	#打印:'E:\\\xe4\xb8\xad\xe6\x96\x87\xe8\xb7\xaf\xe5\xbe\x84\xe6\xb5\x8b\xe8\xaf\x95'
print(repr(path2))	#打印u'E:\\\u4e2d\u6587\u8def\u5f84\u6d4b\u8bd5'

大家都知道repr()转化为供解释器读取的形式,在这里的意思就是把字符串翻译成python内部的编码格式可以看出path1翻译过来的并不是unicode的格式所以找不到该路径

(3) os.path.join()函数进行路径拼接的时候经常乱码或者报错

这里出现问题的原因本质上是和问题2是一样的,所以这里的解决方案是将os.path.join()再封装成一个函数,将传入的参数先进行decode再进行拼接

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

def joins(str1, str2):
	return os.path.join(str1.decode('utf8'), str2.decode('utf8'))

但是这里可能出现的问题是传入的str1或者str2本身就已经是unicode的格式再执行decode的操作就会报错,我们优化一下代码,写成下面这样:

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

def utf8_to_unicode(src):
	return src.decode('utf8')

def unicode_path(path_):
	if isinstance(path_, unicode):
		return path_
	return utf8_to_unicode(path_)

def joins(str1, str2):
    return os.path.join(unicode_path(str1), unicode_path(str2))

(4) print()打印的预期结果不一样

我们直接来看一下代码#后面的批注是实际运行的打印结果

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

str1 = "D:\\中文测试1"
str2 = "D:\\中文测试2"
print(str1) #D:\中文测试1
print str1  #D:\中文测试1
print(str2) #D:\中文测试2
print str2  #D:\中文测试2
print(str1, str2)   #('D:\\\xe4\xb8\xad\xe6\x96\x87\xe6\xb5\x8b\xe8\xaf\x951', 'D:\\\xe4\xb8\xad\xe6\x96\x87\xe6\xb5\x8b\xe8\xaf\x952')
print str1, str2    #D:\中文测试1 D:\中文测试2

如果结果和你的预期一样,这里你就可以跳过了。反正,和我想的是不一样。我们先去深入了解一下print和print()。这里我查阅了python的官方文档,为了方便大家理解,我这里直接将中文版的解释截图了下来,下图为官网直译:在这里插入图片描述有些翻译不全大家也不用在意,重点看一下注释里的内容。翻译过来的意思就是:
该print()函数通常不能作为内置功能使用,因为名称print被识别为print语句。 要禁用该print语句并使用print()函数,请在模块顶部使用此future语句。

python2版本里根本就没有print()函数,它是一个语句!!!

至于什么是语句和函数的定义这个不重要,需要的童鞋自行百度,我们只要知道python2没有print()函数,想使用官方定义的标准函数需要升级到python3.5。很显然版本变更时大忌,所以官方提供了一个中间解决方案:

from __future__ import print_function

你如果想使用python的标准内置函数需要加上,此语句的解析可以参考这里

python2版本里print()将括号里的内容当成元组处理

python2的版本里print可带括号也可以不带括号,带括号的情况有两种:

  1. print(str1) 参数只有一个的情况下,此时的参数类型就是str(字符串)
  2. print(str1, str2) 参数大于1个的情况下,python就会把str1和str2当成元组

然而对于下面这条语句的打印我还没有研究透彻,但是知道了问题现象在没搞清楚原理之前我们完全可以规避掉 在我写下这几句话的时候,下午茶时间到了,转悠一圈上来发现了一种可能:python会在输出str1和str2的时候将字符串自行dencode(‘utf-8’),然后当成str(字符串)进行打印了!!!天呐。。。心里一万个草泥马跑过(这么简单的原理困扰了我好几天)。

print(str1, str2)   #('D:\\\xe4\xb8\xad\xe6\x96\x87\xe6\xb5\x8b\xe8\xaf\x951', 'D:\\\xe4\xb8\xad\xe6\x96\x87\xe6\xb5\x8b\xe8\xaf\x952')

为了验证这个猜想我们做下测试:

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

str1 = "中文测试1"
str2 = "中文测试2"

print(repr(str1))  #'\xe4\xb8\xad\xe6\x96\x87\xe6\xb5\x8b\xe8\xaf\x951'
print(str1, str2)  #('\xe4\xb8\xad\xe6\x96\x87\xe6\xb5\x8b\xe8\xaf\x951', '\xe4\xb8\xad\xe6\x96\x87\xe6\xb5\x8b\xe8\xaf\x952')

结果和预想完全一致,注意看打印出来的结果都带有’'单引号,说明这是个字符串!
至于不带括号的print就很好理解了可以简单的当成一个for语句来看待。这里就不展开来进行讲解了。
至此,蛋疼的print()就彻底的搞明白了。最后针对print做个小小的总结:

print()的用法总结
  1. 在python里尽量使用print()函数的形式,毕竟不带括号的python在后面的新版本中已经废弃了。
  2. print()在输出中文字符串的时候尽量保证只有一个参数,如果需要多个请确保你知道在python2中python会把它当成元组来处理。
  3. 在使用print()输出多个参数时,建议重写print()函数,避免它的双重定义
  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qqooopp123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值