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;
}