เนื้อหาของ file.txt คือ:
5 3
6 4
7 1
10 5
11 6
12 3
12 4
5 3
คู่ประสานงานอยู่ที่ไหน ฉันจะประมวลผลข้อมูลนี้ทีละบรรทัดใน C ++ ได้อย่างไร
ฉันสามารถรับบรรทัดแรก แต่ฉันจะรับบรรทัดถัดไปของไฟล์ได้อย่างไร
ifstream myfile;
myfile.open ("text.txt");
เนื้อหาของ file.txt คือ:
5 3
6 4
7 1
10 5
11 6
12 3
12 4
5 3
คู่ประสานงานอยู่ที่ไหน ฉันจะประมวลผลข้อมูลนี้ทีละบรรทัดใน C ++ ได้อย่างไร
ฉันสามารถรับบรรทัดแรก แต่ฉันจะรับบรรทัดถัดไปของไฟล์ได้อย่างไร
ifstream myfile;
myfile.open ("text.txt");
คำตอบ:
ก่อนอื่นให้สร้างifstream
:
#include <fstream>
std::ifstream infile("thefile.txt");
สองวิธีมาตรฐานคือ:
สมมติว่าทุกบรรทัดประกอบด้วยตัวเลขสองตัวและอ่านโทเค็นด้วยโทเค็น:
int a, b;
while (infile >> a >> b)
{
// process pair (a,b)
}
การแยกตามบรรทัดโดยใช้สตริงสตรีม:
#include <sstream>
#include <string>
std::string line;
while (std::getline(infile, line))
{
std::istringstream iss(line);
int a, b;
if (!(iss >> a >> b)) { break; } // error
// process pair (a,b)
}
คุณไม่ควรผสม (1) และ (2) เนื่องจากการแจงโดยใช้โทเค็นไม่ได้ขึ้นบรรทัดใหม่ดังนั้นคุณอาจจบด้วยบรรทัดว่างเปล่าหากคุณใช้getline()
หลังจากการแยกโทเค็นทำให้คุณจบ บรรทัดแล้ว
int a, b; char c; while ((infile >> a >> c >> b) && (c == ','))
while(getline(f, line)) { }
สร้างและเกี่ยวกับการจัดการข้อผิดพลาดโปรดดูที่บทความ (ของฉัน) นี้: gehrcke.de/2011/06/… (ฉันคิดว่าฉันไม่จำเป็นต้องมีมโนธรรมที่ไม่ดีโพสต์ที่นี่ วันที่คำตอบนี้)
ใช้ifstream
เพื่ออ่านข้อมูลจากไฟล์:
std::ifstream input( "filename.ext" );
หากคุณจำเป็นต้องอ่านทีละบรรทัดให้ทำสิ่งนี้:
for( std::string line; getline( input, line ); )
{
...for each line in input...
}
แต่คุณอาจต้องแยกคู่พิกัด:
int x, y;
input >> x >> y;
ปรับปรุง:
ในรหัสของคุณที่คุณใช้ofstream myfile;
แต่o
ในยืนofstream
output
หากคุณต้องการที่จะอ่านจากแฟ้ม (input) ifstream
การใช้งาน fstream
หากคุณต้องการใช้งานทั้งการอ่านและการเขียน
การอ่านไฟล์ทีละบรรทัดใน C ++ สามารถทำได้หลายวิธี
วิธีที่ง่ายที่สุดคือเปิด std :: ifstream และ loop โดยใช้ std :: getline () รหัสสะอาดและง่ายต่อการเข้าใจ
#include <fstream>
std::ifstream file(FILENAME);
if (file.is_open()) {
std::string line;
while (std::getline(file, line)) {
// using printf() in all tests for consistency
printf("%s", line.c_str());
}
file.close();
}
ความเป็นไปได้อีกอย่างหนึ่งคือการใช้ห้องสมุด Boost แต่รหัสจะได้รับรายละเอียดมากขึ้น ประสิทธิภาพค่อนข้างคล้ายกับรหัสข้างต้น (วนกับ std :: getline ())
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <fcntl.h>
namespace io = boost::iostreams;
void readLineByLineBoost() {
int fdr = open(FILENAME, O_RDONLY);
if (fdr >= 0) {
io::file_descriptor_source fdDevice(fdr, io::file_descriptor_flags::close_handle);
io::stream <io::file_descriptor_source> in(fdDevice);
if (fdDevice.is_open()) {
std::string line;
while (std::getline(in, line)) {
// using printf() in all tests for consistency
printf("%s", line.c_str());
}
fdDevice.close();
}
}
}
หากประสิทธิภาพมีความสำคัญต่อซอฟต์แวร์ของคุณคุณอาจพิจารณาใช้ภาษา C รหัสนี้อาจเร็วกว่ารุ่น C ++ 4-5 เท่าข้างต้นดูมาตรฐานด้านล่าง
FILE* fp = fopen(FILENAME, "r");
if (fp == NULL)
exit(EXIT_FAILURE);
char* line = NULL;
size_t len = 0;
while ((getline(&line, &len, fp)) != -1) {
// using printf() in all tests for consistency
printf("%s", line);
}
fclose(fp);
if (line)
free(line);
ฉันได้ทำการวัดประสิทธิภาพด้วยโค้ดด้านบนและผลลัพธ์นั้นน่าสนใจ ฉันทดสอบโค้ดด้วยไฟล์ ASCII ที่มี 100,000 บรรทัด, 1,000,000 บรรทัดและ 10,000,000 บรรทัดข้อความ ข้อความแต่ละบรรทัดมีค่าเฉลี่ย 10 คำ โปรแกรมจะคอมไพล์ด้วย-O3
การออปติไมซ์และเอาท์พุทของมันถูกส่งต่อไป/dev/null
เพื่อเอาตัวแปรเวลาการบันทึกออกจากการวัด สุดท้าย แต่ไม่ท้ายสุดแต่ละโค้ดจะบันทึกแต่ละบรรทัดด้วยprintf()
ฟังก์ชันเพื่อความมั่นคง
ผลลัพธ์แสดงเวลา (เป็นมิลลิวินาที) ที่แต่ละส่วนของรหัสเอาไปอ่านไฟล์
ความแตกต่างด้านประสิทธิภาพระหว่างวิธี C ++ สองวิธีนั้นน้อยที่สุดและไม่ควรสร้างความแตกต่างในทางปฏิบัติ ประสิทธิภาพของรหัส C คือสิ่งที่ทำให้เกณฑ์มาตรฐานน่าประทับใจและสามารถเป็นตัวเปลี่ยนเกมในแง่ของความเร็ว
10K lines 100K lines 1000K lines
Loop with std::getline() 105ms 894ms 9773ms
Boost code 106ms 968ms 9561ms
C code 23ms 243ms 2397ms
std::cout
printf
printf()
ฟังก์ชั่นในทุกกรณีเพื่อความมั่นคง ฉันได้ลองใช้std::cout
ในทุกกรณีแล้วและมันก็ไม่ได้ต่างอะไรอย่างแน่นอน ตามที่ฉันได้อธิบายไว้ในข้อความผลลัพธ์ของโปรแกรมจะเป็นไปเพื่อ/dev/null
ให้เวลาในการพิมพ์เส้นไม่ถูกวัด
cstdio
ในลำธารออกมาจะตรงกับ std::ios_base::sync_with_stdio(false)
คุณควรจะได้พยายามกับการตั้งค่า ฉันเดาว่าคุณจะได้รับการแสดงที่ดีขึ้นมาก (ไม่รับประกันว่าจะมีการกำหนดตามการนำไปใช้เมื่อมีการสลับการซิงโครไนซ์)
เนื่องจากพิกัดของคุณอยู่รวมกันเป็นคู่ทำไมไม่เขียนโครงสร้างให้พวกเขา?
struct CoordinatePair
{
int x;
int y;
};
จากนั้นคุณสามารถเขียนโอเปอเรเตอร์การโอเวอร์โหลดสำหรับ istreams:
std::istream& operator>>(std::istream& is, CoordinatePair& coordinates)
{
is >> coordinates.x >> coordinates.y;
return is;
}
จากนั้นคุณสามารถอ่านไฟล์พิกัดตรงไปยังเวกเตอร์ดังนี้:
#include <fstream>
#include <iterator>
#include <vector>
int main()
{
char filename[] = "coordinates.txt";
std::vector<CoordinatePair> v;
std::ifstream ifs(filename);
if (ifs) {
std::copy(std::istream_iterator<CoordinatePair>(ifs),
std::istream_iterator<CoordinatePair>(),
std::back_inserter(v));
}
else {
std::cerr << "Couldn't open " << filename << " for reading\n";
}
// Now you can work with the contents of v
}
int
ราชสกุลจากกระแสในoperator>>
? เราจะทำให้มันทำงานร่วมกับตัวแยกวิเคราะห์ย้อนรอยได้อย่างไร (เช่นเมื่อoperator>>
ล้มเหลวให้ย้อนกระแสไปยังตำแหน่งก่อนหน้านี้ว่าคืนค่าเท็จหรืออะไรทำนองนั้น)
int
โทเค็นสองโทงได้is
สตรีมจะประเมินfalse
และลูปการอ่านจะสิ้นสุด ณ จุดนั้น คุณสามารถตรวจจับสิ่งนี้ได้operator>>
โดยการตรวจสอบค่าส่งคืนของการอ่านแต่ละรายการ is.clear()
หากคุณต้องการที่จะย้อนกลับสตรีมคุณจะเรียก
operator>>
นั้นจะถูกต้องมากขึ้นที่จะพูดis >> std::ws >> coordinates.x >> std::ws >> coordinates.y >> std::ws;
ตั้งแต่มิฉะนั้นคุณจะสมมติว่าสตรีมอินพุตของคุณอยู่ในโหมดข้ามช่องว่าง
การขยายคำตอบที่ยอมรับถ้าอินพุตคือ:
1,NYC
2,ABQ
...
คุณจะยังสามารถใช้ตรรกะเดียวกันได้เช่นนี้
#include <fstream>
std::ifstream infile("thefile.txt");
if (infile.is_open()) {
int number;
std::string str;
char c;
while (infile >> number >> c >> str && c == ',')
std::cout << number << " " << str << "\n";
}
infile.close();
แม้ว่าจะไม่จำเป็นต้องปิดไฟล์ด้วยตนเอง แต่ก็ควรทำเช่นนั้นหากขอบเขตของตัวแปรไฟล์ใหญ่กว่า:
ifstream infile(szFilePath);
for (string line = ""; getline(infile, line); )
{
//do something with the line
}
if(infile.is_open())
infile.close();
คำตอบนี้มีไว้สำหรับ visual studio 2017 และหากคุณต้องการอ่านจากไฟล์ข้อความตำแหน่งที่สัมพันธ์กับแอปพลิเคชันคอนโซลที่คอมไพล์ของคุณ
ก่อนอื่นให้ใส่เท็กซ์ไฟล์ของคุณ (test.txt ในกรณีนี้) ลงในโฟลเดอร์โซลูชันของคุณ หลังจากรวบรวมไฟล์ข้อความในโฟลเดอร์เดียวกันกับ applicationName.exe
C: \ Users \ "ชื่อผู้ใช้" \ แหล่ง \ Repos \ "solutionName" \ "solutionName"
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ifstream inFile;
// open the file stream
inFile.open(".\\test.txt");
// check if opening a file failed
if (inFile.fail()) {
cerr << "Error opeing a file" << endl;
inFile.close();
exit(1);
}
string line;
while (getline(inFile, line))
{
cout << line << endl;
}
// close the file stream
inFile.close();
}
นี่เป็นโซลูชันทั่วไปสำหรับการโหลดข้อมูลลงในโปรแกรม C ++ และใช้ฟังก์ชัน readline สิ่งนี้สามารถแก้ไขได้สำหรับไฟล์ CSV แต่ตัวคั่นเป็นช่องว่างที่นี่
int n = 5, p = 2;
int X[n][p];
ifstream myfile;
myfile.open("data.txt");
string line;
string temp = "";
int a = 0; // row index
while (getline(myfile, line)) { //while there is a line
int b = 0; // column index
for (int i = 0; i < line.size(); i++) { // for each character in rowstring
if (!isblank(line[i])) { // if it is not blank, do this
string d(1, line[i]); // convert character to string
temp.append(d); // append the two strings
} else {
X[a][b] = stod(temp); // convert string to double
temp = ""; // reset the capture
b++; // increment b cause we have a new number
}
}
X[a][b] = stod(temp);
temp = "";
a++; // onto next row
}