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 str(static_cast<std::stringstream const&>(std::stringstream() << in.rdbuf()).str());
<sstream>
문자열 스트림에 대한 추가 헤더가 필요합니다 . (이것은 평범한 오래된 것을 반환 static_cast
하기 때문에 필요 하지만 실제로 는 캐스트가 안전하다는 것을 알고 있습니다.)operator <<
ostream&
stringstream&
여러 줄로 나누고 임시 변수를 변수로 옮기면 더 읽기 쉬운 코드를 얻습니다.
std::string slurp(std::ifstream& in) {
std::stringstream sstr;
sstr << in.rdbuf();
return sstr.str();
}
또는 다시 한 줄로 :
std::string slurp(std::ifstream& in) {
return static_cast<std::stringstream const&>(std::stringstream() << in.rdbuf()).str();
}
비슷한 질문에 대한 이 답변 을 참조하십시오 .
귀하의 편의를 위해 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
요청대로 함수를 작성하지 않았다는 것을 이해 합니다.
를 사용하여 답변에 직접 의견을 올릴만한 명성이 없습니다 tellg()
.
양해하여 주시기 바랍니다 tellg()
반환 할 수 에러시 -1. tellg()
할당 매개 변수로 결과를 전달하는 경우 결과를 먼저 확인해야합니다.
문제의 예 :
...
std::streamsize size = file.tellg();
std::vector<char> buffer(size);
...
In the above example, if tellg()
encounters an error it will return -1. Implicit casting between signed (ie the result of tellg()
) and unsigned (ie the arg to the vector<char>
constructor) will result in a your vector erroneously allocating a very large number of bytes. (Probably 4294967295 bytes, or 4GB.)
Modifying paxos1977's answer to account for the above:
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);
}
If you have C++17 (std::filesystem), there is also this way (which gets the file's size through std::filesystem::file_size
instead of seekg
and 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 };
// Obtain the size of the file.
const auto sz = fs::file_size(path);
// Create a buffer.
std::string result(sz, ' ');
// Read the whole file into the buffer.
f.read(result.data(), sz);
return result;
}
Note: you may need to use <experimental/filesystem>
and std::experimental::filesystem
if your standard library doesn't yet fully support C++17. You might also need to replace result.data()
with &result[0]
if it doesn't support non-const std::basic_string data.
Something like this shouldn't be too bad:
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>());
}
The advantage here is that we do the reserve first so we won't have to grow the string as we read things in. The disadvantage is that we do it char by char. A smarter version could grab the whole read buf and then call underflow.
Never write into the std::string's const char * buffer. Never ever! Doing so is a massive mistake.
Reserve() space for the whole string in your std::string, read chunks from your file of reasonable size into a buffer, and append() it. How large the chunks have to be depends on your input file size. I'm pretty sure all other portable and STL-compliant mechanisms will do the same (yet may look prettier).
This solution adds error checking to the rdbuf()-based method.
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();
}
I'm adding this answer because adding error-checking to the original method is not as trivial as you'd expect. The original method uses stringstream's insertion operator (str_stream << file_stream.rdbuf()
). The problem is that this sets the stringstream's failbit when no characters are inserted. That can be due to an error or it can be due to the file being empty. If you check for failures by inspecting the failbit, you'll encounter a false positive when you read an empty file. How do you disambiguate legitimate failure to insert any characters and "failure" to insert any characters because the file is empty?
You might think to explicitly check for an empty file, but that's more code and associated error checking.
Checking for the failure condition str_stream.fail() && !str_stream.eof()
doesn't work, because the insertion operation doesn't set the eofbit (on the ostringstream nor the ifstream).
So, the solution is to change the operation. Instead of using ostringstream's insertion operator (<<), use ifstream's extraction operator (>>), which does set the eofbit. Then check for the failiure condition file_stream.fail() && !file_stream.eof()
.
Importantly, when file_stream >> str_stream.rdbuf()
encounters a legitimate failure, it shouldn't ever set eofbit (according to my understanding of the specification). That means the above check is sufficient to detect legitimate failures.
You can use the 'std::getline' function, and specify 'eof' as the delimiter. The resulting code is a little bit obscure though:
std::string data;
std::ifstream in( "test.txt" );
std::getline( in, data, std::string::traits_type::to_char_type(
std::string::traits_type::eof() ) );
#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);
}
usage:
const string logAsString = GetFileAsString(logFilePath);
'IT story' 카테고리의 다른 글
Go에서 C ++를 사용하는 방법? (0) | 2020.06.10 |
---|---|
JavaScript에서 날짜 / 시간을 빼는 방법? (0) | 2020.06.10 |
서버를 다시 시작하지 않고 MySQL 쿼리 캐시 지우기 (0) | 2020.06.10 |
OAuth 인증 코드와 암시 적 워크 플로의 차이점은 무엇입니까? (0) | 2020.06.10 |
MySQL 사용자 이름과 비밀번호를 어떻게 검색합니까? (0) | 2020.06.10 |