但是


我们切割文件后,要怎么样无须再动手去完善呢?要知道一个大脚本切割下来可能有10份以上,完善每个脚本的开头和收尾,还要标记这是第几个脚本,是一件非常繁琐耗时的事情。所以,安老师绝对不允许这样的事情出现,老铁们也一样,对不对!如果能一次性切割完成,究竟能带来什么好处呢?

1)拿来主义,省事,切割完成,直接用。

2)可标记脚本个数,便于管理和安排计划。

3)降低人为编辑错误的可能性。



            大纲            

1.  切割程序演示

2.  使用re模块

3.  "一劳永逸"切割


01

切割程序演示

1.  演示场景

本次默认Windows切割文件,演示的图片如下


1.1 原文件

文件路径  F:\python\py自动化运维\公众号\5 python切割文件\test.sql

文件首部(图一,总1416行,此sql脚本为数据库劈2016年的月分区。)

python切割文件,我的闪亮"大马士革刀"表示不喜欢补刀!_java


文件尾部(图二)

python切割文件,我的闪亮"大马士革刀"表示不喜欢补刀!_java_02


2  使用程序演示

使用程序将上节推文的sql文件进行切割,可以看到该程序需要输入的信息有以下内容:

1)输入文件的绝对路径

2)每个新文件所需的行数

3)文件内容首部添加的内容

4)文件尾部添加的内容

5)生成文件的路径


02

使用re模块

1  正则表达式


2  利用re库处理正则表达式

正则表达式虽然本身比较复杂,但是python使用re标准库来处理它时却非常简单。


2.1  re库演示

re模块包含了正则表达式相关的函数、标志和一个异常。若希望在re模块在匹配时忽略字符的大小写可以添加标志,flags=re.IGNORECASE


03

"一劳永逸"切割



1  高级切割代码

 1# -*- coding: GB18030 -*-
2import re
3import os
4import time
5
6def splitByLineCount(qianzhuitext,houzhuitext,filenamepath, count):
7    global FILENAMELIST
8    FILENAMELIST=[]
9    filename = open(filenamepath, 'r')
10    try:
11        bufferline = []
12        num = 1
13        filename.seek(0)
14        for line in filename:
15            bufferline.append(line)
16            if len(bufferline) == count and num >=1 :
17                num = mkSubFile(qianzhuitext,houzhuitext,bufferline,filenamepath,num)
18                bufferline = []
19        if len(bufferline) != count and num >=1 :
20            num = mkSubFile(qianzhuitext,houzhuitext,bufferline,filenamepath,num)
21    finally:
22        filename.close()
23        return FILENAMELIST
24
25def mkSubFile(qianzhuitext,houzhuitext,bufferline,filenamepath,num):
26    [desfilename, typename] = os.path.splitext(filenamepath)
27    nfilename = desfilename + '_' + str(num) + typename
28    print('make file: %s' % nfilename)
29    scriptfile = open(nfilename, 'w')
30    try:
31        qianzhuitextnew = getnewqianzhui(qianzhuitext,num)
32        scriptfile.writelines(qianzhuitextnew)
33        scriptfile.writelines(bufferline)
34        scriptfile.writelines(houzhuitext)
35        return num + 1
36    finally:
37        scriptfile.close()
38        FILENAMELIST.append(nfilename)
39
40def getnewqianzhui(qianzhuitext,num):
41    global qianzhuitextnew
42    qianzhuitextnew=[]
43    f = open(r'F:\python\py自动化运维\公众号\5 python切割文件\qianzhui.txt',"w")
44    f.write(qianzhuitext)
45    f.close()
46    try:
47        fr = open(r'F:\python\py自动化运维\公众号\5 python切割文件\qianzhui.txt',"r")
48        for each in fr:
49            result = re.search(r"\.(log|sql|sh|py)",each.strip())
50            if result is not None:
51                neweach = each.replace(result.group(),"_"+str(num)+result.group())
52                qianzhuitextnew.append(neweach)
53            else:
54                neweach = each.strip() + '\n'
55                qianzhuitextnew.append(neweach)
56        fr.close()
57        return qianzhuitextnew
58
59    except Exception as e:
60        print('原因:', e)
61
62
63if __name__ == '__main__':
64    begin = time.time()
65    filenamepath = r'F:\python\py自动化运维\公众号\5 python切割文件\test.sql'
66    qianzhuitext= 'vi p_test_pmax_20190314.sh \n '\
67                  'sqlplus "/ as sysdba" << EOF \n '\
68                  'spool p_test_pmax_20190314.log \n '\
69                  'set time on \n'\
70                  'set timing on \n'\
71                  'set echo on\n \n'
72    houzhuitext = '\n<< EOF \n'\
73                  'exit \n'\
74                  '! \n '
75    splitByLineCount(qianzhuitext,houzhuitext,filenamepath, 500)
76    end = time.time()
77    print('time is %d seconds ' % (end - begin))


