istreambuf_iterator何时抛出异常?当底层流尝试读取目录时,我收到异常,但在其他情况下则没有。具体来说,我从Jan-Philip Gehrcke的blog中抓取了脚本readfile_tests.sh
并略微修改了它:
$ cat readfile_tests.sh
#!/bin/bash
COMPILATION_SOURCE=$1
NE_FILE="na"
EMPTY_FILE="empty_file"
ONE_LINE_FILE="one_line_file"
INVALID_LINE_FILE="invalid_line_file"
FILE_READ="file_read"
FILE_WRITTEN="file_written"
FILE_DENIED="/root/.bashrc"
DIR="dir"
# compile test program, resulting in a.out executable
g++ -std=c++11 $COMPILATION_SOURCE
# create test files / directories and put them in the desired state
touch $EMPTY_FILE
if [[ ! -d $DIR ]]; then
mkdir $DIR
fi
echo "rofl" > $ONE_LINE_FILE
echo -ne "validline\ninvalidline" > $INVALID_LINE_FILE
echo "i am opened to read from" > $FILE_READ
python -c 'import time; f = open("'$FILE_READ'"); time.sleep(4)' &
echo "i am opened to write to" > $FILE_WRITTEN
python -c 'import time; f = open("'$FILE_WRITTEN'", "a"); time.sleep(4)' &
# execute test cases
echo "******** testing on non-existent file.."
./a.out $NE_FILE
echo
echo "******** testing on empty file.."
./a.out $EMPTY_FILE
echo
echo "******** testing on valid file with one line content"
./a.out $ONE_LINE_FILE
echo
echo "******** testing on a file with one valid and one invalid line"
./a.out $INVALID_LINE_FILE
echo
echo "******** testing on a file that is read by another process"
./a.out $FILE_READ
echo
echo "******** testing on a file that is written to by another process"
./a.out $FILE_WRITTEN
echo
echo "******** testing on a /root/.bashrc (access should be denied)"
./a.out $FILE_DENIED
echo
echo "******** testing on a directory"
./a.out $DIR
然后,我在以下程序中运行测试
$ cat test.cpp
#include <iostream>
#include <vector>
#include <iterator>
#include <fstream>
#include <string>
int main(int argc, char* argv[]) {
if(argc != 2) {
std::cerr << "Provide one argument" << std::endl;
return EXIT_FAILURE;
}
std::ifstream fin(argv[1]);
std::istreambuf_iterator <char> eos;
std::istreambuf_iterator <char> fin_it(fin.rdbuf());
std::string str;
try {
std::copy(fin_it,eos,std::back_inserter(str));
} catch(...) {
perror("Error");
throw;
}
std::cout << str;
return EXIT_SUCCESS;
}
运行后,我收到了以下结果:
$ ./readfile_tests.sh test.cpp
******** testing on non-existent file..
******** testing on empty file..
******** testing on valid file with one line content
rofl
******** testing on a file with one valid and one invalid line
validline
invalidline
******** testing on a file that is read by another process
i am opened to read from
******** testing on a file that is written to by another process
i am opened to write to
******** testing on a /root/.bashrc (access should be denied)
******** testing on a directory
Error: Is a directory
terminate called after throwing an instance of 'std::ios_base::failure'
what(): basic_filebuf::underflow error reading the file
./readfile_tests.sh: line 51: 22563 Aborted ./a.out $DIR
这些结果对我来说很奇怪,因为在读取目录测试时抛出异常,但在尝试阅读/root/.bashrc
时没有抛出异常。这样,istreambuf_iterator何时抛出异常?如果重要,我使用gcc version 4.7.3
。
是的,打开一个目录进行阅读是非标准和坏的。与此同时,我需要正确地捕捉这个案例。根据下面的评论,这是一段试图从目录中读取的代码:
#include <iostream>
#include <vector>
#include <iterator>
#include <fstream>
#include <string>
void withIterators() {
std::ifstream fin("mydirectory");
if(!fin.is_open())
perror("Error opening file");
std::istreambuf_iterator <char> eos;
std::istreambuf_iterator <char> fin_it(fin.rdbuf());
std::string str;
try {
std::copy(fin_it,eos,std::back_inserter(str));
} catch(...) {
perror("Error reading file");
}
}
void noIterators() {
std::ifstream fin("mydirectory");
if(!fin.is_open())
perror("Error opening file");
std::string line;
while(getline(fin,line)) {}
if(fin.bad())
perror("Error reading file");
}
int main() {
withIterators();
noIterators();
}
运行后,我们收到输出:
Error reading file: Is a directory
Error reading file: Is a directory
这告诉我们两件事。首先,我们无法理解我们在fin
的构造函数之后将目录作为文件打开的事实。其次,仅在使用迭代器时抛出异常。上面的代码noIterators
在从目录中读取时不会引发异常。相反,它设置了badbit。为什么代码会在一种情况下抛出异常并在另一种情况下设置badbit?
我从上面扩展了目录开放代码,以便更好地追踪正在进行的操作
#include <iostream>
#include <vector>
#include <iterator>
#include <fstream>
#include <string>
void withIterators() {
std::ifstream fin("mydirectory");
if(!fin.is_open())
perror("Error opening file");
std::istreambuf_iterator <char> eos;
std::istreambuf_iterator <char> fin_it(fin.rdbuf());
std::string str;
try {
std::copy(fin_it,eos,std::back_inserter(str));
} catch(...) {
perror("Error reading file");
}
}
void withIteratorsUnderflow() {
std::ifstream fin("mydirectory");
try {
fin.rdbuf()->sgetc();
} catch(...) {
perror("Error reading file");
}
}
void withOtherIterators() {
std::ifstream fin("mydirectory");
if(!fin.is_open())
perror("Error opening file");
std::istream_iterator <char> eos;
try {
std::istream_iterator <char> fin_it(fin);
} catch(...) {
perror("Error making iterator");
}
}
void noIterators() {
std::ifstream fin("mydirectory");
if(!fin.is_open())
perror("Error opening file");
std::string line;
while(getline(fin,line)) {}
if(fin.bad())
perror("Error reading file");
}
int main() {
withIterators();
withIteratorsUnderflow();
withOtherIterators();
noIterators();
}
基本上,我们会在不同的地方获得例外与否,但出于类似的原因。
在withIterators()
上,对copy
的调用最终会调用代码/usr/lib/gcc/i686-pc-linux-gnu/4.7.3/include/g++-v4/bits/streambuf_iterator.h:187
,其中包含
else if (!traits_type::eq_int_type((__ret = _M_sbuf->sgetc())
对sgetc()
的调用会引发异常。可能,这是在@ user657267建议的情况下调用下溢。
在withIteratorsUnderflow()
上,我们看到sgetc
直接抛出了异常,这证实了withIterators()
会发生什么。
在withOtherIterators()
上,我们在创建std::istream_iterator
后立即抛出异常。这与std::istreambuf_iterator
的情况不同,后者直到稍后都没有抛出异常。在任何情况下,在构造过程中,我们都会调用代码/usr/lib/gcc/i686-pc-linux-gnu/4.7.3/include/g++-v4/bits/stream_iterator.h:121
,其中包含
*_M_stream >> _M_value;
基本上,类istream_iterator
在构造上创建一个istream
,它会立即尝试读取一个抛出异常的字符。
在noIterators()
上,getline
只设置badbit并且不会抛出异常。
最重要的是,尝试从目录中读取是不好的。是否有一个好的stl(不是提升)方式来检测这个?还有其他情况会抛出需要捕获的特定于实现的异常吗?
答案 0 :(得分:1)
istreambuf_iterator
本身永远不会抛出。
它迭代的basic_filebuf
打开文件就好像通过调用std::fopen
一样(无论它实际上是否这样做都无关紧要)。
正如您在this question中看到的那样,标准C没有关于目录是什么的概念,因此尝试打开一个是实现定义或未定义的行为。在gcc的情况下(或者更确切地说是libc),可以打开目录,但是你可以用它做很多事情。
根据标准,basic_filebuf
也永远不会抛出,但由于你已经超越了标准强制要求,试图打开不是文件的东西,libstdc ++是免费的当您尝试从目录中读取时抛出异常。
检查给定名称是否是文件通常取决于平台,但boost::filesystem
/ std::experimental::filesystem
提供便携式支票。
要回答您的编辑,您在尝试从流中读取时不会看到异常,因为Sentry将失败并且流将永远不会从缓冲区读取(最终调用underflow
)。 istreambuf_iterator
直接在缓冲区上运行,因此没有哨兵。
事实证明,libstdc ++不会对overflow
进行类似的调用,并且由于设置了underflow
的方式,如果例外,则不会抛出异常流是开放的写作,overflow
返回eof。