ฉันจะอ่านไฟล์ทั้งหมดใน std :: string ใน C ++ ได้อย่างไร?


178

ฉันจะอ่านไฟล์ลงใน a ได้std::stringอย่างไรเช่นอ่านไฟล์ทั้งหมดในครั้งเดียว?

ข้อความหรือโหมดไบนารีควรถูกระบุโดยผู้เรียก การแก้ปัญหาควรเป็นไปตามมาตรฐานพกพาและมีประสิทธิภาพ ไม่ควรคัดลอกข้อมูลของสตริงโดยไม่จำเป็นและควรหลีกเลี่ยงการจัดสรรหน่วยความจำใหม่ขณะอ่านสตริง

วิธีหนึ่งที่จะทำเช่นนี้จะ stat ขนาดไฟล์ที่ปรับขนาดstd::stringและfread()เข้าไปในstd::string's 'const_cast<char*>() เอ็ด data()สิ่งนี้ต้องใช้std::stringข้อมูลของต่อเนื่องซึ่งไม่จำเป็นต้องใช้ตามมาตรฐาน แต่ดูเหมือนจะเป็นกรณีสำหรับการใช้งานที่รู้จักทั้งหมด ถ้าไฟล์อ่านในโหมดข้อความstd::stringขนาดของไฟล์อาจไม่เท่ากับขนาดของไฟล์

ถูกต้องอย่างเต็มที่แก้ตามมาตรฐานและแบบพกพาอาจจะสร้างขึ้นโดยใช้std::ifstream's rdbuf()เป็นและจากที่นั่นเป็นstd::ostringstream std::stringอย่างไรก็ตามสิ่งนี้สามารถคัดลอกข้อมูลสตริงและ / หรือจัดสรรหน่วยความจำโดยไม่จำเป็น

  • การใช้งานไลบรารีมาตรฐานที่เกี่ยวข้องทั้งหมดนั้นฉลาดพอที่จะหลีกเลี่ยงค่าใช้จ่ายที่ไม่จำเป็นทั้งหมดได้หรือไม่?
  • มีวิธีอื่นที่จะทำหรือไม่
  • ฉันพลาดฟังก์ชั่น Boost ที่ซ่อนอยู่ซึ่งมีฟังก์ชั่นที่ต้องการอยู่แล้วหรือไม่?


void slurp(std::string& data, bool is_binary)

โปรดทราบว่าคุณยังคงมีบางสิ่งที่ไม่ได้ระบุไว้ ตัวอย่างเช่นการเข้ารหัสอักขระของไฟล์คืออะไร คุณจะพยายามตรวจหาอัตโนมัติ (ซึ่งจะทำงานเฉพาะในบางกรณีเท่านั้น) หรือไม่ คุณจะให้เกียรติเช่นส่วนหัว XML บอกให้คุณเข้ารหัสไฟล์หรือไม่ นอกจากนี้ยังไม่มีสิ่งเช่น "โหมดข้อความ" หรือ "โหมดไบนารี" - คุณกำลังคิด FTP?
Jason Cohen

โหมดข้อความและไบนารีเป็นแฮ็กเฉพาะ MSDOS และ Windows ที่พยายามหลีกเลี่ยงความจริงที่ว่าบรรทัดใหม่ถูกแสดงด้วยอักขระสองตัวใน Windows (CR / LF) ในโหมดข้อความพวกเขาจะถือว่าเป็นหนึ่งอักขระ ('\ n')
Ferruccio

1
แม้ว่าจะไม่ใช่ (ค่อนข้าง) ซ้ำกันอย่างแน่นอน แต่สิ่งนี้เกี่ยวข้องอย่างใกล้ชิดกับ: วิธีจัดสรรหน่วยความจำล่วงหน้าสำหรับวัตถุ std :: string ได้อย่างไร (ซึ่งตรงกันข้ามกับคำแถลงของ Konrad ด้านบนรวมถึงรหัสในการทำเช่นนี้อ่านไฟล์ไปยังปลายทางโดยตรงโดยไม่ต้องทำสำเนาเพิ่มเติม)
Jerry Coffin

