1. 文件过大(需要进行文件分割)
2. 列异常(列不一致,如原始数据的列为10列,但导出的csv文件有些行是11列,或者4列)
本脚本用于解决此问题。
使用说明:
> python csvtoolkit.py -h
usage: csvtoolkit.py [-h] [-f CSV_FILENAME] [-d DELIMITER_CHAR]
[-n SPLIT_FILE_NUMBERS] [-e OUTPUT_ENCODING]
本脚本用来分割处理csv文件,其中解决了csv文件的列异常问题。
使用示例如下:
python csvtoolkit.py -f "2013.csv" -n 10 -e "utf-8"
示例说明:
要分割的csv文件: 2013.csv
分割成的文件个数:10
分割后的文件使用的字符编码:utf-8
分割文件的字符编码,在简体中文系统中推荐使用的字符编码为“gb18030、utf-8、utf-8-sig”,不要使用gbk或者gb2312
optional arguments:
-h, --help show this help message and exit
-f CSV_FILENAME, --filename CSV_FILENAME
要处理的csv文件名
-d DELIMITER_CHAR, --delimiterchar DELIMITER_CHAR
csv文件的分隔符号
-n SPLIT_FILE_NUMBERS, --splitnumbers SPLIT_FILE_NUMBERS
分割成的文件数
-e OUTPUT_ENCODING, --outputencoding OUTPUT_ENCODING
分割成的文件数
1 #!/usr/bin/env python3
2 #coding=utf-8
3
4 import os, csv, sys, locale, codecs, chardet, time
5 from argparse import ArgumentParser, RawTextHelpFormatter
6
7 #操作系统中默认的文件编码(当文件编码为中文扩展字符集时,统一使用gb18030编码--比gb2312和gbk支持的汉字多,同时兼容gb2312和gbk)
8 #中文扩展字符集编码列表
9 chinese_charsetstr='|gbk|gb2312|gb18030|cp936|'
10 default_chinese_charset='gb18030'
11 #使用gb18030解决了类似下面的错误:UnicodeDecodeError: 'gbk' codec can't decode byte 0xf8 in position 5902: illegal multibyte sequence
12 defaultencoding = default_chinese_charset if chinese_charsetstr.find(locale.getpreferredencoding().lower())>0 else locale.getpreferredencoding().lower()
13
14 def detectfileencoding(filename, filerowcount=None, info_fileobj=None):
15 #获取文件编码(为空则设置为操作系统默认文件编码,其中中文扩展字符集统一设置为'gb18030'大字符集)
16 time_start=time.time()
17 #编码检测结果
18 detectresult=''
19 with codecs.open(filename, 'rb') as fobj:
20 if filerowcount==None:
21 fcontent = fobj.read()
22 detectresult = chardet.detect(fcontent)
23 else:
24 linenum = 0
25 maxdetectrownum = 100 if filerowcount > 100 else filerowcount
26 #初始化要检测编码的内容
27 fcontent = bytes()
28 for line in fobj.readlines():
29 linenum += 1
30 if linenum < maxdetectrownum:
31 fcontent += line
32 else:
33 break
34 print_twice("\r\n【字符编码检测】:\r\n字符编码检测行数: %d"%(linenum), fileobj=info_fileobj)
35 detectresult = chardet.detect(fcontent)
36 detectencoding=detectresult.get('encoding')
37 print_twice("检测出来的字符编码: %s"%(detectencoding), fileobj=info_fileobj)
38 if detectencoding is None:
39 fileencoding = defaultencoding
40 else:
41 fileencoding = default_chinese_charset if chinese_charsetstr.find(detectencoding.lower())>0 else detectencoding
42 time_end=time.time()
43 print_twice("检测字符编码耗时: %d s"%(time_end-time_start), fileobj=info_fileobj)
44 return fileencoding
45
46 def getfilerowscount(filename):
47 # 计算文件总行数
48 count=-1
49 #, encoding=fileencoding
50 with codecs.open(filename, 'rb') as fobj:
51 for count,line in enumerate(fobj):
52 pass
53 count += 1
54 return count
55
56 def print_twice(output_string, fileobj):
57 #打印输出两次,“终端输出”和“输出到文件”
58 print(output_string)
59 print(output_string, file=fileobj)
60
61 def spit_csvfile(filename, delimiterchar=",", splitnumbers=0, outputencoding='utf-8' ):
62 if not os.path.isfile(filename):
63 print('不存在文件: %s'%(filename))
64 #退出脚本
65 sys.exit()
66 file_path = os.path.split(filename)[0]
67 short_filename = os.path.basename(os.path.splitext(filename)[0])
68 file_ext = os.path.splitext(filename)[1]
69 #日志文件名
70 info_filename = os.path.join(file_path, short_filename+'-info'+'.txt')
71
72 #获取文件行数
73 rowscount = getfilerowscount(filename)
74 #行号,初始值为1
75 rowsnum = 1
76 #列数,初始值为0(处理过程中将以第一行为标准,即以第一行的列数为正确的列数)
77 columncount = 0
78 #列正确的文件,生成正常的分割文件: 源文件-0.csv 源文件-1.csv
79 #列错误的文件: 源文件名-错误列.csv
80 error_filename = os.path.join(file_path, short_filename+'-错误列'+'.txt')
81 #打开日志文件
82 fileobj_info = open(info_filename, 'w+', newline='\r\n', encoding=outputencoding)
83
84 #获取csv文件信息
85 statinfo = os.stat(filename)
86 print_twice('\r\n【源文件信息】: ', fileobj=fileobj_info)
87 print_twice("源文件名称: %s"%(filename), fileobj=fileobj_info)
88 print_twice("源文件行数: %s"%(str(rowscount)), fileobj=fileobj_info)
89 #获取文件编码
90 file_encoding = detectfileencoding(filename, filerowcount=rowscount, info_fileobj=fileobj_info)
91
92 print_twice('\r\n【文件处理】:', fileobj=fileobj_info)
93 print_twice('处理源文件时使用的字符编码: %s'%(file_encoding), fileobj=fileobj_info)
94 print_twice("源文件大小: %s"%(str(statinfo.st_size//1024//1024)+"M"), fileobj=fileobj_info)
95
96 print_twice("分割成的文件数:%s"%(str(splitnumbers)), fileobj=fileobj_info)
97 splitLineCount = rowscount//splitnumbers
98 print_twice("分割文件的行数:%s"%(str(splitLineCount)), fileobj=fileobj_info)
99
100 # 可以用一个list包含文件对象列表
101 # 源文件名-文件序号.csv
102 fileList = []
103 fileIndex = 0
104 print_twice("分割后的文件名:", fileobj=fileobj_info)
105 while fileIndex < splitnumbers:
106 filename_tmp=os.path.join(file_path, short_filename+'-'+str(fileIndex)+file_ext)
107 print_twice(" "+filename_tmp, fileobj=fileobj_info)
108 file_tmp=codecs.open(filename_tmp, 'w+', encoding=outputencoding)
109 fileList.append(file_tmp)
110 fileIndex=fileIndex+1
111
112
113 fileobj_error=codecs.open(error_filename, 'w+', encoding=outputencoding)
114 print_twice("分割后的文件名(列异常数据):", fileobj=fileobj_info)
115 print_twice(" %s"%(error_filename), fileobj=fileobj_info)
116
117 print_twice("分割后文件的字符编码: %s"%(outputencoding), fileobj=fileobj_info)
118
119 with codecs.open(filename, encoding=file_encoding) as csvfile:
120 spamreader = csv.reader(csvfile,delimiter=delimiterchar)
121 for line in spamreader:
122 # 列数为0时,读取第一行作为准确的列数。
123 if ( columncount == 0 ):
124 columncount = len(line)
125 # 列数不为0时,当前行的列数与其匹配,将匹配的和不匹配的保存到不同的文件。
126 else:
127 #列数和第一行的列数匹配,则输出到分割的文件中
128 if ( columncount == len(line) ):
129 # 输出到对应文件序号的文件中: 行数“整除”分割行数
130 if ( rowsnum//splitLineCount > len(fileList)-1 ):
131 print((','.join(line)), file=fileList[len(fileList)-1])
132 else:
133 print((','.join(line)), file=fileList[rowsnum//splitLineCount])
134 else :
135 #列数与第一行的列数不匹配,则输出到异常文件中
136 print((','.join(line)), file=fileobj_error)
137 rowsnum=rowsnum+1
138
139 fileIndex=0
140 #文件列表中的文件处理:刷新缓存区,关闭文件
141 while fileIndex < splitnumbers:
142 if not fileList[fileIndex].closed:
143 fileList[fileIndex].flush()
144 fileList[fileIndex].close()
145 fileIndex=fileIndex+1
146 #关闭文件
147 if not fileobj_error.closed:
148 fileobj_error.flush()
149 fileobj_error.close()
150 if not fileobj_info.closed:
151 fileobj_info.flush()
152 fileobj_info.close()
153
154 if __name__ == "__main__":
155 description="\n本脚本用来分割处理csv文件,其中解决了csv文件的列异常问题。\n使用示例如下:"
156 description=description+'\npython csvtoolkit.py -f "2013.csv" -n 10 -e "utf-8"'
157 description=description+'\n\n'+"示例说明:"
158 description=description+'\n'+"要分割的csv文件: 2013.csv"
159 description=description+'\n'+"分割成的文件个数:10"
160 description=description+'\n'+"分割后的文件使用的字符编码:utf-8"
161 description=description+'\n'+"分割文件的字符编码,在简体中文系统中推荐使用的字符编码为“gb18030、utf-8、utf-8-sig”,不要使用gbk或者gb2312"
162
163 # 添加程序帮助,程序帮助支持换行符号
164 parser = ArgumentParser(description=description, formatter_class=RawTextHelpFormatter)
165
166 # 添加命令行选项
167
168 parser.add_argument("-f", "--filename",
169 dest="csv_filename",
170 default="",
171 help="要处理的csv文件名")
172 parser.add_argument("-d", "--delimiterchar",
173 dest="delimiter_char",
174 default=",",
175 help="csv文件的分隔符号")
176 parser.add_argument("-n", "--splitnumbers",
177 dest="split_file_numbers",
178 default=0,
179 help="分割成的文件数")
180 parser.add_argument("-e", "--outputencoding",
181 dest="output_encoding",
182 default='utf-8',
183 help="分割成的文件数")
184
185 args = parser.parse_args()
186
187 #try:
188 spit_csvfile(args.csv_filename, args.delimiter_char, int(args.split_file_numbers), args.output_encoding)
189 #except:
190 # print('Error to split csv file:')
191 # print(sys.exc_info()[0],sys.exc_info()[1])