Python可以高效处理文件,配合正则模块,可以轻松实现提取文本文件关键信息、转换内容格式等功能,现将Python文件操作相关内容以及正则模块使用总结如下。
文章目录
一、文件的类型与编码
(一) 文件类型
计算机中所有文件都以二进制形式保存在存储介质上,文件类型的区分是逻辑上的区分,是由于文件内容编码逻辑不同导致的,文件可以分为文本文件和二进制文件:
1.文本文件
文本文件是基于字符编码的文件,编码格式包括ASCII、UTF-8等,可以用编辑器直接打开查看,如Python源码、TXT文件。
比如100在ASCII编码的文本文件中会保存为三个字符’1’、‘0’、‘0’,占用三个字节,10000保存为五个字符’1’、‘0’、‘0’、‘0’、‘0’,占用五个字节,每个字符按照编码规则解码就能正常查看文件内容。
2.二进制文件
二进制文件是基于值编码的文件,提供给专用软件使用,根据与专用软件之间的协议规定值的具体含义,不能直接用编辑器打开查看,如图片、音视频文件、Word文档。
比如JPEG文件内容是按照段来存储的,每个段起始两个部分是段的标识: 0xFF+段类型,比如文件头: FFD8,文件尾: FFD9,不同段对应的字节具有不同含义,文件必须通过图片编辑器查看,如果按照文本文件解码就会出现乱码。
(二) 编码方式
编码方式是针对字符而言的,所以只针对文本文件,常用编码方式及区别如下:
- ASCII:单字节编码,使用7位(128个字符,基础ASCII)或8位(256个字符,扩展ASCII)来表示所有的英文字符、数字、标点符号以及特殊字符
- GB2312:双字节编码,对文件中任意一个图形字符都采用双字节编码,是最早制定的汉字编码,是ASCII汉字编码的扩充,但是不包含繁体字,兼容ASCII编码
- Big5:双字节编码,台湾、香港等地区使用的繁体汉字的编码,与GB2312不兼容,兼容ASCII编码
- GBK:双字节编码,兼容GB2312并在其基础上进行扩展,收录了Big5中所有的繁体字以及更多的汉字字符和符号
- GB18030:单字节、双字节、四字节分段编码,是GBK的取代版本,在GBK基础上增加了CJK统一汉字扩充A和扩充B的汉字,兼容GBK
- Unicode:又称为万国码,准确说它是一个字符集,默认以UCS-2标准,用两个字节为每种语言中的每个字符设定了统一并且唯一的编码,具体包括UTF-8、UTF-16、UTF-32三种编码实现方式,这三种编码方式都可以表示目前世界上所有的字符
- UTF-8:变长编码,以一个字节为基本编码单位,所有字节用一个到四个字节不等,虽然可以表示所有的中文字符,但是**不兼容GBK、GB2312,**空间利用率高,灵活性强,应用广泛,兼容性较好
- UTF-16:变长编码,以两个字节为基本编码单位,所有字符用两个或者四个字节表示
- UTF-32:以四个字节为基本编码单位,所有字节都用四个字节表示,空间利用率差
- ANSI:并不是某一种特定的字符编码,而是在不同的系统中,ANSI表示不同的编码。英文系统中ANSI默认是ASCII编码,简体中文系统中ANSI默认是GB2312编码,繁体中文系统中ANSI默认是Big5编码,不同系统的ANSI编码不兼容
一张图解释,简单明了:
注意Python2/3编码兼容性问题:
Python2默认用ASCII编码,Python3默认使用UTF-8编码,如果Python2中使用UTF-8解析文件,则需要在文件的第一行 (在Shebang之后) 添加编码说明,如:
#!/usr/bin/python
# -*- coding: utf-8 -*-
但是即便添加了coding注释,Python2解释器在读取字符串时仍然是一个一个字节读取,如果要以正确的UTF-8格式读取,则要在字符串前加一个u
,这样类似以下代码就不会出错了:
#!/usr/bin/python
# -*- coding: utf-8 -*-
hello_str = u"Hello世界"
for c in hello_str:
print(c)
二、文件操作与管理
(一) 文件操作函数/方法
文件的操作主要是打开文件、读写文件、关闭文件,对应一个函数和三个方法:
函数/方法 | 作用 | 返回值 |
---|---|---|
open(name[, mode[, encoding]]) | 函数,用于打开文件,指定的文件名区分大小写,打开模式默认为只读r ,编码默认为系统平台编码,如果文件存在则返回文件对象,不存在则抛出异常 | IO |
read([count]) | 将文件内容读取到内存,默认一次性读入并返回文件的所有内容,也可以指定读取count个字节 | str |
readline([count]) | 读取并返回当前文件指针所在行的字符串,读取大文件时常用,可以指定读取count个字节,最多只能读取到单行的字符 | str |
readlines([count]) | 读取并以列表方式返回文件所有的行,可以指定读取count个字节,字节数不足一行的按一行内容返回 | List(str) |
write(str) | 将指定字符串写入到文件,返回字符串的长度 | int |
writelines(List[str]) | 将指定字符串列表写入到文件 | None |
close() | 关闭文件,如果忘记关闭文件,会造成系统资源消耗,而且会影响后续对文件的访问 | None |
理解文件指针
-
文件指针用于标记从哪个位置开始读取数据,第一次打开文件时,文件指针会指向文件的开始位置
-
文件方法执行后,文件指针会随之变化,比如:执行read方法后,文件指针会移动到文件的末尾,此时如果再执行一次read方法则不会读到任何内容;执行readline方法后,文件指针会移动到下一行的位置,从而可以迭代读取文件内容
(二) 文件打开方式
用open函数打开文件可以有多种操作方法:
file = open(name[, mode[, encoding]])
mode具体打开模式为:
- r:只读方式打开文件,不存在文件则会抛出异常,函数的缺省设置
- w:只写方式打开文件,如果存在则会被覆盖,不存在则新建文件
- a:追加方式打开文件,如果该文件已存在,文件指针会放在文件的结尾,如果不存在,创建新文件写入
- r+:以读写方式打开文件,文件指针放在文件开头,不存在则抛出异常
- w+:以读写方式打开文件,如果文件存在则会被覆盖,文件不存在则创建新文件
- a+:以读写方式打开文件,如果该文件已存在,文件指针会放在文件的结尾,如果不存在,创建新文件写入
encoding可以是ansi、gbk、gb2312等上述所有的编码方式
注意:频繁移动文件指针会影响读写效率,开发中大多时候以只读、只写方式来读取
(三) 文件的管理操作
Python中,如果要实现文件的创建、重命名、删除、改变路径、查看目录内容等,需要导入 os
模块,具体方法如下:
方法 | 作用 | 返回值 |
---|---|---|
os.rename(src_file, dst_file) | 将src_file文件名重命名为dst_file | None |
os.remove(file) | 删除file文件 | None |
os.listdir([dir]) | 返回目录列表,默认为当前目录 | List[str] |
os.mkdir(dir[, mode]) | 以mode权限创建路径为dir的目录,mode默认为0777(Windows系统忽略mode值) | None |
os.rmdir(dir) | 删除dir指定的空目录,如果目录非空则抛出异常 | None |
os.getcwd() | 返回当前目录 | str |
os.chdir(dir) | 改变当前工作目录为dir | None |
os.rmdir(dir) | 删除dir指定的空目录,如果目录非空则抛出异常 | None |
os.path.isdir(dir) | 判断某dir是否为目录 | bool |
os.path.isfile(file) | 判断file是否为文件 | bool |
三、正则模块的使用
正则表达式描述了一种字符串匹配的模式,可以用来检查一个字符串是否含有匹配的子串,从而将其替换或者取出符合某个条件的子串等,在文件处理时常会用到。
re
模块使得Python可以通过Perl风格的正则表达式匹配字符串,模块中有两个重要对象,两种对象有不同的方法,注意区分。
(一) 正则 (Pattern) 对象: RegexObject
正则 (Pattern) 对象,由re.compile函数返回的对象,其方法和re模块中的函数相对应,如以下两种方式等同:
# 方法一:
re.match(r"\d*@", "12121212@qq.com")
# 方法二:
pattern = re.compile(r"\d*@")
pattern.match("12121212@qq.com")
将正则表达式编译成正则对象的意义在于,作为对象更加方便调用和修改,正则对象的具体方法如下:
方法 | 作用 | 返回值 |
---|---|---|
match(string[, pos=0[, endpos=len(string)]]) | string为用于匹配的字符串,pos/endpos为起始位置/结束位置索引,默认从字符串起始位置开始匹配,匹配到则返回匹配对象,不成功返回None | MatchObject / None |
search(string[, pos=0[, endpos=len(string)]]) | string为用于匹配的字符串,pos/endpos为起始位置/结束位置索引,默认搜索整个字符串并返回第一个成功的匹配对象,不成功返回None | MatchObject / None |
findall(string[, pos=0[, endpos=len(string)]]) | string为用于匹配的字符串,pos/endpos为起始位置/结束位置索引,默认搜索整个字符串并返回所有成功匹配的子串列表,不成功返回空列表 | List[str] |
split(string[, maxsplit=0]) | 按照匹配的子串将string分割后,并返回子串列表,maxsplit用于指定最大分割次数,不指定默认全部分割 | List[str] |
sub(repl, string[, count=0]) | 字符串string中的匹配项替换成repl,并返回替换后的字符串 | Str |
(二) 匹配 (Match) 对象: MatchObject
匹配 (Match) 对象,由re模块的函数或正则对象的方法如match、search返回的对象,可进一步处理正则匹配到的内容,其方法如下:
方法 | 作用 | 返回值 |
---|---|---|
group([num=0]) | 返回第num个分组匹配的字符串,num默认为0,group() 获得整个匹配的子串 | str |
groups() | 返回由所有分组匹配的子字符串组成的元祖 | tuple[str] |
start([num=0]) | 返回第num个分组匹配的子串在整个字符串中的起始索引,num默认为0,start() 获得匹配的子串在整个字符串中的起始索引 | int |
end([num=0]) | 返回第num个分组匹配的子串在整个字符串中的末尾索引,num默认为0,end() 获得匹配的子串在整个字符串中的末尾索引 | int |
span([num=0]) | 返回 (start(num), end(num)) | tuple |
(三) Re模块常用函数
函数 | 作用 | 返回值 |
---|---|---|
re.compile(pattern[, flags]) | 编译正则表达式,生成一个正则对象 | RegexObject |
re.match(pattern, string[, flags=0]) | 从字符串的起始位置匹配一个模式,匹配成功返回匹配对象,不成功返回None | MatchObject / None |
re.search(pattern, string[, flags=0]) | 搜索整个字符串并返回第一个成功的匹配对象,不成功返回None | MatchObject / None |
re.findall(pattern, string[, flags=0]) | 搜索整个字符串并返回所有成功匹配的子串列表,不成功返回空列表 | List[str] |
re.split(pattern, string[, maxsplit=0[, flags=0]]) | 按照匹配的子串将string分割后,并返回子串列表,maxsplit用于指定最大分割次数,不指定默认全部分割 | List[str] |
re.sub(pattern, repl, string[, count=0[, flags=0]]) | 将字符串string中的匹配项替换成repl,并返回替换后的字符串 | Str |
上述函数中pattern是匹配的正则表达式,string是要进行匹配的字符串,flags标志位用于控制正则表达式的匹配方式,具体如下:
re.I
:使得大小写匹配不敏感re.M
:使得^
和$
可以匹配到每行的开头和结尾re.S
:使.
可以匹配到包括换行符\n
的所有字符re.X
:使得正则匹配字符串中tab、换行符以及注释符#
后的内容都会被忽略,这样易于正则匹配字符串的组织和解释
通过或符号|
来设置多种模式共存,如re.I | re.M
正则表达式规则
1.普通字符(包括字母和数字等)表示他们自身;
2.特殊字符 表示特殊含义,常用特殊字符如下表所示:(如果需要使用特殊字符作为普通匹配字符,则需要\
转义)
正则表达式 | 匹配内容 |
---|---|
. | 除了换行符\n 以外的所有字符 |
^ | 字符串的起始位置 |
$ | 字符串的结束位置 |
[…] | 单独标识一组字符,[abc]匹配字符’a’、‘b’和’c’,[a-zA-Z]匹配任何大小写字母 |
[^…] | 不在这组字符以外的其他字符 |
re* | 匹配0个或多个re表达式,默认贪婪模式 |
re+ | 匹配1个或多个re表达式,默认贪婪模式 |
re? | 匹配0个或1个re表达式,非贪婪方式 |
re{n} | 精确匹配n个re表达式 |
re{n,} | 匹配n个或多于n个re表达式 |
re{n,m} | 匹配n到m个re表达式 |
re1 | re2 |
(re) | 对正则表达式进行分组,分组可作为一个整体使用,如(abc){1,2} |
\1…\9 | 匹配与第1-9个分组相同的内容,如(\d)abc\1,则会匹配1abc1、2abc2等 |
\w | 匹配字母数字及下划线,等价于[a-zA-Z0-9_] |
\W | 匹配非字母数字及下划线[^a-zA-Z0-9_] |
\d | 匹配任意数字,等价于[0-9] |
\D | 匹配任意非数字,等价于[^0-9] |
\s | 匹配任意空白字符,等价于[ \f\n\r\t\v] |
\S | 匹配任意非空白字符,等价于[^ \f\n\r\t\v] |
\b | 匹配单词的边界,也就是指单词和空格间的位置,例如 ‘er\b’ 可以匹配 “never” 中的 ‘er’ |
\B | 匹配非单词的边界,例如 ‘er\B’ 不可以匹配 “never” 中的 ‘er’,但可以匹配 “perfect” 中的 ‘er’ |
注意:由于部分正则表达式包含反斜杠 (如: \w\d\s
),所以最好使用原始字符串来表示,即在正则表达式前加上r
,r"\d{1,2}"
等价于"\\d{1,2}"
。