使用python在linux中合​​并文件时文件大小的巨大减less

我写了一个脚本,其中包含一个文件的文件夹,并使用Python的多处理池库将它们组合成最大大小为500MB的文件。 脚本获取文件夹中的文件列表,并将其分成16个列表,每个列表映射到一个进程。 在每个进程中,组合的临时文件由每个列表中的一组文件组成。 在获得所有这16个文件后,我将这16个文件按顺序合并,并删除临时文件。 我在ext4文件系统的CentOS系统上运行这个,并且我传递了一个大小为930 MB的文件夹,其中186147个文件分布在50个子文件夹中,它给了我一个单一的文件作为输出,大小为346 MB。 我很困惑如何减less文件大小如此之多。

请注意,这些186147文件中的每一个在开始时都有一个额外的头文件,在最终的组合文件创build过程中被忽略,但只有头文件只有233个字节。

为了检查我的脚本是否正确,我检查了合并文件(3083015)中的总行数,它匹配186147个文件(3269162)中的行数总和(186147)。 我也试图猫单个文件和行看起来是完整的,但我没有通过整个文件。

有什么我在这里失踪?

这是我使用的并行函数:

curr_write_file_name = os.path.join(output_folder, str(list_index) + '_' + "00000.flows") curr_write_file = open(curr_write_file_name, 'w') curr_write_file.write(header) curr_write_count = 1 for curr_file in file_list: print('Processing', curr_file) netflow_read = open(curr_file, 'r') for index, line in enumerate(netflow_read): if index == 0: continue else: curr_write_file.write(line) if os.stat(curr_file).st_size >= 500000000: curr_write_file.close() curr_write_file_name = os.path.join(output_folder, str(list_index) + '_' + str(curr_write_count).zfill(5) + '.flows') curr_write_file = open(curr_write_file_name, 'w') curr_write_count = curr_write_count + 1 curr_write_file.write(header) netflow_read.close() 

这是相应的主要:

 if __name__=='__main__' dataFileList = [] for dirPath, dirNames, fileNames in os.walk(str(sys.argv[1])): # Since the filtering occurs parallel, sorting the files has no benefit dirNames.sort() fileNames.sort() dataFileList = dataFileList + [os.path.join(dirPath, fileName) for fileName in fileNames if fileName.endswith('.flows')] noOfProcesses = os.cpu_count()*2 # To create a maximum of no_of_cores*2 processes process_pool = mp.Pool(noOfProcesses) # To create a parallel pool of noOfProcesses processes file_split_number = int(len(dataFileList)/noOfProcesses) dataFile_list_of_lists = [(dataFileList[x:x+file_split_number], x) for x in range(0, len(dataFileList), file_split_number)] process_pool.map(worker_process_combine_set, dataFile_list_of_lists) # To map the processes to the files in the list and split them stage_1 = time.time() print('Completed first stage combining successfully in', stage_1 - start_time, 'seconds') process_pool.close() process_pool.join() # sequential combining finalFiles = combine_final() print('Completed combining files successfully in', time.time() - start_time, 'seconds') 

对于ext4文件系统 ,在Linux上,你可以检查:

 df -T / | awk '{print $2}' | tail -1 

磁盘存储的统一是文件系统的块大小。 每个文件都使用一定数量的块存储。 但最后一块并不总是满的,你的文件系统完成这个空间来尊重你的磁盘存储规则。 这就是为什么如果您通过串联将多个文件转换为单个文件,您可以节省磁盘空间。

对于你的情况,将186147文件转换为单个文件。 您最多可以节省186147*sizeof(block)字节。 如果考虑到最后一个块中没有真正使用的字节数是一个统一的规则,那么平均而言,您可以节省sizeof(block)/2个字节。

对于大多数文件系统,块的大小是4KB,也就是说4096个字节。 这里最多可以节省4096*186147//(1024**2) = 727MB ,平均(4096/2)*186147//(1024**2) = 363MB

以root用户身份查看文件系统的块大小:

 device=`df -T / | awk '{print $1}' | tail -1` dumpe2fs $device | grep 'Block size 

测试你的bash或者zsh控制台:

 echo 'a' > filea.txt; echo 'b' > fileb.txt; more *; ls -sh * 

输出:

 :::::::::::::: filea.txt :::::::::::::: a :::::::::::::: fileb.txt :::::::::::::: b 4,0K filea.txt 4,0K fileb.txt 

然后:

 cat * > file.txt; more *; ls -sh * 

输出:

 :::::::::::::: filea.txt :::::::::::::: a :::::::::::::: fileb.txt :::::::::::::: b :::::::::::::: file.txt :::::::::::::: a b 4,0K filea.txt 4,0K fileb.txt 4,0K file.txt 

file.txt的大小是4KB而不是8KB = sizeof(fileb.txt) + sizeof(filea.txt)

如果您创建了4097个字节的文件,则将分配2个文件系统的块。 看到:

 dd if=/dev/zero of=file.txt bs=1 count=4097 &> /dev/null ls -s --block-size=1 file.txt 

输出:

 8192 file.txt 

file.txt的大小是8192 = 2*sizeof(filesystem_block) bytes不是4097字节。

尝试:

 files = ['file1.txt', 'file2.txt', 'file3.txt'] def cut(files, n_chunks): size_chunk = len(files)//n_chunks for i in range(0, len(files), size_chunk): yield files[i:i + size_chunk] def merge(files): for _file in files: ... 

什么是您的文件系统的块大小? 因为,你看,在最后一个块的末尾有一些未使用的空间,平均来说是半个块,如果你乘以文件的数量(186147),这可能达到几百兆…