这里包涵了文本处理中常见的一些常见问题编码问题,代码示例使用python3。文末给出了一些工作中遇到的坑及其解,这部分会不定期更新。

Unicode与Utf-8/16

Unicode: 通用字符编码,为了解决不兼容编码格式应运而生。用抽象的方式(数字)来处理字符,而将视觉上的演绎工作留给其他软件处理。
Utf-8/Utf-16: 为了解决Unicode编码在网络传输的问题而产生,Utf-8就是8位8位地传数据,而Utf-16就是每次传16位。这类编码都是变长的以节省空间和流量。以Utf-8为例,其储存一个英文字母用到的位数是unicode的一半。

Unicode 是信源编码,目的是将Unicode字符集给数字化。
Utf-8/16 是信道编码,目的是为了更好的传输和存储。

Linux系统中,file命令可以用来查看文件的编码,iconv则可以将文件的编码进行转换并输出到标准输出STDOUT。

file foo.md
foo.md: Utf-8 Unicode text   
#下面的代码将macroman编码的macroman.txt文件转换为utf-8编码并输出到utf-8.txt
iconv -f macroman -t utf-8 macroman.txt > utf-8.txt

Python中我们可以通过codecs模块来读写不同编码的文件,或者使用str.decode,以读入GBK编码为例:

import codecs 
with codecs.open('foo.txt', encoding='GBK') as f:
    print(f.readlines()) 

with open('foo.txt') as f:
    print(f.readlines().decode('GBK'))

CR和LF,还有CRLF

CR(Carriage Return)就是回车,LF(Line Feed)也就是换行。我们平时来讲貌似这两个是同一个概念,但是这里为啥要分开呢?
追本溯源,早在计算机还没诞生的时候,电传打字机在打完一行换行的时候需要0.2s,这0.2秒的时间里传入的字符会丢失,这样就给手快的打字员造成很大苦恼,设计人员就规定在每行结尾时还需要加上两个字符,也就是回车CR(把打印头移动到左边界)和LF(打印头下移一行)。这样打字员的强迫症就得到了医治。
到了计算机时代,由于初期存储比较贵,所以很多人觉得行末用一个字符表示换行就行啦,于是就产生了分歧:Unix(Linux)系统中行末只有换行LF(\n),Win家的结尾则是换行+回车LFCR(\n\r),水果家则只有回车CR(\r)。因为行末符号不一样,所以不同的系统的文本文件打开时可能出现错位,例如Unix/Mac的文件在Win下直接打开,所有文字都变成一行。好在现在许多编辑器会自动识别出行末符号是哪一种,并做相应的调整。不过如果你打开文本是发现行错位不妨检查一下是不是CR/LF的问题。

URL/HTML编码和SQL转义

在处理文本时我们经常会遇到URL编码和HTML编码,特别是在HTML抽取中。

URL编码

一个很简答的例子,我们用Google搜索Just a sample, question: 1+1=?,我们会发现地址栏后面的字符是Just+a+sample,+question:+1%2B1%3D?,这就是对搜索内容进行了URL编码。python3中我们可以用urllib.parse来处理该问题:

import urllib.parse
urllib.parse.quote('Just a sample, question: 1+1=?')
# "Just+a+sample,+question:+1%2B1%3D?"  
urllib.parse.unquote_plus('www.google.com.hk/?gws_rd=cr#newwindow=1&safe=strict&q=Just+a+sample,+question:+1%2B1%3D?')
# "'www.google.com.hk/?gws_rd=cr#newwindow=1&safe=strict&q=Just a sample, question: 1+1=?'"
# 这里unquote_plus()的话就会把'+'解析为空格,使用unquote()则只转换%XX符号,'+'还保留为'+'

HTML编码

我们在提交Web表单内容时,为了防止恶意攻击者的破坏例如提交可执行的js代码,我们通常需要对用户提交内容进行HTML编码,它会把一些字符替换成某些实体引用。在python3中,我们使用html模块来解决这类编码问题。

import html
s = '<script> 蛤蛤 </script>'
# 编码
encoded_s = cgi.escape(s).encode('utf-8')
print(encoded_s)
# b'&lt;script&gt; \xe8\x9b\xa4\xe8\x9b\xa4 &lt;/script&gt;'

# 解码
print(html.unescape(encoded_s.decode('utf-8')))
# <script> 蛤蛤 </script>

SQL转义

为了防止SQL注入攻击,对于一些数据库内容需要做转义,通常来说是",',;,--/*前面加转义字符\,需要注意的是,由于python字符串本身的转义,所以\需要多重转义,例如"sql转义为\",python的string里还要再转义也就是\\\",或者在字符串前面加’r’。下面以代码说明:

print('\"')
#Output: "
print('\\\"')
#Output: \"
print(r'\"')
#Output: \"

遇到过的坑

invalid start byte

是在使用pandas的read_csv时出现'utf-8' codec can't decode byte 0x89 in position 7: invalid start byte,查看了一下,文件是mac上创建的,我是在win的环境下读取。该问题是解析文件时使用了utf-8编码但是有部分编码无法解析,换了win上另一种编码pd.read_csv('sample.csv', encoding='cp1252')解决问题。在出现这种情况时,不妨换几种常见编码试试看,如encoding='latin1', encoding='iso-8859-1'