C ++에서 전체 파일을 std :: string으로 읽는 방법은 무엇입니까? 번에 읽는 방법은 무엇입니까? 텍스트 또는 이진 모드는

파일을로 읽는 방법 std::string, 즉 전체 파일을 한 번에 읽는 방법은 무엇입니까?

텍스트 또는 이진 모드는 호출자가 지정해야합니다. 솔루션은 표준을 준수하고 휴대 가능하며 효율적이어야합니다. 문자열 데이터를 불필요하게 복사해서는 안되며 문자열을 읽는 동안 메모리 재 할당을 피해야합니다.

의 파일 크기를 찍으하는 것이 작업을 수행하는 한 가지 방법은 크기를 조정 std::string하고 fread()std::string‘의 const_cast<char*>()‘에드 data(). 이를 위해서는 std::string의 데이터가 표준에 필요하지 않은 연속적이어야하지만 모든 알려진 구현의 경우 인 것 같습니다. 더 나쁜 것은, 파일을 텍스트 모드에서 읽는 경우 std::string의 크기가 파일의 크기와 같지 않을 수 있습니다.

완전히 올바른, 표준 준수 및 휴대용 솔루션을 사용하여 구성 할 수 std::ifstream의를 rdbuf()std::ostringstream와에 거기에서 std::string. 그러나 이것은 문자열 데이터를 복사하거나 메모리를 불필요하게 재 할당 할 수 있습니다.

  • 모든 관련 표준 라이브러리 구현이 불필요한 오버 헤드를 피할 수있을만큼 똑똑합니까?
  • 다른 방법이 있습니까?
  • 이미 원하는 기능을 제공하는 숨겨진 부스트 기능이 누락 되었습니까?

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


답변

한 가지 방법은 스트림 버퍼를 별도의 메모리 스트림으로 플러시하고 다음으로 변환하는 것입니다 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;
}

답변

비슷한 질문에 대한 이 답변 을 참조하십시오 .

귀하의 편의를 위해 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);
}

이 솔루션은 Moby Dick (1.3M) 텍스트에 대해 평균 100 회 실행하는 경우 여기에 제시된 다른 답변보다 약 20 % 빠른 실행 시간을 제공합니다. 휴대용 C ++ 솔루션에는 나쁘지 않습니다. 파일을 mmap’ing 한 결과를보고 싶습니다.)


답변

가장 짧은 변형 : Live On Coliru

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

헤더가 필요합니다 <iterator>.

이 방법이 문자열을 미리 할당하고 사용하는 것보다 느리다는보고가있었습니다 std::istream::read. 그러나 최적화가 활성화 된 최신 컴파일러에서는 더 이상 그렇지 않은 것처럼 보이지만 다양한 방법의 상대적인 성능은 컴파일러에 크게 의존하는 것으로 보입니다.


답변

사용하다

#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요청대로 함수를 작성하지 않았다는 것을 이해 합니다.


답변

C ++ 17 (std :: filesystem)이있는 경우이 방법도 있습니다 ( 및 std::filesystem::file_size대신 파일 크기를 가져옵니다 ).seekgtellg

#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표준 라이브러리가 아직 완전히 ++ 17 C를 지원하지 않는 경우. 당신은 교체해야 할 수도 있습니다 result.data()&result[0]는 지원하지 않는 경우 const가 아닌 표준 : : basic_string 데이터를 .


답변

를 사용하여 답변에 직접 의견을 올릴만한 명성이 없습니다 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);
}

답변

이 솔루션은 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();
}

원래 방법에 오류 확인을 추가하는 것이 예상만큼 사소하지 않기 때문에이 답변을 추가하고 있습니다. 원래 메소드는 문자열 스트림의 삽입 연산자 ( str_stream << file_stream.rdbuf())를 사용합니다. 문제는 문자가 삽입되지 않을 때 문자열 스트림의 페일 비트를 설정한다는 것입니다. 오류로 인한 것일 수도 있고 파일이 비어 있기 때문일 수도 있습니다. 페일 비트를 검사하여 실패를 확인하면 빈 파일을 읽을 때 오 탐지가 발생합니다. 파일이 비어 있기 때문에 문자를 삽입하지 않는 합법적 인 실패와 문자를 삽입하는 “실패”를 어떻게 명확하게합니까?

빈 파일을 명시 적으로 검사한다고 생각할 수도 있지만 더 많은 코드와 관련 오류 검사입니다.

str_stream.fail() && !str_stream.eof()삽입 조작이 eofbit (ostringstream 또는 ifstream)를 설정하지 않으므로 실패 조건 점검이 작동하지 않습니다.

따라서 해결책은 작업을 변경하는 것입니다. ostringstream의 삽입 연산자 (<<)를 사용하는 대신, eofbit를 설정하는 ifstream의 추출 연산자 (>>)를 사용하십시오. 그런 다음 실패 조건을 확인하십시오 file_stream.fail() && !file_stream.eof().

중요한 file_stream >> str_stream.rdbuf()것은 합법적 인 오류가 발생 했을 때 (사양에 대한 나의 이해에 따라) eofbit를 설정해서는 안됩니다. 즉, 위의 점검은 합법적 인 오류를 감지하기에 충분하다는 것을 의미합니다.