CSV文件分割與列異常處理的python腳本


處理csv文件時經常會遇到下面的問題:

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])

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM