大文件排序

http://www.cnblogs.com/uvsjoh/archive/2012/03/24/2415343.html

在某些應用中,因爲內存資源有限制,而要排序的文件很大(比如10G的文件,只有10M的內存)主要的思想是:1 分割文件,使分割的文件能全部加載到內存。2 分別排序每一個分割的文件3 合併文件 難的是合併操作1 跌增合併,一次合併兩個文件。依次類推,直到最終只剩一個文件。時間複雜度主要在讀取文件,要多次讀取。2 利用堆,一次合併多個文件 時間複雜度主要取決於堆的查找。(堆主要用於查找當前最小的行) 或者更直接的是,每次順序查找當前內存中的最小行。 一般而言內存訪問速度要快很多,因此第2中方法應該快很多。如下是一個簡單的window上的實現:

//單行最大長度lEN_LINE - 1
#define LEN_LINE 80

int cmp(const void * str1, const void *str2)
{
    const char *p1 = (char *)str1, *p2 = (char *)str2;
    return strcmp(p1, p2);
}

/*
    文本文件以行爲單位排序
*/
bool FileSort(const char *file, size_t memLimit)
{
    FILE *fp;
    fp = fopen(file, "r");
    fseek(fp, 0, SEEK_END);
    size_t file_size = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    size_t split_cnt = (file_size+memLimit-1)/memLimit;
    char prefix[256] = "tttt";
    char file_t[256];
    
    int fileNo; //臨時文件編號
    char *buff = new char[memLimit];
    if (buff == NULL)
        return false;

    size_t totalReadLen=0;                //已讀文件大小  
    size_t lineCnt = memLimit/LEN_LINE;    //分配的空間能存下的行數
    fileNo = 1;

    //分割文件並排序
    while (totalReadLen < file_size)
    {
        //生成一個臨時文件名
        sprintf(file_t, "%s%d.txt", prefix, fileNo);
        FILE *fp_t = fopen(file_t, "w");
        size_t read_len=0;
        int i=0;
        //按行讀取
        while ( (i<lineCnt) && fgets(buff+read_len, LEN_LINE, fp))
        {
            i++;            
            totalReadLen += strlen(buff+read_len)+1;  //算上回車
            read_len += LEN_LINE;
        }
        //排序
        qsort(buff, i, LEN_LINE, cmp);
        int j=0;
        read_len = 0;
        //寫入文件
        while (j < i)
        {
            fputs(buff+read_len, fp_t);
            j++;
            read_len += LEN_LINE;
        }
        fclose(fp_t);
        fileNo++;
    }
    fclose(fp);

    fp = fopen(file, "w");
    FILE **fp_t = (FILE **)malloc(sizeof(FILE *)*fileNo);
    bool *flg = new bool[fileNo]; //標識是否該從相應文件中讀取新的一行

    fileNo--;
    for (int i=1; i<=fileNo; i++)
    {
        sprintf(file_t, "%s%d.txt", prefix, i);
        fp_t[i-1] = fopen(file_t, "r");
        flg[i-1] = true;
    }
    //合併
    while (1)
    {        
        //讀取新的一行
        for (int i=0; i<fileNo; i++)
        {            
            if (flg[i] )
            {
                if (fp_t[i])
                {
                    if (!fgets(buff+i*LEN_LINE, LEN_LINE, fp_t[i]))
                    {
                        fclose(fp_t[i]);
                        sprintf(file_t, "%s%d.txt", prefix, i);
                        DeleteFile(file_t);
                        buff[i*LEN_LINE] = 0;
                        fp_t[i] = NULL;
                    }
                }                
                flg[i] = false;
            }
        }
    
        int min = 0;
        //找出當前最小的行
        for (i=1; i<fileNo; i++)
        {
            if (buff[min*LEN_LINE] == 0)
                min = i;
            else if (buff[i*LEN_LINE] && strcmp(buff+min*LEN_LINE, buff+i*LEN_LINE)>0)
                min = i;
        }
        

        if (buff[min*LEN_LINE])
        {
            fputs(buff+min*LEN_LINE, fp);
            flg[min] = true;
        }
        else
            break;        
    }
    fclose(fp);

    return true;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章