赞
踩
最近踩的一个坑,填这个坑大概花了我1个小时,记录下来希望可以帮助大家避免跟我一样(捂脸)。事情是这样,我最近需要一个c++的简单的记log到文件的功能,就是一个比较临时的功能,我第一反应就是用c++文件流标准库简单实现。很自然我就凭着记忆写下了下边的代码(因为算是临时的一个功能,错误判断也就没有加了,一切简单粗暴,切勿模仿):
std::ofstream simpleLog;
simpleLog.open(logFile, std::ios::app | std::ios::trunc);
【代码框可左右滑动以查看更多】
一切很简单完美,对吧?嘿嘿,我对自己写的代码还是比较自恋的(如果你看出问题,就不用往下看了,顺便记得留言让我膜拜一下,)。接下来我就开始写了不少用simpleLog进行log输出的相关代码,写完很潇洒地点了下运行按钮。我一边得意地等待编译和运行完成,一边准备去看log输出。等程序运行结束后发现,咦?怎么没有log文件生成?什么鬼?没办法,开始一步一步地加上文件操作的错误判断,然后发现simpleLog.open()函数调用后,simpleLog.bad()函数就返回true了。小问题,这还难得到我?肯定是文件路径或文件权限不对,我简单检查了下,没有问题呀。到了这个时候,感觉开始有点不可思议了,难道又要来玄学了?不不不,还是要相信科学。因为在Windows下开发,我又在simpleLog.open()失败后的分支里边使用Windows的错误码获取函数GetLastErrror()获取Windows的系统错误码。因为文件操作主要是系统api调用,所以猜测是系统api调用失败了。打印出来后发现错误码是2,在Windows的System Error Codes中查了下2的定义如下:
ERROR_FILE_NOT_FOUND
2 (0x2)
The system cannot find the file specified.
【代码框可左右滑动以查看更多】
找不到文件?我再一个一个字符地检查了一遍文件路径和文件名,还是没有错误呀(开始有点进入玄学状态)。Windows系统又不开源,对我来说就是个黑盒,还是要找我们的老朋友Linux。我在Linux下快速撸了个test.cpp作为验证的小demo,并在判断simpleLog.open()失败后通过strerror(errno)获取错误信息,代码如下:
int main()
{
ofstream f;
f.open("./abc.txt", ios::app | ios::trunc);
if (f.bad()) {
cout << "open bad:" << strerror(errno) << endl;
} else {
cout << "open good" << endl;
}
f << "write something" << endl;
f.close();
}
然后我们来编译并运行,看看输出:
$g++ -Wall -g -std=c++11 test.cpp -o test && ./test
open good
$ cat abc.txt
cat: abc.txt: No such file or directory
咦?输出"open good",但是当前目录下却找不到输出的文体abc.txt,真是见鬼了。好在我们的老朋友Linux对我们很开放,我们想看它做了什么都可以。我用strace命令跟踪执行了./test, 命令及输出如下:
$strace ./test
execve("./t", ["./t"], [/* 21 vars */]) = 0
brk(NULL) = 0x2514000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=38005, ...}) = 0
mmap(NULL, 38005, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fa9d1097000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/usr/lib/x86_64-linux-gnu/libstdc++.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \235\10\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=1566440, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa9d1096000
mmap(NULL, 3675136, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa9d0afa000
mprotect(0x7fa9d0c6c000, 2097152, PROT_NONE) = 0
mmap(0x7fa9d0e6c000, 49152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x172000) = 0x7fa9d0e6c000
mmap(0x7fa9d0e78000, 13312, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa9d0e78000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p*\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=89696, ...}) = 0
mmap(NULL, 2185488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa9d08e4000
mprotect(0x7fa9d08fa000, 2093056, PROT_NONE) = 0
mmap(0x7fa9d0af9000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15000) = 0x7fa9d0af9000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\t\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0
mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa9d051a000
mprotect(0x7fa9d06da000, 2097152, PROT_NONE) = 0
mmap(0x7fa9d08da000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7fa9d08da000
mmap(0x7fa9d08e0000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fa9d08e0000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0V\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=1088952, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa9d1095000
mmap(NULL, 3178744, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fa9d0211000
mprotect(0x7fa9d0319000, 2093056, PROT_NONE) = 0
mmap(0x7fa9d0518000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x107000) = 0x7fa9d0518000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa9d1094000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa9d1092000
arch_prctl(ARCH_SET_FS, 0x7fa9d1092740) = 0
mprotect(0x7fa9d08da000, 16384, PROT_READ) = 0
mprotect(0x7fa9d0518000, 4096, PROT_READ) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fa9d1091000
mprotect(0x7fa9d0e6c000, 40960, PROT_READ) = 0
mprotect(0x601000, 4096, PROT_READ) = 0
mprotect(0x7fa9d10a1000, 4096, PROT_READ) = 0
munmap(0x7fa9d1097000, 38005) = 0
brk(NULL) = 0x2514000
brk(0x2546000) = 0x2546000
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 6), ...}) = 0
write(1, "open good\n", 10open good
) = 10
exit_group(0) = ?
+++ exited with 0 +++
大家认真看下,发现什么了没有?竟然没有open("abc.txt", ...)的系统调用,哇塞,这是什么鬼?到了这个时候,我不得不低下我骄傲的头颅,到c++ reference查文档去了,。看到cpp reference的open里边写着:
打开参考文档,我第一时间看了下openmode的解释(上图红框部分)。app(append的缩写),每次写之前先定位到stream的最后边,跟我记忆一样,没有问题。trunc(truncate的缩写),打开文件的时候丢弃现有文件里边的内容,跟我记忆中也一样,没毛病。然后看了下函数介绍,发现这个函数只是代理,后边继续调用了rdbuf()->open(filename, mode),然后我顺着介绍链接点开 std::basic_filebuf::open的文档,里边亮了!
看最后红框框住的这句话:如果openmode的组合不是上边列出来的之一,open()调用将失败!哇咔咔!然后再看,app 和 trunc 同时使用的情况并不在上边列表!(这里我要吐槽下,c++的文件流的错误处理做的真不够好,只提高了一个标志位告诉使用者成功还是失败,没有具体的错误码,这样让使用的人定位原因的时候很痛苦呀,捂脸。)
终于真相大白了,我们把std::ios:app去掉看看:
int main()
{
ofstream f;
f.open("./abc.txt", ios::trunc);
if (f.bad()) {
cout << "open bad:" << strerror(errno) << endl;
} else {
cout << "open good" << endl;
}
f << "write something" << endl;
f.close();
}
然后我们来编译并运行,输出还是一样:
$g++ -Wall -g -std=c++11 t.cpp -o t && ./t
open good
$ g++ -Wall -g -std=c++11 t.cpp -o t && ./t
open good
$ cat abc.txt
write something
文件终于出来了!
我们再strace看下:
$ strace ./t
execve("./t", ["./t"], [/* 21 vars */]) = 0
brk(NULL) = 0xc0e000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=38005, ...}) = 0
mmap(NULL, 38005, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f1c58c61000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/usr/lib/x86_64-linux-gnu/libstdc++.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \235\10\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=1566440, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1c58c60000
mmap(NULL, 3675136, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1c586c4000
mprotect(0x7f1c58836000, 2097152, PROT_NONE) = 0
mmap(0x7f1c58a36000, 49152, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x172000) = 0x7f1c58a36000
mmap(0x7f1c58a42000, 13312, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f1c58a42000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0p*\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=89696, ...}) = 0
mmap(NULL, 2185488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1c584ae000
mprotect(0x7f1c584c4000, 2093056, PROT_NONE) = 0
mmap(0x7f1c586c3000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x15000) = 0x7f1c586c3000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`\t\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0
mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1c580e4000
mprotect(0x7f1c582a4000, 2097152, PROT_NONE) = 0
mmap(0x7f1c584a4000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7f1c584a4000
mmap(0x7f1c584aa000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f1c584aa000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\0V\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0644, st_size=1088952, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1c58c5f000
mmap(NULL, 3178744, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1c57ddb000
mprotect(0x7f1c57ee3000, 2093056, PROT_NONE) = 0
mmap(0x7f1c580e2000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x107000) = 0x7f1c580e2000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1c58c5e000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1c58c5c000
arch_prctl(ARCH_SET_FS, 0x7f1c58c5c740) = 0
mprotect(0x7f1c584a4000, 16384, PROT_READ) = 0
mprotect(0x7f1c580e2000, 4096, PROT_READ) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1c58c5b000
mprotect(0x7f1c58a36000, 40960, PROT_READ) = 0
mprotect(0x601000, 4096, PROT_READ) = 0
mprotect(0x7f1c58c6b000, 4096, PROT_READ) = 0
munmap(0x7f1c58c61000, 38005) = 0
brk(NULL) = 0xc0e000
brk(0xc40000) = 0xc40000
open("./abc.txt", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 6), ...}) = 0
write(1, "open good\n", 10open good
) = 10
write(3, "write something\n", 16) = 16
close(3) = 0
exit_group(0) = ?
+++ exited with 0 +++
open("./abc.txt", ...)系统调用终于有了!一个小时也过去了,捂脸。希望大家注意下,c++文件流的openmode,只有在上边表格列出来的组合才是允许的,像app 与 trunc 组合是不允许的,将导致打开文件失败。还有个疑问,为什么Windows下报ERROR_FILE_NOT_FOUND,Linux下却是没有错误?其实都没有到系统调用,获取到的错误码是之前的历史值,没有任何意义。
最后建议大家查cpp标准的时候使用cppreference.com,不要用cplusplus.com,cplusplus.com很久没有更新了,也不全,比如上边的openmode组合表格cplusplus.com那边就没有。
更多最新文章,请扫描下边二维码,关注公众号:学习者说
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。