1
"ต่อเนื่องไม่จำเป็นต้องใช้โดยมาตรฐาน" - ใช่มันเป็นในทางอ้อม ทันทีที่คุณใช้ op [] บนสตริงนั้นจะต้องรวมกันเป็นบัฟเฟอร์ที่เขียนได้อย่างต่อเนื่องดังนั้นจึงรับประกันความปลอดภัยในการเขียนถึง & str [0] ถ้าคุณ. reses () มีขนาดใหญ่เพียงพอก่อน และใน C ++ 11 สตริงนั้นต่อเนื่องกันเสมอ
Tino Didriksen

2
ลิงค์ที่เกี่ยวข้อง: จะอ่านไฟล์ใน C ++ ได้อย่างไร? - มาตรฐานและอภิปรายวิธีการต่างๆ และใช่rdbuf(คำตอบที่ยอมรับได้) ไม่ใช่วิธีที่เร็วที่สุดreadคือ
ตำนาน 2k

คำตอบ:


138

วิธีหนึ่งคือการล้างบัฟเฟอร์กระแสเข้าไปในกระแสหน่วยความจำที่แยกต่างหากและแล้วแปลงที่std::string:

std::string slurp(std::ifstream& in) {
    std::ostringstream sstr;
    sstr << in.rdbuf();
    return sstr.str();
}

นี่เป็นเรื่องย่อ อย่างไรก็ตามตามที่ระบุไว้ในคำถามนี้ดำเนินการสำเนาที่ซ้ำซ้อนและน่าเสียดายที่ไม่มีวิธีการลบสำเนานี้โดยพื้นฐาน

ทางออกที่แท้จริงเพียงข้อเดียวที่หลีกเลี่ยงการทำสำเนาซ้ำซ้อนคือการอ่านด้วยตนเองในวงวนโชคไม่ดี ตั้งแต่ตอนนี้ C ++ รับประกันว่าจะมีสตริงที่ต่อเนื่องกันดังนั้นจึงสามารถเขียนต่อไปนี้ (≥C ++ 14):

auto read_file(std::string_view path) -> std::string {
    constexpr auto read_size = std::size_t{4096};
    auto stream = std::ifstream{path.data()};
    stream.exceptions(std::ios_base::badbit);

    auto out = std::string{};
    auto buf = std::string(read_size, '\0');
    while (stream.read(& buf[0], read_size)) {
        out.append(buf, 0, stream.gcount());
    }
    out.append(buf, 0, stream.gcount());
    return out;
}

20
จุดประสงค์ของการทำให้เป็นผู้เผยแพร่เองคืออะไร ฉันมักจะเลือกรหัสที่ชัดเจน ในฐานะที่เป็น VB.Net ที่ยอมรับตนเอง (IIRC) ฉันคิดว่าคุณควรเข้าใจความรู้สึกของตนเอง
sehe

5
@sehe: ฉันคาดหวัง Coder c ++ ที่มีความสามารถครึ่งหนึ่งเพื่อให้เข้าใจว่าหนึ่งซับ มันค่อนข้างเชื่องเมื่อเทียบกับสิ่งอื่น ๆ ที่อยู่รอบ ๆ
DevSolar

43
@DevSolar ทีนี้รุ่นที่อ่านง่ายขึ้นจะสั้นกว่า ~ 30% ไม่มีนักแสดงและไม่เทียบเท่า คำถามของฉันจึงหมายถึง: "อะไรคือจุดประสงค์ในการทำให้เป็นผู้เผยแพร่เอง"
sehe

13
ทราบ: วิธีการนี้อ่านไฟล์ลงในบัฟเฟอร์ stringstream stringจากนั้นสำเนาที่กันชนทั้งเข้าไปใน เช่นต้องการหน่วยความจำมากเป็นสองเท่าของตัวเลือกอื่น ๆ (ไม่มีทางที่จะย้ายบัฟเฟอร์) สำหรับไฟล์ขนาดใหญ่นี่อาจเป็นบทลงโทษที่สำคัญบางทีอาจทำให้การจัดสรรล้มเหลว
MM

9
@DanNissenbaum คุณสับสนอะไรบางอย่าง ความรัดกุมเป็นสิ่งสำคัญในการเขียนโปรแกรม แต่วิธีที่เหมาะสมเพื่อให้บรรลุคือการแยกปัญหาออกเป็นส่วน ๆ และใส่เข้าไปในหน่วยอิสระ (ฟังก์ชั่นคลาส ฯลฯ ) การเพิ่มฟังก์ชั่นไม่ได้ทำให้เสียความกระชับ ค่อนข้างตรงกันข้าม
Konrad Rudolph

