使用 stringstream 逐行读取文件两次
Read a file line-by-line twice using stringstream
我需要逐行读取一个文件两次。文件内容应适合内存。所以,我通常会将整个文件读入一个缓冲区,然后再使用该缓冲区。
但是,由于我想使用 std::getline
,因此我需要使用 std::basic_istream
。所以,我认为写
是个好主意
std::ifstream file(filepath);
std::stringstream ss;
ss << file.rdbuf();
for (std::string line; std::getline(ss, line);)
{
}
但是,我不确定这里到底发生了什么。我猜 ss << file.rdbuf();
确实 而不是 将文件读入 ss
的任何内部缓冲区。实际文件访问应该只发生在 std::getline(ss, line);
.
因此,使用所提供形式的第二个 for 循环,我应该再次读取整个文件。那是低效的。
我是否正确,因此需要想出其他方法?
第一次循环后,清除 EOF 和失败位并返回到 stringstream
的开头:
ss.clear();
ss.seekg(0, std::ios::beg);
I guess ss << file.rdbuf();
does not read the file into any internal
buffer of ss
. Actual file access should occure only at
std::getline(ss, line);
.
这是不正确的。 cppreference.com 关于 operator<<
过载有这样的说法:
basic_ostream& operator<<( std::basic_streambuf<CharT, Traits>* sb); (9)
9) Behaves as an UnformattedOutputFunction. After constructing and checking the sentry object, checks if sb
is a null pointer. If it is, executes setstate(badbit)
and exits. Otherwise, extracts characters from the input sequence controlled by sb
and inserts them into *this
until one of the following conditions are met:
- end-of-file occurs on the input sequence;
- inserting in the output sequence fails (in which case the character to be inserted is not extracted);
- an exception occurs (in which case the exception is caught).
If no characters were inserted, executes setstate(failbit)
. If an exception was thrown while extracting, sets failbit
and, if failbit
is set in exceptions()
, rethrows the exception.
所以你的假设是不正确的。 file
的全部内容被复制到由 ss
控制的缓冲区,因此从 ss
读取不会访问文件系统。您可以随意通读 ss
并根据需要多次返回开头,而不会每次都产生 re-reading 文件的开销。
Am I correct and hence need to come up with an other approach?
你错了。 "hense" 也是没有根据的。问题中的信息不足,但我怀疑问题与使用流缓冲区无关。
不知道第一个 "garbage" 字符是什么,我不能肯定地说,但我怀疑文件是 wide-character unicode 格式,并且您使用的访问操作不起作用在宽字符上。如果是这样,缓冲文件与问题无关。
作为实验,请尝试以下操作。注意w。
std::wifstream file(filepath);
std::wstringstream ss;
ss << file.rdbuf();
for (int i = 0; i < 42; ++i) {
wchar_t ch;
ss >> ch;
std::cout << static_cast<unsigned>(ch) << ' ';
}
如果前四个数字是 255 254 92 0 或 255 254 47 0,我不会感到惊讶。
我需要逐行读取一个文件两次。文件内容应适合内存。所以,我通常会将整个文件读入一个缓冲区,然后再使用该缓冲区。
但是,由于我想使用 std::getline
,因此我需要使用 std::basic_istream
。所以,我认为写
std::ifstream file(filepath);
std::stringstream ss;
ss << file.rdbuf();
for (std::string line; std::getline(ss, line);)
{
}
但是,我不确定这里到底发生了什么。我猜 ss << file.rdbuf();
确实 而不是 将文件读入 ss
的任何内部缓冲区。实际文件访问应该只发生在 std::getline(ss, line);
.
因此,使用所提供形式的第二个 for 循环,我应该再次读取整个文件。那是低效的。
我是否正确,因此需要想出其他方法?
第一次循环后,清除 EOF 和失败位并返回到 stringstream
的开头:
ss.clear();
ss.seekg(0, std::ios::beg);
I guess
ss << file.rdbuf();
does not read the file into any internal buffer ofss
. Actual file access should occure only atstd::getline(ss, line);
.
这是不正确的。 cppreference.com 关于 operator<<
过载有这样的说法:
basic_ostream& operator<<( std::basic_streambuf<CharT, Traits>* sb); (9)
9) Behaves as an UnformattedOutputFunction. After constructing and checking the sentry object, checks if
sb
is a null pointer. If it is, executessetstate(badbit)
and exits. Otherwise, extracts characters from the input sequence controlled bysb
and inserts them into*this
until one of the following conditions are met:
- end-of-file occurs on the input sequence;
- inserting in the output sequence fails (in which case the character to be inserted is not extracted);
- an exception occurs (in which case the exception is caught).
If no characters were inserted, executes
setstate(failbit)
. If an exception was thrown while extracting, setsfailbit
and, iffailbit
is set inexceptions()
, rethrows the exception.
所以你的假设是不正确的。 file
的全部内容被复制到由 ss
控制的缓冲区,因此从 ss
读取不会访问文件系统。您可以随意通读 ss
并根据需要多次返回开头,而不会每次都产生 re-reading 文件的开销。
Am I correct and hence need to come up with an other approach?
你错了。 "hense" 也是没有根据的。问题中的信息不足,但我怀疑问题与使用流缓冲区无关。
不知道第一个 "garbage" 字符是什么,我不能肯定地说,但我怀疑文件是 wide-character unicode 格式,并且您使用的访问操作不起作用在宽字符上。如果是这样,缓冲文件与问题无关。
作为实验,请尝试以下操作。注意w。
std::wifstream file(filepath);
std::wstringstream ss;
ss << file.rdbuf();
for (int i = 0; i < 42; ++i) {
wchar_t ch;
ss >> ch;
std::cout << static_cast<unsigned>(ch) << ' ';
}
如果前四个数字是 255 254 92 0 或 255 254 47 0,我不会感到惊讶。