gpt4 book ai didi

c++ - istreambuf_iterator 什么时候抛出异常?

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:21:06 26 4
gpt4 key购买 nike

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 4.7.3 版

编辑1

是的,打开一个目录进行阅读是不标准的,也是不好的。同时,我需要正确地设下这个案子。根据下面的评论,这是一段尝试从目录中读取的代码:

#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?

编辑2

我从上面扩展了目录打开代码,以便更好地跟踪发生了什么

#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(不是 boost)方法来检测这个?是否有其他情况会抛出需要捕获的特定于实现的异常?

最佳答案

istreambuf_iterator 本身永远不会抛出。

它迭代的 basic_filebuf 打开文件,就像调用 std::fopen 一样(无论它是否真的这样做都无关紧要)。

正如您在 this question 中看到的那样,标准 C 没有目录的概念,因此尝试打开一个目录是实现定义的或未定义的行为。在 gcc 的情况下(或者更确切地说是 libc),目录可以打开,但你不能用它做太多事情。

根据标准,basic_filebuf 也永远不会抛出异常,但是由于您已经通过尝试打开非文件的内容而超出了标准强制执行的范围,因此 libstdc++ 可以自由地当您尝试从目录中读取时抛出异常。

检查给定名称是否为文件通常取决于平台,但 boost::filesystem/std::experimental::filesystem 提供可移植检查。


要回答您的编辑,您在尝试从流中读取时不会看到异常,因为哨兵将失败并且流永远不会从缓冲区中读取(最终调用 underflow)。 istreambuf_iterator 直接在缓冲区上操作,因此没有哨兵。

事实证明,libstdc++ 不会抛出对 overflow 的类似调用,并且由于 underflow 的设置方式,不会抛出异常如果流已打开以进行写入并且 overflow 返回 eof。

关于c++ - istreambuf_iterator 什么时候抛出异常?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26231837/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com