52

ดูคำตอบนี้ในคำถามที่คล้ายกัน

เพื่อความสะดวกของคุณฉันกำลังโพสต์วิธีแก้ปัญหาของ CTT ใหม่:

string readFile2(const string &fileName)
{
    ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate);

    ifstream::pos_type fileSize = ifs.tellg();
    ifs.seekg(0, ios::beg);

    vector<char> bytes(fileSize);
    ifs.read(bytes.data(), fileSize);

    return string(bytes.data(), fileSize);
}

วิธีแก้ปัญหานี้ส่งผลให้เวลาในการประมวลผลเร็วขึ้นประมาณ 20% เมื่อเทียบกับคำตอบอื่น ๆ ที่แสดงเมื่อใช้ค่าเฉลี่ย 100 ครั้งต่อข้อความของ Moby Dick (1.3M) ไม่เลวสำหรับโซลูชัน C ++ แบบพกพาฉันต้องการดูผลลัพธ์ของ mmap'ing ไฟล์;)


3
ที่เกี่ยวข้อง: การเปรียบเทียบประสิทธิภาพเวลาของวิธีการต่างๆ: การอ่านทั้งไฟล์ในครั้งเดียวใน C ++
jfs

12
จนถึงวันนี้ฉันไม่เคยเห็น tellg () ที่รายงานผลลัพธ์ที่ไม่ใช่ไฟล์ ใช้เวลาหลายชั่วโมงในการค้นหาแหล่งที่มาของข้อบกพร่อง กรุณาอย่าใช้ tellg () เพื่อรับขนาดไฟล์ stackoverflow.com/questions/22984956/…
Puzomor โครเอเชีย

คุณไม่ควรโทรifs.seekg(0, ios::end)มาก่อนtellgหรือ หลังจากเปิดตัวชี้การอ่านไฟล์อยู่ที่จุดเริ่มต้นและtellgส่งกลับค่าเป็นศูนย์
Andriy Tylychko

1
นอกจากนี้คุณต้องตรวจสอบไฟล์ที่ว่างเปล่าเป็นคุณจะ dereference nullptrโดย&bytes[0]
อังเดร Tylychko

ตกลงฉันพลาดios::ateดังนั้นฉันคิดว่ารุ่นที่มีการย้ายไปยังจุดสิ้นสุดอย่างชัดเจนจะสามารถอ่านได้มากขึ้น
Andriy Tylychko

50

ตัวแปรที่สั้นที่สุด: Live On Coliru

std::string str(std::istreambuf_iterator<char>{ifs}, {});

<iterator>มันต้องมีส่วนหัว

มีบางรายงานว่าวิธีนี้ช้ากว่าการจัดสรรสตริงและใช้งานstd::istream::readล่วงหน้า อย่างไรก็ตามในคอมไพเลอร์สมัยใหม่ที่มีการเปิดใช้งานออพติไมซ์ดูเหมือนว่าจะไม่เป็นเช่นนั้นอีกต่อไปแม้ว่าประสิทธิภาพสัมพัทธ์ของวิธีการต่างๆนั้นจะขึ้นอยู่กับคอมไพเลอร์


7
คุณช่วยอธิบายคำตอบนี้ได้ไหม มันเป็นวิธีที่มีประสิทธิภาพมันไม่อ่านไฟล์ถ่านในเวลาต่อไปเพื่อจัดสรรหน่วยความจำกวน?
Martin Beckett

@MM วิธีที่ฉันอ่านการเปรียบเทียบวิธีนี้ช้ากว่าวิธีการอ่าน C ++ แบบบริสุทธิ์ที่แบ่งเป็น preallocated-buffer
Konrad Rudolph

คุณขวามันเป็นกรณีของชื่อเรื่องความเป็นอยู่ภายใต้โค้ดตัวอย่างมากกว่าข้างต้นนั้น :)
MM

@juzzlin C ++ ไม่ทำงานอย่างนั้น ไม่ต้องการส่วนหัวในสภาพแวดล้อมที่เฉพาะเจาะจงไม่ใช่เหตุผลที่ดีสำหรับคุณที่จะไม่รวมมัน
LF

วิธีนี้จะทำให้การจัดสรรหน่วยความจำซ้ำหลายครั้งหรือไม่
coin cheung

