1. 程式人生 > >C++檔案的讀寫和對多行多個字串的處理

C++檔案的讀寫和對多行多個字串的處理

C++讀寫檔案中的字串

今天幫人做了個簡單的作業,沒想到花時間最多的不是演算法而是檔案的讀寫,還有對讀入字串的分割處理。晚上寫作業的時候又用到了對字串的處理,這裡記錄一下。
小白第一次寫部落格,做的不好的請多多指正。

題目如下:

Retail.dat檔案中包含了某零售商店M的8萬多條真實(16,470種)商品的銷售記錄(每行對應一條銷售記錄),現M店裝修,需要把這些商品擺放在一個單行長櫃中,如下:
… k K+1 K+2 …
其中每個格子僅放一種商品,且商店左右兩側各有一個門,顧客從左門進入挑選商品,並在把所有商品都加入購物籃後可以馬上結帳從右門離去。注意顧客每次可能需要買多種商品,我們把顧客從進門選貨到選完所有貨物所經過的格子總數作為他的“購物不便程度”(以下簡稱“不便度”)度量,如:設顧客從左側(格子編號從1開始)進入商店,他所需的貨物分別擺放在101, 103, 210三個格子上,則該顧客的不便度為210。
請根據商品的歷史銷售記錄,為裝修後的M店設計一個合適的商品佈局順序,使得Retail.dat中所有顧客的總的不便度儘可能的小,並請編碼實現和驗證你的模型。程式的輸入/輸出要求描述如下:
輸入:Retail.dat 檔案
輸出:Layout.dat 檔案。該檔案總共有16,470行,第2行至最後一行每行包含如下內容:
商品編號, 格子編號
如 “123,1”表示第123號商品應該擺放在第一個格子裡面。
Layout.dat 檔案的第一行是Retail.dat中所有購買記錄在你的模型下的不便度的總和。


讀入:從硬碟讀入記憶體ifstream

首先將檔案流物件與檔案建立連線

  • 注意這裡判別是否開啟失敗很重要
    我第一次做的時候檔案根本沒有讀取到,但是我完全不知道。
    把作業給朋友朋友也沒有把檔案放到同一位置,做一個簡單的檢驗是有必要的。
string fileName = "retail.dat";
in.open(fileName.data(), ios::in);   //將檔案流物件與檔案連線起來
assert(in.is_open()); //如果開啟失敗,這裡會終止執行

  • 要求讀入檔案中的全部數字,檔案中數字的儲存方式為:

    在這裡插入圖片描述
    因為檔案的輸入輸出都是以字串的形式,我面對的問題是:如何讀取檔案中的每一個字串並將他們轉換成數字
    這裡可以提一下我踩過的坑(首先我沒有試過二進位制讀取和定義指標的讀取):

    1. 行末和行初會無法讀入
       string filename;  
      string line;  
         while (getline (in, line)) // line中不包括每行的換行符  
          {   
              cout << line << endl;  
          }  
      }  
    
    2. 直接用EOF判定檔案末尾的,問題都是最後一行無法讀入或無法停止迴圈
     ifstream FILE("test.txt");
      while (FILE.peek() != EOF)//修改
      {
          FILE.get(c);
          cout << c;
      }    
    
    3.試圖直接逐個讀取,但實際上行末或者行初會無法讀入
    string buffer;
    fstream in;
    in.open("com.txt",ios::in);
    while(!in.eof())
    {
        in.getline(buffer,256,'\n');// 表示該行字元達到256個或遇到換行就結束
    }
    
    4.還有各種一邊讀入一邊轉換的騷操作,下次要一邊嘗試一邊記錄錯誤的經歷

總而言之,最簡單的做法應該是,逐行讀取,在再提取空格,將單個字串轉換為數字。
注意這裡用到了stringstream來再次讀取從getline中讀到的每一行,自動跳過空格。

stringstream ss(buffer);

這部分實現的程式碼如下:

	string fileName = "retail.dat";

	ifstream in;
	in.open(fileName.data(), ios::in);   //將檔案流物件與檔案連線起來 
	assert(in.is_open());//如果開啟失敗,這裡會終止執行
	
	string buffer;
	while (getline(in, buffer))//一次讀取檔案的一行內容,含空格,為buffer的字串
	{
		int temp;
		stringstream ss(buffer);  //建立stringstream物件,初始化流內容為buffer所代表的字串
		while (ss >> temp)            //從buffer中一次次讀取數字存入temp,直到到達字串流的末尾
		{
			...對ss進行操作
		}
	}
	in.close();

