【multimap在文件處理中顯奇效】將文本文件的每行內容,按照行首6個數字的升序,重新排序

這是我編程生涯的一塊里程碑,作爲菜鳥小白,一直在底層仰望程序天空中自由翱翔的前輩們,這次自己起飛了一下下,認識到了數據結構的巨大魅力和無限潛力,感受到了編程帶來的快樂!

這裏簡單記錄,以備後續使用。

問題描述

有一份文件,示例如下,前6個字符表示編號,編號應從0-8000左右,但由於某種原因(不重要啦),生成的順序被打亂,但序號和該行的內容是匹配的。

007453,-1,621,754,1109,927,0.9999844,-1,-1,-1
007453,-1,646,330,1095,522,0.99999344,-1,-1,-1

 
005556,-1,588,681,1004,870,0.9999943,-1,-1,-1
005556,-1,891,166,1222,504,0.99999654,-1,-1,-1

 
007181,-1,1033,130,1259,513,0.99998987,-1,-1,-1
007181,-1,628,593,1056,846,0.9999931,-1,-1,-1

……………………

現在需要按照編號遞增的順序,將每行重新排列。排好後應該是這樣的:

000001,-1,607,306,947,518,0.999977,-1,-1,-1
000001,-1,597,700,938,922,0.99997723,-1,-1,-1
000002,-1,608,309,955,519,0.9999672,-1,-1,-1
000002,-1,601,698,919,918,0.99997675,-1,-1,-1
000003,-1,599,302,944,519,0.99996614,-1,-1,-1
000003,-1,603,698,936,920,0.9999789,-1,-1,-1
……………………

待解決的難點:

1、文件讀取時,僅使用string讀取,句首出現亂碼;(使用寬字符串wstring解決)
2、讀取文件後,如何減少循環查詢的次數。

方法一:暴力解決法

方法一是循環8000次(大約),每次循環過程中,製作該次循環要查找的標準格式號碼,每讀取到一行非空的內容,取出該行的前6個字符,與製作的號碼比較,如果相等說明找到了目標行,把該行和該行下面的行(直到遇到空行)一起寫入輸出文件。

下面這個程序跑了將近一個小時纔出結果:

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <io.h>
#include <codecvt>//讀取utf-8文件

using namespace std;

int main()
{
    string fileName = "result.txt";//文件名稱

    ofstream out("allout.txt", ofstream::app);//以追加形式寫入
    if (!out) {
        cerr << "無法打開輸出文件" << endl;
        return -1;
    }

    int j=0;
    
    wstring_convert<std::codecvt_utf8<wchar_t>> conv;
    
    for (int i = 1;i < 8770;++i) {
        //製作應有的名稱
        string numstr,nullstring="";
        if (i < 10) numstr = nullstring+"00000" + to_string(i);
        else if(i<100) numstr = nullstring + "0000" + to_string(i);
        else if (i < 1000) numstr = nullstring + "000" + to_string(i);
        else if (i < 10000) numstr = nullstring + "00" + to_string(i);
        wstring wnumstr = conv.from_bytes(numstr);

        string str;
        ifstream in(fileName);
        if (in)//若文件打開成功
        {
            while (getline(in, str))//逐行獲取in句柄綁定的文件內容
            {
                wstring wstr = conv.from_bytes(str);
                if (!str.empty() && str != " ") {
                    wstring wstr1(wstr, 0, 6);//獲取每行前6個字符
                    
                    if (wnumstr == wstr1) {//找到了首個
                   
                        out << str << endl;
                        getline(in, str);
                        out << str << endl;
                        getline(in, str);
                        if(!str.empty() && str != " ")
                            out << str << endl;
                        break;
                    }
                }
            }
        }
        else {//若文件打開失敗
            cerr << "無法打開輸入文件" << endl;
            return -1;
        }
    }

    return 0;
}

方法二:巧用multimap

在multimap數據結構的幫助下,一秒出結果!!!

方法二使用multimap<string,string>,第一個string存儲非空行的前6個字符(即號碼),第二個string存儲該行的整行內容。

由於multimap具備自動根據第一個string的字典序排序的能力,所以整個過程只需要一趟循環,便可以將文件中所有的內容存放到multimap中,大約佔用800Kb,還是可以接受的, 相當於犧牲空間換時間了。

multimap最具吸引力的就是:只要把數據存好得到的就是排好序的,這是在太高效了!最後再來一趟遍歷,輸出保存的內容,全程高速!

#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <io.h>
#include <codecvt>//讀取utf-8文件
#include <map>

using namespace std;

int main()
{
    string fileName = "result.txt";//文件名稱

    ofstream out("allout.txt", ofstream::app);//以追加形式寫入
    if (!out) {
        cerr << "無法打開輸出文件" << endl;
        return -1;
    }

    wstring_convert<std::codecvt_utf8<wchar_t>> conv;//用於wstring和string的轉換
    multimap<string, string> mymultimap;//應取得C位!!!

    string str;
    ifstream in(fileName);
    if (in)//若文件打開成功
    {
        while (getline(in, str))//逐行獲取in句柄綁定的文件內容
        {
            wstring wstr = conv.from_bytes(str);
            if (!str.empty() && str != " ") {
                wstring wstr1(wstr, 0, 6);//獲取每行前6個字符
                string str1 = conv.to_bytes(wstr1);
                auto ret = mymultimap.insert({ str1,str });//將<前綴,該行內容>存入map
            }
        }
        //遍歷multimap,將內容輸出到文件
        for (auto i : mymultimap) {
            out << i.second << endl;
        }
    }
    else {//若文件打開失敗
        cerr << "無法打開輸入文件" << endl;
        return -1;
    }

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