1.1  重点代码详解

1)执行流

qianzhuitext  每个新文件首部添加的内容

houzhuitext   每个新文件尾部添加的内容

2)第6行  splitByLineCount函数 内容详解请查看上节推文[阅读原文]

3)第25行  mkSubFile函数 内容详解请查看上节推文[阅读原文]

4)第32-34行  主要思路,在新文件内容的基础上添加前文和后文。

5)第40行  getnewqianzhui函数 需要用到qianzhuitext内容和num这两个参数,是因为要重命名脚本文件名,以及spool 新生成的log日志,这两个需要特别标记,有利于我们检查和管理

6)第43行  打开文件记录首部内容,而不是直接使用数组,是因为首部内容可能会比较复杂,程序里首部是一个text框,text框输入什么内容,我们就应该让text框内容完全显示出来。

7)第49行  re.search(r"\.(log|sql|sh|py)",each.strip()),re.search仅仅查找匹配一次,匹配成功返回SRE_Match对象,如果匹配失败则返回None。

8)第50-52行  以"log|sql|sh|py"进行匹配。若匹配成功,则进行replace,如“.sh”被替换成“_1.sh”,并添加到新的qianzhuitextnew数组;若匹配不成功,则将该行直接写入新的qianzhuitextnew数组。

9)第59行  自定义抛出异常。


1.2  高级切割文件优缺点

优点:输入内容,“一劳永逸”。

缺点:代码还不够精简(欢迎老铁们留言完善)。


1.3  执行演示

python切割文件,我的闪亮"大马士革刀"表示不喜欢补刀!_java_03


第一个脚本test_1.sql和原文件的开头一样,而且包含输入的内容,文件名和log均标记了这是第一个脚本,这是成功切割的第一步。

python切割文件,我的闪亮"大马士革刀"表示不喜欢补刀!_java_04


第一个脚本test_1.sql的尾部,添加的尾部内容正确。如此类推,test_2.sql和test_3.sql也是没有问题的。

python切割文件,我的闪亮"大马士革刀"表示不喜欢补刀!_java_05


第三个脚本test_3.sql文件的sql语句只有416行,并且包含尾部内容。整个切割过程,数据和行数并没有丢失,完美实现了一次切割,无须补刀!

python切割文件,我的闪亮"大马士革刀"表示不喜欢补刀!_java_06


总结

其实本文"一劳永逸"切割代码,已经是第三个版本了!第一个版本全部使用if判断,第二个版本增加replace函数,第三个版本改用re正则表达式。安老师觉得本文代码还有改进之处。比如,replace函数可以换成re.sub;尽量少使用全局变量等等。

定期迭代产品,可温故而知新。安老师也鼓励老铁们不妨写些坏代码,因为我们编写软件就是为了解决问题,如果编写的代码没有解决问题,一味遵循"好代码"规则,反而会作茧自缚!安老师认为,我们会进步,产品会更新,自己写出程序,实现功能,定期迭代,才是python入门的正确方式!