22

ใช้

#include <iostream>
#include <sstream>
#include <fstream>

int main()
{
  std::ifstream input("file.txt");
  std::stringstream sstr;

  while(input >> sstr.rdbuf());

  std::cout << sstr.str() << std::endl;
}

หรือสิ่งที่ใกล้เคียง ฉันไม่มีข้อมูลอ้างอิง stdlib เปิดให้ตรวจสอบตัวเองอีกครั้ง

ใช่ฉันเข้าใจว่าไม่ได้เขียนslurpฟังก์ชั่นตามที่ถาม


มันดูดี แต่ก็ไม่ได้รวบรวม การเปลี่ยนแปลงเพื่อทำให้คอมไพล์ลดคำตอบอื่น ๆ ในหน้านี้ ideone.com/EyhfWm
JDiMatteo

5
ทำไมขณะที่ลูป?
Zitrax

ตกลง เมื่อoperator>>อ่านใน a std::basic_streambufมันจะใช้ (สตรีมที่เหลืออยู่) สตรีมอินพุตดังนั้นการวนซ้ำจึงไม่จำเป็น
Remy Lebeau

15

หากคุณมี C ++ 17 (ระบบไฟล์ std ::) ก็มีวิธีนี้เช่นกัน (ซึ่งทำให้ขนาดของไฟล์ผ่านstd::filesystem::file_sizeแทนที่จะเป็นseekgและtellg):

#include <filesystem>
#include <fstream>
#include <string>

namespace fs = std::filesystem;

std::string readFile(fs::path path)
{
    // Open the stream to 'lock' the file.
    std::ifstream f(path, std::ios::in | std::ios::binary);

    // Obtain the size of the file.
    const auto sz = fs::file_size(path);

    // Create a buffer.
    std::string result(sz, '\0');

    // Read the whole file into the buffer.
    f.read(result.data(), sz);

    return result;
}

หมายเหตุ : คุณอาจต้องใช้<experimental/filesystem>และstd::experimental::filesystemถ้าไลบรารีมาตรฐานของคุณยังไม่รองรับ C ++ 17 นอกจากนี้คุณยังอาจต้องเปลี่ยนresult.data()ด้วย&result[0]ถ้ามันไม่สนับสนุนไม่ใช่ const มาตรฐาน :: basic_string ข้อมูล


1
สิ่งนี้อาจทำให้เกิดพฤติกรรมที่ไม่ได้กำหนด การเปิดไฟล์ในโหมดข้อความจะให้สตรีมที่แตกต่างจากไฟล์ดิสก์ในระบบปฏิบัติการบางระบบ
MM

1
แต่เดิมพัฒนาขึ้นboost::filesystemเพื่อให้คุณสามารถใช้บูสเตอร์ได้ถ้าคุณไม่มี c ++ 17
Gerhard Burger

2
การเปิดไฟล์ด้วย API เดียวและเพิ่มขนาดให้กับอีกอันหนึ่งดูเหมือนว่าจะขอความไม่สอดคล้องและสภาพการแข่งขัน
Arthur Tacca

14

tellg()ฉันไม่ได้มีชื่อเสียงพอที่จะแสดงความคิดเห็นโดยตรงต่อการตอบสนองการใช้

โปรดทราบว่าtellg()สามารถส่งคืน -1 เมื่อเกิดข้อผิดพลาด หากคุณผ่านผลลัพธ์ของtellg()การเป็นพารามิเตอร์การจัดสรรคุณควรมีสติตรวจสอบผลลัพธ์ก่อน

ตัวอย่างของปัญหา:

...
std::streamsize size = file.tellg();
std::vector<char> buffer(size);
...

ในตัวอย่างข้างต้นหากtellg()พบข้อผิดพลาดมันจะกลับ -1 หล่อนัยระหว่างลงนาม (เช่นผลของtellg()) และได้รับการรับรอง (เช่นหาเรื่องกับvector<char>คอนสตรัค) จะส่งผลให้เวกเตอร์ของคุณสมควรจัดสรรมากจำนวนมากของไบต์ (อาจเป็น 4294967295 ไบต์หรือ 4GB)

แก้ไขคำตอบของ paxos1977 สำหรับบัญชีด้านบน:

string readFile2(const string &fileName)
{
    ifstream ifs(fileName.c_str(), ios::in | ios::binary | ios::ate);

    ifstream::pos_type fileSize = ifs.tellg();
    if (fileSize < 0)                             <--- ADDED
        return std::string();                     <--- ADDED

    ifs.seekg(0, ios::beg);

    vector<char> bytes(fileSize);
    ifs.read(&bytes[0], fileSize);

    return string(&bytes[0], fileSize);
}

5

วิธีนี้เพิ่มการตรวจสอบข้อผิดพลาดให้กับวิธีการที่ใช้ rdbuf ()

std::string file_to_string(const std::string& file_name)
{
    std::ifstream file_stream{file_name};

    if (file_stream.fail())
    {
        // Error opening file.
    }

    std::ostringstream str_stream{};
    file_stream >> str_stream.rdbuf();  // NOT str_stream << file_stream.rdbuf()

    if (file_stream.fail() && !file_stream.eof())
    {
        // Error reading file.
    }

    return str_stream.str();
}

ฉันกำลังเพิ่มคำตอบนี้เนื่องจากการเพิ่มการตรวจสอบข้อผิดพลาดไปยังวิธีดั้งเดิมนั้นไม่สำคัญอย่างที่คุณคาดไว้ วิธีการดั้งเดิมใช้ตัวดำเนินการแทรกของ stringstream ( str_stream << file_stream.rdbuf()) ปัญหาคือสิ่งนี้ตั้งค่าความล้มเหลวของสตริงสตรีมเมื่อไม่มีการแทรกตัวอักษร อาจเกิดจากข้อผิดพลาดหรืออาจเกิดจากไฟล์ว่างเปล่า หากคุณตรวจสอบความล้มเหลวโดยการตรวจสอบความล้มเหลวคุณจะพบกับผลบวกปลอมเมื่อคุณอ่านไฟล์เปล่า คุณแก้ความล้มเหลวที่ถูกต้องตามกฎหมายในการแทรกตัวอักขระใด ๆ และ "ความล้มเหลว" เพื่อแทรกตัวอักขระใด ๆ เนื่องจากไฟล์ว่างเปล่า?

คุณอาจคิดว่าจะตรวจสอบไฟล์ว่างอย่างชัดเจน แต่นั่นเป็นรหัสเพิ่มเติมและการตรวจสอบข้อผิดพลาดที่เกี่ยวข้อง

การตรวจสอบสภาพความล้มเหลวstr_stream.fail() && !str_stream.eof()ใช้งานไม่ได้เนื่องจากการดำเนินการแทรกไม่ได้ตั้งค่า eofbit (บน ostringstream หรือ ifstream)

ดังนั้นวิธีแก้ไขคือเปลี่ยนการทำงาน แทนที่จะใช้ตัวดำเนินการแทรกของ ostringstream (<<) ให้ใช้ตัวดำเนินการแยกของ ifstream (>>) ซึ่งตั้งค่า eofbit file_stream.fail() && !file_stream.eof()จากนั้นตรวจสอบสภาพ failiure

ที่สำคัญเมื่อfile_stream >> str_stream.rdbuf()พบความล้มเหลวที่ถูกกฎหมายมันไม่ควรตั้งค่า eofbit (ตามความเข้าใจของฉันเกี่ยวกับข้อมูลจำเพาะ) นั่นหมายความว่าการตรวจสอบข้างต้นเพียงพอที่จะตรวจสอบความล้มเหลวที่ถูกกฎหมาย


3

สิ่งนี้ไม่ควรเลวร้ายเกินไป:

void slurp(std::string& data, const std::string& filename, bool is_binary)
{
    std::ios_base::openmode openmode = ios::ate | ios::in;
    if (is_binary)
        openmode |= ios::binary;
    ifstream file(filename.c_str(), openmode);
    data.clear();
    data.reserve(file.tellg());
    file.seekg(0, ios::beg);
    data.append(istreambuf_iterator<char>(file.rdbuf()), 
                istreambuf_iterator<char>());
}

ข้อได้เปรียบตรงนี้คือเราต้องสำรองก่อนเพื่อที่เราจะได้ไม่ต้องต่อสายเมื่อเราอ่านสิ่งต่าง ๆ ข้อเสียคือเราทำถ่านด้วยถ่าน เวอร์ชันที่ฉลาดขึ้นสามารถคว้า buf การอ่านทั้งหมดแล้วเรียกอันเดอร์โฟลว์


