istream_iterator 行为误区
istream_iterator behavior misunderstanding
目标是从二进制文件中读取16位有符号整数。首先,我将文件作为 ifstream 打开,然后我想使用 [=23 将每个数字复制到一个向量中=]istream_iterator 和 copy 算法。
我不明白这段代码有什么问题:
int main(int argc, char *argv[]) {
std::string filename("test.bin");
std::ifstream is(filename);
if (!is) {
std::cerr << "Error while opening input file\n";
return EXIT_FAILURE;
}
std::noskipws(is);
std::vector<int16_t> v;
std::copy(
std::istream_iterator<int16_t>(is),
std::istream_iterator<int16_t>(),
std::back_inserter(v)
);
//v is still empty
}
这段代码没有产生错误,但在调用 std::copy 之后向量仍然为空。因为我在标准输入模式(“文本”模式)下打开文件,所以我期望 istream_iterator 甚至可以工作如果文件是二进制的。当然,关于这个 class.
的行为,我缺少一些东西
首先,要使用 ifstream
读取二进制文件,您需要以 binary
模式打开文件,而不是文本模式(默认)。否则,读取操作可能会错误解释换行符字节并在平台编码(即 CRLF->LF,反之亦然)之间转换它们,从而破坏您的二进制数据。
其次,istream_iterator
使用operator>>
,它默认读取并解析格式化文本,这不是读取二进制文件时想要的。您需要改用 istream::read()
。但是,没有迭代器包装器(但如果需要,您可以编写自己的包装器)。
试试这个:
int main(int argc, char *argv[]) {
std::string filename = "test.bin";
std::ifstream is(filename, std::ifstream::binary);
if (!is) {
std::cerr << "Error while opening input file\n";
return EXIT_FAILURE;
}
std::vector<int16_t> vec;
int16_t value;
while (is.read(reinterpret_cast<char*>(&value), sizeof(value))) {
// swap value's endian, if needed...
vec.push_back(value);
}
// use vec as needed...
return 0;
}
话虽这么说,如果你真的想对二进制文件使用 istream_iterator
,那么你将不得不编写自定义 class/struct 来包装 int16_t
,然后定义一个operator>>
为该类型调用 read()
,例如:
struct myInt16_t {
int16_t value;
operator int16_t() const { return value; }
};
std::istream& operator>>(std::istream &is, myInt16_t &v) {
if (is.read(reinterpret_cast<char*>(&v.value), sizeof(v.value))) {
// swap v.value's endian, if needed...
}
return is;
}
int main(int argc, char *argv[]) {
std::string filename = "test.bin";
std::ifstream is(filename, std::ifstream::binary);
if (!is) {
std::cerr << "Error while opening input file\n";
return EXIT_FAILURE;
}
std::noskipws(is);
std::vector<int16_t> vec;
std::copy(
std::istream_iterator<myInt16_t>(is),
std::istream_iterator<myInt16_t>(),
std::back_inserter(vec)
);
// use vec as needed...
return 0;
}
Since I'm opening the file in the standard input mode ("textual" mode), I was expecting istream_iterator to work even if the file is binary.
您在概念上理解错了。由于文件“是二进制文件”1,你应该不期望istream_iterator
工作即使 文件以“文本模式”打开2。文件的格式决定了您可以用它做什么;没有工具可以从文件中读取“格式为人类可读文本的数字”,除非文件实际上打算以这种方式读取。您的文件大概打算被读取为“字节对,每个字节代表一个 16 位数值”,因此您需要与该格式兼容的工具。文件 mode 只是拼图的一小部分。
要有意义地遍历文件,您需要以二进制模式打开它(以避免在 Windows 平台上损坏)并且 使用一个有能力的工具以您想要的方式解释二进制数据。您还需要确保正确考虑数据。尝试使用像 noskipws
这样的东西是没有意义的,因为 数据没有空白的概念,因为它不代表文本 .
严格来说这并没有什么意思;但通常情况下,说一个文件“是二进制的”表明文件内容 而不是 旨在供人类阅读,并且数值表示为它们在计算机内存中的样子,即直接以256为基数,而不使用字节来表示文本,而文本又使用阿拉伯数字、.
符号等来表示一个数字。
“以文本模式”打开文件的含义取决于您使用的语言和平台。在许多语言(包括 C 和 C++)中,它对 Windows(基本上只是就地翻译 CR-LF 序列)和 none(至少,我最后检查过)对 [=54 的影响很小=]-类似平台。在某些(如 Python 3.x)中,它会自动引入将字节转换为表示实际文本的对象的机制,实际上使用编码而不是假装字节“是字符”(它们不是)。
目标是从二进制文件中读取16位有符号整数。首先,我将文件作为 ifstream 打开,然后我想使用 [=23 将每个数字复制到一个向量中=]istream_iterator 和 copy 算法。 我不明白这段代码有什么问题:
int main(int argc, char *argv[]) {
std::string filename("test.bin");
std::ifstream is(filename);
if (!is) {
std::cerr << "Error while opening input file\n";
return EXIT_FAILURE;
}
std::noskipws(is);
std::vector<int16_t> v;
std::copy(
std::istream_iterator<int16_t>(is),
std::istream_iterator<int16_t>(),
std::back_inserter(v)
);
//v is still empty
}
这段代码没有产生错误,但在调用 std::copy 之后向量仍然为空。因为我在标准输入模式(“文本”模式)下打开文件,所以我期望 istream_iterator 甚至可以工作如果文件是二进制的。当然,关于这个 class.
的行为,我缺少一些东西首先,要使用 ifstream
读取二进制文件,您需要以 binary
模式打开文件,而不是文本模式(默认)。否则,读取操作可能会错误解释换行符字节并在平台编码(即 CRLF->LF,反之亦然)之间转换它们,从而破坏您的二进制数据。
其次,istream_iterator
使用operator>>
,它默认读取并解析格式化文本,这不是读取二进制文件时想要的。您需要改用 istream::read()
。但是,没有迭代器包装器(但如果需要,您可以编写自己的包装器)。
试试这个:
int main(int argc, char *argv[]) {
std::string filename = "test.bin";
std::ifstream is(filename, std::ifstream::binary);
if (!is) {
std::cerr << "Error while opening input file\n";
return EXIT_FAILURE;
}
std::vector<int16_t> vec;
int16_t value;
while (is.read(reinterpret_cast<char*>(&value), sizeof(value))) {
// swap value's endian, if needed...
vec.push_back(value);
}
// use vec as needed...
return 0;
}
话虽这么说,如果你真的想对二进制文件使用 istream_iterator
,那么你将不得不编写自定义 class/struct 来包装 int16_t
,然后定义一个operator>>
为该类型调用 read()
,例如:
struct myInt16_t {
int16_t value;
operator int16_t() const { return value; }
};
std::istream& operator>>(std::istream &is, myInt16_t &v) {
if (is.read(reinterpret_cast<char*>(&v.value), sizeof(v.value))) {
// swap v.value's endian, if needed...
}
return is;
}
int main(int argc, char *argv[]) {
std::string filename = "test.bin";
std::ifstream is(filename, std::ifstream::binary);
if (!is) {
std::cerr << "Error while opening input file\n";
return EXIT_FAILURE;
}
std::noskipws(is);
std::vector<int16_t> vec;
std::copy(
std::istream_iterator<myInt16_t>(is),
std::istream_iterator<myInt16_t>(),
std::back_inserter(vec)
);
// use vec as needed...
return 0;
}
Since I'm opening the file in the standard input mode ("textual" mode), I was expecting istream_iterator to work even if the file is binary.
您在概念上理解错了。由于文件“是二进制文件”1,你应该不期望istream_iterator
工作即使 文件以“文本模式”打开2。文件的格式决定了您可以用它做什么;没有工具可以从文件中读取“格式为人类可读文本的数字”,除非文件实际上打算以这种方式读取。您的文件大概打算被读取为“字节对,每个字节代表一个 16 位数值”,因此您需要与该格式兼容的工具。文件 mode 只是拼图的一小部分。
要有意义地遍历文件,您需要以二进制模式打开它(以避免在 Windows 平台上损坏)并且 使用一个有能力的工具以您想要的方式解释二进制数据。您还需要确保正确考虑数据。尝试使用像 noskipws
这样的东西是没有意义的,因为 数据没有空白的概念,因为它不代表文本 .
严格来说这并没有什么意思;但通常情况下,说一个文件“是二进制的”表明文件内容 而不是 旨在供人类阅读,并且数值表示为它们在计算机内存中的样子,即直接以256为基数,而不使用字节来表示文本,而文本又使用阿拉伯数字、
.
符号等来表示一个数字。“以文本模式”打开文件的含义取决于您使用的语言和平台。在许多语言(包括 C 和 C++)中,它对 Windows(基本上只是就地翻译 CR-LF 序列)和 none(至少,我最后检查过)对 [=54 的影响很小=]-类似平台。在某些(如 Python 3.x)中,它会自动引入将字节转换为表示实际文本的对象的机制,实际上使用编码而不是假装字节“是字符”(它们不是)。