C++의 std:: 문자열로 전체 파일을 읽는 방법은 무엇입니까?
을 파을어읽까니로 ?std::string즉, 한 번에 전체 파일을 읽으시겠습니까?
텍스트 또는 이진 모드는 호출자가 지정해야 합니다.솔루션은 표준 준수, 휴대성 및 효율성을 갖추어야 합니다.문자열의 데이터를 불필요하게 복사해서는 안 되며 문자열을 읽는 동안 메모리 재할당을 방지해야 합니다.
한 크기를 를 조정하는 입니다.std::string그리고.fread()std::string의const_cast<char*>()에드data()이를 위해서는 다음이 필요합니다.std::string의 데이터는 표준에서 요구하지 않지만 알려진 모든 구현의 경우에 해당합니다.더 나쁜 것은, 파일이 텍스트 모드에서 읽히면,std::string의 크기가 파일의 크기와 같지 않을 수 있습니다.
다음을 사용하여 완전히 정확하고 표준을 준수하는 휴대용 솔루션을 구성할 수 있습니다.std::ifstream의rdbuf()의 상태가.std::ostringstream그리고 거기서 부터 a로.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++17, 오류 처리 포함).
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);
if (not stream) {
throw std::ios_base::failure("file does not exist");
}
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;
}
가장 짧은 변형:
std::string str(std::istreambuf_iterator<char>{ifs}, {});
에는 헤더 " 가필니다합요헤더"가합니다.<iterator>.
이 하고 사용하는 것보다 .std::istream::read그러나 최적화가 가능한 최신 컴파일러에서는 다양한 방법의 상대적 성능이 컴파일러에 크게 의존하는 것처럼 보이지만 더 이상 그렇지 않은 것 같습니다.
편의를 위해 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(130M) 텍스트에 대해 평균 100회 실행했을 때 여기에 제시된 다른 답변보다 실행 시간이 약 20% 단축되었습니다.휴대용 C++ 솔루션치고는 나쁘지 않습니다, 파일을 mmapping 한 결과를 보고 싶습니다 ;)
system)을 사용하는 ++17(std:: file ).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을 완전히 지원하지 않는 경우. 또교해야할있다니습수도체한다니있▁also습▁you▁need▁to▁replace▁might수도할을 대체해야 할 수도 있습니다.result.data()와 함께&result[0]nonconstd::basic_string 데이터를 지원하지 않는 경우.
사용하다
#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;
}
아니면 아주 가까운 것.내 자신을 다시 확인할 수 있는 stddlib 참조가 없습니다.
네, 제가 작성하지 않은 것은 이해합니다.slurp요구대로 기능합니다.
다음을 사용하여 응답에 대해 직접 언급할 만큼 평판이 좋지 않습니다.tellg().
▁please니▁that▁aware▁be.tellg()오류 시 -1을 반환할 수 있습니다.만약 당신이 그 결과를 통과한다면.tellg()할당 매개 변수로 먼저 결과를 제대로 확인해야 합니다.
문제의 예:
...
std::streamsize size = file.tellg();
std::vector<char> buffer(size);
...
예에서, 위의예에서라면, 약만.tellg()오류가 발생하면 -1을 반환합니다.서명된 사이의 암묵적 주조(즉, 결과)tellg()즉, 에서 arg로)입니다.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);
}
이것이 널리 사용되는 유틸리티처럼 보이기 때문에 제 접근 방식은 특히 부스트 라이브러리가 프로젝트에서 이미 링크되어 있는 경우(linker flags -lboost_system -lboost_filesystem) 수동 솔루션보다 이미 사용 가능한 라이브러리를 검색하고 선호하는 것입니다.여기서 boost는 load_string_file 유틸리티를 제공합니다.
#include <iostream>
#include <string>
#include <boost/filesystem/string_file.hpp>
int main() {
std::string result;
boost::filesystem::load_string_file("aFileName.xyz", result);
std::cout << result.size() << std::endl;
}
장점으로 이 함수는 크기를 결정하기 위해 전체 파일을 검색하지 않고 내부적으로 stat()을 사용합니다.할 수 있는 코드를 할 때 할 수 : 하게 그나무수있단점으로 조정됩니다. 문자열은 불필요하게 크기가 조정됩니다.'\0'파일 내용으로 다시 작성되는 문자입니다.
이 솔루션은 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()삽입 작업이 (ostringstream 또는 ifstream에서) eofit를 설정하지 않기 때문에 작동하지 않습니다.
그래서, 그 해결책은 운영을 바꾸는 것입니다.를 사용하는 ostringstream을 를 합니다. 이 연산자는 ifstream의 삽입 연산자(>)입니다. 다음 인 "고장"을 확인합니다.file_stream.fail() && !file_stream.eof().
중요한 것은, 언제file_stream >> str_stream.rdbuf()합법적인 오류가 발생할 경우, (사양에 대한 제 이해에 따라) 비트를 설정해서는 안 됩니다.즉, 위의 검사는 정당한 오류를 감지하기에 충분합니다.
이와 같은 것은 그리 나쁘지 않을 겁니다.
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>());
}
여기서 장점은 우리가 먼저 예약을 해서 우리가 물건을 읽을 때 줄을 늘릴 필요가 없다는 것입니다.단점은 차로 한다는 것입니다.더 똑똑한 버전은 전체 읽기 버프를 잡은 다음 언더플로우를 호출할 수 있습니다.
다음은 상당히 강력한 오류 검사 기능을 갖춘 새 파일 시스템 라이브러리를 사용하는 버전입니다.
#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()); };
'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() ) );
저는 이것이 매우 오래된 질문이라는 것을 압니다. 하지만 그 중에서 제가 이를 위한 가장 확실한 방법을 고려했을 것이라는 것은 언급하지 않았습니다.네, 저는 이것이 C++이라는 것을 알고 있고, libc를 사용하는 것은 사악하고 틀리거나 무엇이든 하지만, 그것은 미친 짓입니다.libc를 사용하는 것은 괜찮습니다, 특히 이와 같은 간단한 것은 그렇습니다.
기본적으로 파일을 열고 크기(반드시 그 순서대로는 아님)를 지정한 후 읽기만 하면 됩니다.
#include <string>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <sys/stat.h>
static constexpr char filename[] = "foo.bar";
int main(void)
{
FILE *fp = ::fopen(filename, "rb");
if (!fp) {
::perror("fopen");
::exit(1);
}
// Stat isn't strictly part of the standard C library,
// but it's in every libc I've ever seen for a hosted system.
struct stat st;
if (::fstat(::fileno(fp), &st) == (-1)) {
::perror("fstat");
::exit(1);
}
// You could simply allocate a buffer here and use std::string_view, or
// even allocate a buffer and copy it to a std::string. Creating a
// std::string and setting its size is simplest, but will pointlessly
// initialize the buffer to 0. You can't win sometimes.
std::string str;
str.reserve(st.st_size + 1U);
str.resize(st.st_size);
::fread(str.data(), 1, st.st_size, fp);
str[st.st_size] = '\0';
::fclose(fp);
}
이것은 (실제로) 완전히 휴대할 수 있을 뿐만 아니라 다른 솔루션보다 더 나빠 보이지는 않습니다.물론 즉시 종료하는 대신 예외를 던질 수도 있습니다.크기를 조정하는 것은 나를 심각하게 짜증나게 합니다.std::string항상 0이 초기화되지만 어쩔 수 없습니다.
이 작업은 C++17 이상에 대해 작성된 대로만 수행됩니다.이전 버전(편집을 허용하지 않아야 함)std::string::data()이전 버전으로 작업하는 경우 교체를 고려하십시오.str.data()와 함께&str[0].
여러 위치에서 정보를 가져오는 중...이것이 가장 빠르고 최선의 방법입니다.
#include <filesystem>
#include <fstream>
#include <string>
//Returns true if successful.
bool readInFile(std::string pathString)
{
//Make sure the file exists and is an actual file.
if (!std::filesystem::is_regular_file(pathString))
{
return false;
}
//Convert relative path to absolute path.
pathString = std::filesystem::weakly_canonical(pathString);
//Open the file for reading (binary is fastest).
std::wifstream in(pathString, std::ios::binary);
//Make sure the file opened.
if (!in)
{
return false;
}
//Wide string to store the file's contents.
std::wstring fileContents;
//Jump to the end of the file to determine the file size.
in.seekg(0, std::ios::end);
//Resize the wide string to be able to fit the entire file (Note: Do not use reserve()!).
fileContents.resize(in.tellg());
//Go back to the beginning of the file to start reading.
in.seekg(0, std::ios::beg);
//Read the entire file's contents into the wide string.
in.read(fileContents.data(), fileContents.size());
//Close the file.
in.close();
//Do whatever you want with the file contents.
std::wcout << fileContents << L" " << fileContents.size();
return true;
}
글자로 ▁a▁in로 읽힙니다.std::wstring하지만 당신이 단지 일반적인 캐릭터와 a를 원한다면 당신은 쉽게 적응할 수 있습니다.std::string.
#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);
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 크로아티아가 지적했듯이 fstream 호출 내에서 사용할 수 있는 토큰에 더 가깝습니다.gcount()그러나 마지막으로 추출된 포맷되지 않은 바이트의 양을 반환합니다.따라서 파일을 열고, 파일의 모든 내용을 추출하고 폐기합니다.ignore()파일의 크기를 가져오고 이를 기반으로 출력 문자열을 구성합니다.
.std::vector<char>std::string직접 문자열에 기록함으로써.
성능 측면에서, 이것은 적절한 크기의 문자열을 미리 할당하고 호출하는 가장 빠른 속도여야 합니다.read() 번.로, 한번운사서실로것흥는, 하용사로미것▁once.ignore()그리고.countg()에 ate그리고.tellg()on gcc는 조금씩 거의 같은 것으로 압축됩니다.
이것은 제가 사용하는 기능이며, 어떤 이유로 대용량 파일(1GB+)을 다룰 때 std::ifstream:read()가 파일 크기를 알 때 std::ifstream::rdbuf()보다 훨씬 빠르기 때문에 "파일 크기 먼저 확인"하는 것이 실제로 속도 최적화입니다.
#include <string>
#include <fstream>
#include <sstream>
std::string file_get_contents(const std::string &$filename)
{
std::ifstream file($filename, std::ifstream::binary);
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
file.seekg(0, std::istream::end);
const std::streampos ssize = file.tellg();
if (ssize < 0)
{
// can't get size for some reason, fallback to slower "just read everything"
// because i dont trust that we could seek back/fourth in the original stream,
// im creating a new stream.
std::ifstream file($filename, std::ifstream::binary);
file.exceptions(std::ifstream::failbit | std::ifstream::badbit);
std::ostringstream ss;
ss << file.rdbuf();
return ss.str();
}
file.seekg(0, std::istream::beg);
std::string result(size_t(ssize), 0);
file.read(&result[0], std::streamsize(ssize));
return result;
}
성능 면에서 아래 코드보다 빠른 것을 찾을 수 없습니다.
std::string readAllText(std::string const &path)
{
assert(path.c_str() != NULL);
FILE *stream = fopen(path.c_str(), "r");
assert(stream != NULL);
fseek(stream, 0, SEEK_END);
long stream_size = ftell(stream);
fseek(stream, 0, SEEK_SET);
void *buffer = malloc(stream_size);
fread(buffer, stream_size, 1, stream);
assert(ferror(stream) == 0);
fclose(stream);
std::string text((const char *)buffer, stream_size);
assert(buffer != NULL);
free((void *)buffer);
return text;
}
제가 개발한 rst C++ 라이브러리를 사용하면 다음과 같은 작업을 수행할 수 있습니다.
#include "rst/files/file_utils.h"
std::filesystem::path path = ...; // Path to a file.
rst::StatusOr<std::string> content = rst::ReadFile(path);
if (content.err()) {
// Handle error.
}
std::cout << *content << ", " << content->size() << std::endl;
#include <string>
#include <fstream>
int main()
{
std::string fileLocation = "C:\\Users\\User\\Desktop\\file.txt";
std::ifstream file(fileLocation, std::ios::in | std::ios::binary);
std::string data;
if(file.is_open())
{
std::getline(file, data, '\0');
file.close();
}
}
작은 크기에서 중간 크기의 파일의 경우 상당히 빠른 이러한 방법을 사용합니다.반환 문자열은 바이트 배열을 문자열로 "변환"하는 데 사용할 수 있습니다.
auto read_file_bytes(std::string_view filepath) -> std::vector<std::byte> {
std::ifstream ifs(filepath.data(), std::ios::binary | std::ios::ate);
if (!ifs)
throw std::ios_base::failure("File does not exist");
auto end = ifs.tellg();
ifs.seekg(0, std::ios::beg);
auto size = std::size_t(end - ifs.tellg());
if (size == 0) // avoid undefined behavior
return {};
std::vector<std::byte> buffer(size);
if (!ifs.read((char *) buffer.data(), buffer.size()))
throw std::ios_base::failure("Read error");
return buffer;
}
auto read_file_string(std::string_view filepath) -> std::string {
auto bytes = read_file_bytes(filepath);
return std::string(reinterpret_cast<char *>(bytes.begin().base()), bytes.size());
}
std::string의 constchar * 버퍼에 절대 쓰지 마십시오.절대로!그렇게 하는 것은 큰 실수입니다.
std:: string의 전체 문자열에 대한 공간을 예약하고 적절한 크기의 청크를 파일에서 버퍼로 읽은 다음 추가합니다().청크의 크기는 입력 파일 크기에 따라 달라집니다.다른 모든 휴대용 및 STL 호환 메커니즘도 동일하게 작동할 것이라고 확신합니다(그러나 더 예뻐 보일 수도 있습니다).
#include <iostream>
#include <fstream>
#include <string.h>
using namespace std;
main(){
fstream file;
//Open a file
file.open("test.txt");
string copy,temp;
//While loop to store whole document in copy string
//Temp reads a complete line
//Loop stops until temp reads the last line of document
while(getline(file,temp)){
//add new line text in copy
copy+=temp;
//adds a new line
copy+="\n";
}
//Display whole document
cout<<copy;
//close the document
file.close();
}
std::string get(std::string_view const& fn)
{
struct filebuf: std::filebuf
{
using std::filebuf::egptr;
using std::filebuf::gptr;
using std::filebuf::gbump;
using std::filebuf::underflow;
};
std::string r;
if (filebuf fb; fb.open(fn.data(), std::ios::binary | std::ios::in))
{
r.reserve(fb.pubseekoff({}, std::ios::end));
fb.pubseekpos({});
while (filebuf::traits_type::eof() != fb.underflow())
{
auto const gptr(fb.gptr());
auto const sz(fb.egptr() - gptr);
fb.gbump(sz);
r.append(gptr, sz);
}
}
return r;
}
파티에 늦었다는 것은 알고 있지만, 현재(2021년) 제 머신에서 테스트한 것 중 가장 빠른 구현입니다.
#include <fstream>
#include <string>
bool fileRead( std::string &contents, const std::string &path ) {
contents.clear();
if( path.empty()) {
return false;
}
std::ifstream stream( path );
if( !stream ) {
return false;
}
stream >> contents;
return true;
}
언급URL : https://stackoverflow.com/questions/116038/how-do-i-read-an-entire-file-into-a-stdstring-in-c
'programing' 카테고리의 다른 글
| 대량으로 준비된 문을 삽입하는 동안 JavaScript MySQL 오류가 발생했습니다. (0) | 2023.08.05 |
|---|---|
| ES6 클래스 인스턴스의 클래스 이름을 가져옵니다. (0) | 2023.08.05 |
| PowerShell에서 C# 코드 실행으로 개체 또는 여러 값을 반환하려면 어떻게 해야 합니까? (0) | 2023.07.31 |
| 인쇄 스타일시트를 디버깅하기 위한 제안사항은 무엇입니까? (0) | 2023.07.31 |
| 현재 노드를 포함하는 jQuery find(...) 메서드를 찾고 있습니다. (0) | 2023.07.31 |