寫出:從記憶體寫出到硬碟的資料夾

  • 寫出比讀入簡單,將要輸出的檔案和輸出檔案流(如果輸出物件是螢幕這個流則為cout)聯絡起來以後,用法和cout一樣,只是輸出的物件不再是螢幕而是資料夾。
	//預設開啟方式是:如果原來存在,則刪除原來的檔案;沒有這個檔案會自動建立。
	ofstream fout("Layout.dat");
	for (int i = Size; i > 1; i--)
	{		
			fout << bin[i].num << "," << Size-i+1 << endl;
	}
	fout.close();

我流快速排序

單純記錄一下(有錯誤的請dalao指正),注意swap是我重寫的函式

void qsort(goods *a,int l,int r)
{
	if (abs(r - l) == 1)
	{	
		if (a[l] > a[r])
			swap(a[l], a[r]);
		return;
	}

	int pivot = l;
	int p = l, q = r;
	l++;
	while(l<r)
	{ 
		while (a[r] > a[pivot]&& l < r)
			r--;
		while (a[l] <= a[pivot] && l < r)
			l++;		
		swap(a[r], a[l]);
	}

	swap(a[pivot], a[l]);

	if (p != l - 1)
		qsort(a, p, l - 1);
	else
		return;
	if (l + 1 != q)
		qsort(a, l + 1, q);
	else
		return;
}

最後貼一下完整的實現程式碼:

#include<iostream>
#include<fstream>
#include<cstring>
#include<string>
#include<cassert>
#include <sstream>
#include<cstdio>
using namespace std;
const int Size = 16470;

struct goods
{
	int num;
	int value;
	int degree;

	//過載自定義結構的運算子,根據資料總的出現頻率來判斷大小,出現次數相同,作為顧客不便度的次數越大,看做出現頻率越高
	bool operator>(const goods &a) 
	{
		if (this->value > a.value)
			return 1;
		else if (this->value == a.value&&this->degree > a.degree)
			return 1;
		else
			return 0;
	}

	bool operator<=(const goods &a)
	{
		if (this->value <a.value )
			return 1;
		else if (this->value == a.value&&this->degree <= a.degree)
			return 1;
		else return 0;
	}
};


void swap(goods &l, goods &r)
{
	goods t;
	t = l;
	l = r;
	r = t;
}

void qsort(goods *a,int l,int r)
{
	if (abs(r - l) == 1)
	{
		if (a[l] > a[r])
			swap(a[l], a[r]);
		return;
	}

	int pivot = l;
	int p = l, q = r;
	l++;
	while(l<r)
	{ 
		while (a[r] > a[pivot]&& l < r)
			r--;
		while (a[l] <= a[pivot] && l < r)
			l++;		
		swap(a[r], a[l]);
	}

	swap(a[pivot], a[l]);

	if (p != l - 1)
		qsort(a, p, l - 1);
	else
		return;
	if (l + 1 != q)
		qsort(a, l + 1, q);
	else
		return;
}

long long int cum(goods *bin)
{
	long long int count=0;
	int temp[Size];
	//temp[i]=k 即 序號為i的商品放在第k個貨架上
	for (int i = Size; i >= 1; i--)
	{
		temp[bin[i].num] =Size - i + 1 ;
	}

	for (int i = 1; i <= Size; i++)
	{
		if (bin[i].degree)
			count += bin[i].degree*temp[bin[i].num];
	}
	return count;
}

int main()
{
	string fileName = "retail.dat";
	goods bin[Size+1];//一個箱子,每個num代表著該類商品標號,value為個數

	memset(bin, 0, sizeof(bin));

	ifstream in;
	in.open(fileName.data(), ios::in);   //將檔案流物件與檔案連線起來 
	assert(in.is_open());//如果開啟失敗,這裡會終止執行
	
	string buffer;
	while (getline(in, buffer))//一次讀取檔案的一行內容,含空格,為buffer的字串
	{
		int temp;
		int max = 0;//儲存每一位顧客的不便度
		stringstream ss(buffer);  //建立stringstream物件,初始化流內容為buffer所代表的字串
		while (ss >> temp)            //從buffer中一次次讀取數字存入temp,直到到達字串流的末尾
		{
			if (temp > max)
				max = temp;//記下這個顧客的不方便度的序號
			bin[temp].num = temp; //讀到一個名為num的商品,暫時的編號為temp,等待排序
			bin[temp].value++; //所以名為num的商品暫時被使用了value次
		}
		bin[max].degree++;
	}
	in.close();
	
	qsort(bin,1,Size);//快速排序,按照被拿的次數排序
	long long int inconvient = cum(bin);


	ofstream fout("Layout.dat");
	fout << "這個模型的複雜度是: "<<inconvient << endl;
	for (int i = Size; i > 1; i--)
	{		
			fout << bin[i].num << "," << Size-i+1 << endl;
	}
	fout.close();

	
	system("pause");
	return 0;	

}