1
คุณควรตรวจสอบรุ่นของรหัสนี้ที่ใช้ std :: vector สำหรับการอ่านครั้งแรกแทนที่จะเป็นสตริง เร็วขึ้นมาก
paxos1977

3

นี่คือรุ่นที่ใช้ไลบรารีระบบไฟล์ใหม่พร้อมการตรวจสอบข้อผิดพลาดที่มีเหตุผล:

#include <cstdint>
#include <exception>
#include <filesystem>
#include <fstream>
#include <sstream>
#include <string>

namespace fs = std::filesystem;

std::string loadFile(const char *const name);
std::string loadFile(const std::string &name);

std::string loadFile(const char *const name) {
  fs::path filepath(fs::absolute(fs::path(name)));

  std::uintmax_t fsize;

  if (fs::exists(filepath)) {
    fsize = fs::file_size(filepath);
  } else {
    throw(std::invalid_argument("File not found: " + filepath.string()));
  }

  std::ifstream infile;
  infile.exceptions(std::ifstream::failbit | std::ifstream::badbit);
  try {
    infile.open(filepath.c_str(), std::ios::in | std::ifstream::binary);
  } catch (...) {
    std::throw_with_nested(std::runtime_error("Can't open input file " + filepath.string()));
  }

  std::string fileStr;

  try {
    fileStr.resize(fsize);
  } catch (...) {
    std::stringstream err;
    err << "Can't resize to " << fsize << " bytes";
    std::throw_with_nested(std::runtime_error(err.str()));
  }

  infile.read(fileStr.data(), fsize);
  infile.close();

  return fileStr;
}

std::string loadFile(const std::string &name) { return loadFile(name.c_str()); };

infile.openยังสามารถยอมรับได้std::stringโดยไม่ต้องแปลงด้วย.c_str()
Matt Eding

filepathไม่ได้เป็นก็เป็นstd::string std::filesystem::pathปรากฎว่าstd::ifstream::openสามารถยอมรับหนึ่งในนั้นเช่นกัน
เดวิดจี

@DavidG std::filesystem::pathแปลงโดยปริยายเป็นstd::string
Jeffrey Cash

ตาม cppreference.com ::openฟังก์ชั่นสมาชิกในstd::ifstreamนั้นยอมรับstd::filesystem::pathดำเนินการราวกับว่า::c_str()วิธีการที่ถูกเรียกบนเส้นทาง พื้นฐาน::value_typeของเส้นทางอยู่charภายใต้ POSIX
David G

2

คุณสามารถใช้ฟังก์ชัน 'std :: getline' และระบุ 'eof' เป็นตัวคั่น โค้ดที่ได้นั้นค่อนข้างคลุมเครือเล็กน้อย:

std::string data;
std::ifstream in( "test.txt" );
std::getline( in, data, std::string::traits_type::to_char_type( 
                  std::string::traits_type::eof() ) );

5
ฉันเพิ่งทดสอบสิ่งนี้ดูเหมือนว่าจะช้ากว่าการเรียกขนาดไฟล์และเรียกการอ่านสำหรับขนาดไฟล์ทั้งหมดเป็นบัฟเฟอร์ ตามลำดับ 12x ช้ากว่า
David

สิ่งนี้จะใช้งานได้ตราบใดที่ไม่มีอักขระ "eof" (เช่น 0x00, 0xff, ... ) อักขระในไฟล์ของคุณ หากมีคุณจะอ่านเพียงบางส่วนของไฟล์
Olaf Dietsche

2

อย่าเขียนลงในบัฟเฟอร์ std :: string ของ const char * ไม่เลย! การทำเช่นนั้นเป็นความผิดพลาดครั้งใหญ่

พื้นที่ Reserve () สำหรับสตริงทั้งหมดใน std :: string ของคุณอ่าน chunks จากไฟล์ที่มีขนาดที่เหมาะสมลงในบัฟเฟอร์และผนวก () ขนาดของชิ้นงานใหญ่ขึ้นอยู่กับขนาดไฟล์อินพุตของคุณ ฉันค่อนข้างมั่นใจว่ากลไกแบบพกพาและ STL ที่เป็นไปตามมาตรฐานอื่น ๆ จะทำเช่นเดียวกัน (แต่อาจดูดีกว่า)


