是否定义了 ofstream 实现的默认模式?
Is the default mode of ofstream implementation defined?
给定以下代码:
std::ofstream stream("somefile");
if (!stream)
{
return 1;
}
调用 .write(....) 并使用 stdc++ 和 libc++ 时流处于二进制模式 (std::ios::binary
)。
然而,当使用 MSVC (2015/2017RC1) 时,它似乎处于文本模式或其他奇怪的东西,因为生成的文件比实际写的要大。
但如果我明确设置模式 std::ios::binary
MSVC 的行为类似于前面提到的其他标准库的 std::ofstream
实现。
示例代码:
#include <vector>
#include <cstdio>
#include <fstream>
std::size_t fsz(const char* filename) {
std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
return static_cast<std::size_t>(in.tellg());
}
int main() {
std::ofstream stream("filename");
if (!stream)
return 1;
std::vector<unsigned long long int> v = {0x6F1DA2C6AC0E0EA6, 0x42928C47B18C31A2, 0x95E20A7699DC156A, 0x19F9C94F27FFDBD0};
stream.write(reinterpret_cast<const char*>(v.data()),v.size() * sizeof(unsigned long long int));
stream.close();
printf("expect: %d\n", v.size() * sizeof(unsigned long long int));
printf("file size: %d\n", fsz("filename"));
return 0;
}
使用 msvc 运行 时上述代码的输出:
expect: 32
file size: 33
使用 libc++、stdc++ 时 运行 以上代码的输出:
expect: 32
file size: 32
差异可能会更大,这取决于写入的数据量和数据内容。
最后我的问题还是一样,是未定义的还是未指定的行为?
将上面的向量更改为以下内容可以使示例更清楚地说明发生了什么。
std::vector<unsigned long long int> v = {0x0A0A0A0A0A0A0A0A, 0x0A0A0A0A0A0A0A0A, 0x0A0A0A0A0A0A0A0A, 0x0A0A0A0A0A0A0A0A};
流构造器使用的默认模式是ios_base::out
。由于没有明确的 text
模式标志,这意味着流以文本模式打开。文本模式仅对 Windows 系统有影响,它将 \n
个字符转换为 CR/LF 对。在 POSIX 系统上它不起作用,文本和二进制模式在这些系统上是同义词。
当我使用 g++
和 libstdc++
运行 你在 windows 上的代码时,我得到以下结果:
expect: 32
file size: 33
所以问题不是特定于编译器的,而是 OS 特定的。
虽然 C++ 使用单个字符 \n
来表示以字符串结尾的行,但 Windows 使用两个字节 0x0D
和 0x0A
来表示以字符串结尾的行文件。这意味着如果您以文本模式将字符串写入文件,则所有出现的单个字符 \n
都将使用这两个字节写入。这就是为什么您的示例文件大小会增加额外字节的原因。
给定以下代码:
std::ofstream stream("somefile");
if (!stream)
{
return 1;
}
调用 .write(....) 并使用 stdc++ 和 libc++ 时流处于二进制模式 (std::ios::binary
)。
然而,当使用 MSVC (2015/2017RC1) 时,它似乎处于文本模式或其他奇怪的东西,因为生成的文件比实际写的要大。
但如果我明确设置模式 std::ios::binary
MSVC 的行为类似于前面提到的其他标准库的 std::ofstream
实现。
示例代码:
#include <vector>
#include <cstdio>
#include <fstream>
std::size_t fsz(const char* filename) {
std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
return static_cast<std::size_t>(in.tellg());
}
int main() {
std::ofstream stream("filename");
if (!stream)
return 1;
std::vector<unsigned long long int> v = {0x6F1DA2C6AC0E0EA6, 0x42928C47B18C31A2, 0x95E20A7699DC156A, 0x19F9C94F27FFDBD0};
stream.write(reinterpret_cast<const char*>(v.data()),v.size() * sizeof(unsigned long long int));
stream.close();
printf("expect: %d\n", v.size() * sizeof(unsigned long long int));
printf("file size: %d\n", fsz("filename"));
return 0;
}
使用 msvc 运行 时上述代码的输出:
expect: 32
file size: 33
使用 libc++、stdc++ 时 运行 以上代码的输出:
expect: 32
file size: 32
差异可能会更大,这取决于写入的数据量和数据内容。
最后我的问题还是一样,是未定义的还是未指定的行为?
将上面的向量更改为以下内容可以使示例更清楚地说明发生了什么。
std::vector<unsigned long long int> v = {0x0A0A0A0A0A0A0A0A, 0x0A0A0A0A0A0A0A0A, 0x0A0A0A0A0A0A0A0A, 0x0A0A0A0A0A0A0A0A};
流构造器使用的默认模式是ios_base::out
。由于没有明确的 text
模式标志,这意味着流以文本模式打开。文本模式仅对 Windows 系统有影响,它将 \n
个字符转换为 CR/LF 对。在 POSIX 系统上它不起作用,文本和二进制模式在这些系统上是同义词。
当我使用 g++
和 libstdc++
运行 你在 windows 上的代码时,我得到以下结果:
expect: 32
file size: 33
所以问题不是特定于编译器的,而是 OS 特定的。
虽然 C++ 使用单个字符 \n
来表示以字符串结尾的行,但 Windows 使用两个字节 0x0D
和 0x0A
来表示以字符串结尾的行文件。这意味着如果您以文本模式将字符串写入文件,则所有出现的单个字符 \n
都将使用这两个字节写入。这就是为什么您的示例文件大小会增加额外字节的原因。