[英]Reading csv with fstream, and only fstream
我在此問題上還看到了其他答案,但是所有這些答案都處理std::stringstream
或臨時char
或std::string
數組,各種其他外部庫,但我想嘗試僅使用fstream
標頭,以嘗試讀取僅包含數字char
和short
以及float
(以逗號分隔)的文件,該文本用多行分隔; 一些可能是數組或向量。 例:
1,1.1,11.1,11
2,2.2,22.2,22
3,3.3,33.3,33
...
順序是已知的,因為每一行都遵循struct
的變量。 行數可能會有所不同,但是現在讓我們假設它也是已知的。 同樣為了示例,我們僅考慮此順序以及以下類型:
int, double, double, int
伴隨着我所看到的一段代碼,我嘗試了這種簡單的(並且很可能是幼稚的)方法:
int a, d;
double b, c;
char fileName {"file.txt"};
std::fstream fs {fileName};
if(!fs.is_open())
// open with fs.out, write some defaults; this works, no need to mention
else
{
char comma;
while(fs.getline(fileName, 100, '\n'))
{
fs >> a >> comma >> b >> comma >> c >> comma >> d;
std::cout << 2*a << ", " << 2*b << ", " << 2*c << ", " << 2*d << '\n';
}
}
如果文件包含以上三行,並加上一個\\n
,它將輸出以下內容:
4, 4.4, 44.4, 44
6, 6.6, 66.6, 66
6, 6.6, 66.6, 66
*** stack smashing detected ***: <unknown> terminated
Aborted (core dumped)
如果在文件開頭添加\\n
,則輸出:
2, 2.2, 22.2, 22
4, 4.4, 44.4, 44
6, 6.6, 66.6, 66
6, 6.6, 66.6, 66
如果刪除最后一個\\n
,它將按預期工作。 我有幾個問題:
除了添加開頭\\n
並且不插入終止文件以按預期工作之外,寫文件時還能做什么?
如果變量的數量較長,例如每行100個,我該怎么做才能避免“用fs >> a >> c >> ...
繞地球fs >> a >> c >> ...
?
如果我只需要讀取一行或幾行,則一種方法可能會以某種方式計算\\n
或行的出現。 我該怎么辦?
(編輯)
fstream
(例如,目前如此),是否可以在不涉及其他標頭的情況下做到這一點? 順序是已知的,因為每一行都遵循結構中的變量。 行數可能會有所不同,但是現在讓我們假設它也是已知的。 同樣為了示例,我們僅考慮此順序以及以下類型:
int, double, double, int
如果知道字段的數量和順序,則可以根據需要使用>>
或getline
並同時使用','
或'\\n'
分隔符來進行讀取。 盡管使用面向行的輸入來讀取整行然后使用stringstream
來解析字段是更明智的選擇,但是沒有理由您不能僅使用fstream
做同樣的事情,正如您所指出的那樣。 這不是一個優雅的解決方案,但是仍然是一個有效的解決方案。
使用>>
運算符
您的數據有4個字段,前3個字段以comma
分隔,最后一個字段由newline
。 您可以簡單地連續循環並使用>>
運算符進行讀取,並在每次讀取后測試一下fail()
或eof()
,例如
#include <iostream>
#include <fstream>
#define NFIELD 4
#define MAXW 128
int main (int argc, char **argv) {
int a, d;
double b, c;
char comma;
std::fstream f (argv[1]);
if (!f.is_open()) {
std::cerr << "error: file open failed " << argv[1] << ".\n";
return 1;
}
for (;;) { /* loop continually */
f >> a >> comma >> b >> comma >> c >> comma >> d;
if (f.fail() || f.eof())
break;
std::cout << 2*a << "," << 2*b << "," << 2*c << "," << 2*d << '\n';
f.ignore (MAXW, '\n');
}
f.close();
}
保留一個簡單的字段計數器n
,您可以使用基於字段號的簡單switch
語句將正確的值讀取到相應的變量中,並在讀取所有字段時輸出(或存儲)構成結構的所有4個值。 (顯然,您也可以在閱讀時填充每個成員)。 不需要任何特殊要求,例如
#include <iostream>
#include <fstream>
#define NFIELD 4
int main (int argc, char **argv) {
int a, d, n = 0;
double b, c;
char comma;
std::fstream f (argv[1]);
if (!f.is_open()) {
std::cerr << "error: file open failed " << argv[1] << ".\n";
return 1;
}
for (;;) { /* loop continually */
switch (n) { /* coordinate read based on field number */
case 0: f >> a >> comma; if (f.eof()) goto done; break;
case 1: f >> b >> comma; if (f.eof()) goto done; break;
case 2: f >> c >> comma; if (f.eof()) goto done; break;
case 3: f >> d; if (f.eof()) goto done; break;
}
if (++n == NFIELD) { /* if all fields read */
std::cout << 2*a << "," << 2*b << "," << 2*c << "," << 2*d << '\n';
n = 0; /* reset field number */
}
}
done:;
f.close();
}
輸入文件示例
使用提供的樣本輸入。
$ cat dat/mixed.csv
1,1.1,11.1,11
2,2.2,22.2,22
3,3.3,33.3,33
使用/輸出示例
您只需將輸出中的每個字段加倍即可獲得所需的輸出:
$ ./bin/csv_mixed_read dat/mixed.csv
2,2.2,22.2,22
4,4.4,44.4,44
6,6.6,66.6,66
(以上兩者的輸出是相同的)
使用以','
和'\\n'
分隔的getline
您可以在使用getline
的邏輯上稍加getline
。 在這里,您使用f.getline(buf, MAXC, ',')
讀取前3個字段,找到第三個字段時,使用f.getline(buf, MAXC)
讀取最后一個字段。 例如,
#include <iostream>
#include <fstream>
#define NFIELD 4
#define MAXC 128
int main (int argc, char **argv) {
int a = 0, d = 0, n = 0;
double b = 0.0, c = 0.0;
char buf[MAXC];
std::fstream f (argv[1]);
if (!f.is_open()) {
std::cerr << "error: file open failed " << argv[1] << ".\n";
return 1;
}
while (f.getline(buf, MAXC, ',')) { /* read each field */
switch (n) { /* coordinate read based on field number */
case 0: a = std::stoi (buf); break;
case 1: b = std::stod (buf); break;
case 2: c = std::stod (buf);
if (!f.getline(buf, MAXC)) /* read d with '\n' delimiter */
goto done;
d = std::stoi (buf);
break;
}
if (++n == NFIELD - 1) { /* if all fields read */
std::cout << 2*a << "," << 2*b << "," << 2*c << "," << 2*d << '\n';
n = 0; /* reset field number */
}
}
done:;
f.close();
}
( 注意:與使用>>
運算符不同,在如上所述使用getline
時,每個comma
后面不能有空格 。)
使用/輸出示例
輸出是相同的。
$ ./bin/csv_mixed_read2 dat/mixed.csv
2,2.2,22.2,22
4,4.4,44.4,44
6,6.6,66.6,66
無論您使用的是上面的示例還是stringstream
,您都必須知道字段的數量和順序。 無論使用循環還是if..else if..else
或switch
邏輯都是相同的。 您需要某種方式來協調您的閱讀與正確的字段。 保持簡單的字段計數器幾乎和其他任何事情一樣簡單。 仔細檢查一下,如果您還有其他問題,請告訴我。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.