使用python在linux中组合文件时文件大小大幅减少

时间:2017-06-28 13:01:32

标签: python linux file multiprocessing

我写了一个脚本,它接受一个文件夹,并使用python的多处理池库将它们组合成最大大小为500MB的文件。该脚本获取文件夹中的文件列表,并将其分为16个列表,每个列表都映射到一个进程。在每个进程中,使用每个列表中的一组文件组合临时文件。在获得所有这16个文件之后,我依次组合这16个文件并删除临时文件。  我在带有ext4文件系统的CentOS系统上运行它,我传递了一个大小为930 MB的文件夹,其中186147个文件分布在50个子文件夹中,它给了我一个文件作为输出,大小为346 MB。我很困惑文件大小如何减少这么多。

请注意,这些186147文件中的每个文件在开始时都有一个额外的标头,在最终组合文件创建期间会被忽略,但只有标题的文件只有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')

2 个答案:

答案 0 :(得分:2)

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

答案 1 :(得分:2)

对于 ext4 filesystem ,在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:


...