5
ตั้งแต่ C ++ 11 รับประกันว่าจะโอเคที่จะเขียนลงในstd::stringบัฟเฟอร์โดยตรง และฉันเชื่อว่ามันทำงานได้อย่างถูกต้องกับการใช้งานจริงทั้งหมดก่อนหน้านั้น
MM

1
ตั้งแต่ C ++ 17 เรายังมีที่ไม่ const std::string::data()วิธีการสำหรับการปรับเปลี่ยนสตริง buffer &str[0]โดยตรงโดยไม่ต้องหันไปใช้เทคนิคเช่น
zett42

เห็นด้วยกับ @ zett42 คำตอบนี้ไม่ถูกต้องตามข้อเท็จจริงแล้ว
jeremyong

0
#include <string>
#include <sstream>

using namespace std;

string GetStreamAsString(const istream& in)
{
    stringstream out;
    out << in.rdbuf();
    return out.str();
}

string GetFileAsString(static string& filePath)
{
    ifstream stream;
    try
    {
        // Set to throw on failure
        stream.exceptions(fstream::failbit | fstream::badbit);
        stream.open(filePath);
    }
    catch (system_error& error)
    {
        cerr << "Failed to open '" << filePath << "'\n" << error.code().message() << endl;
        return "Open fail";
    }

    return GetStreamAsString(stream);
}

การใช้งาน:

const string logAsString = GetFileAsString(logFilePath);

0

ฟังก์ชั่นที่ได้รับการปรับปรุงซึ่งสร้างขึ้นบนโซลูชันของ CTT

#include <string>
#include <fstream>
#include <limits>
#include <string_view>
std::string readfile(const std::string_view path, bool binaryMode = true)
{
    std::ios::openmode openmode = std::ios::in;
    if(binaryMode)
    {
        openmode |= std::ios::binary;
    }
    std::ifstream ifs(path.data(), openmode);
    ifs.ignore(std::numeric_limits<std::streamsize>::max());
    std::string data(ifs.gcount(), 0);
    ifs.seekg(0);
    ifs.read(data.data(), data.size());
    return data;
}

มีความแตกต่างที่สำคัญสองประการ:

tellg()ไม่รับประกันว่าจะคืนค่าออฟเซ็ตเป็นไบต์ตั้งแต่เริ่มต้นไฟล์ ในขณะที่ Puzomor Croatia ชี้ว่ามันเป็นโทเค็นที่สามารถใช้ในการโทรแบบสตรีมได้ gcount()อย่างไรก็ตามจะคืนจำนวนไบต์ที่ยังไม่ฟอร์แมตที่แยกล่าสุด ดังนั้นเราจึงเปิดไฟล์แยกและทิ้งเนื้อหาทั้งหมดด้วยignore()เพื่อให้ได้ขนาดของไฟล์และสร้างสตริงเอาต์พุตตามนั้น

ประการที่สองเราหลีกเลี่ยงการคัดลอกข้อมูลของไฟล์จาก a std::vector<char>ไปยัง a std::stringโดยเขียนไปยังสตริงโดยตรง

ในแง่ของประสิทธิภาพนี่ควรเป็นวิธีที่เร็วที่สุดในการจัดสรรสตริงที่มีขนาดเหมาะสมก่อนเวลาและโทรread()หนึ่งครั้ง ในฐานะที่เป็นข้อเท็จจริงที่น่าสนใจการใช้ignore()และcountg()แทนที่จะเป็นateและtellg()ใน gcc คอมไพล์ลงจนเกือบเหมือนกันทีละนิด


1
รหัสนี้ใช้ไม่ได้ฉันได้รับสตริงที่ว่างเปล่า ฉันคิดว่าคุณต้องการifs.seekg(0)แทนifs.clear()(แล้วมันทำงาน)
Xeverous

-1
#include <iostream>
#include <fstream>
#include <string.h>
using namespace std;
main(){
    fstream file;
    file.open("test.txt");
    string copy,temp;
    while(getline(file,temp)){
        copy+=temp;
        copy+="\n";
    }
    cout<<copy;
    file.close();
}

1
กรุณาเพิ่มคำอธิบาย
ปีเตอร์

กรุณาเยี่ยมชมและตรวจสอบวิธีการที่จะตอบคำถาม
Yunus Temurlenk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.