Ole*_*nov 15 c++ file-io boost
我想做一件简单的事情:从文件中读取第一行,并在没有此类文件,没有读取文件的权限等情况下进行正确的错误报告.
我考虑了以下选项:
std::ifstream
.不幸的是,没有可移植的方式来报告系统错误.其他一些答案建议errno
在读取失败后进行检查,但标准并不保证errno
由iostreams库中的任何函数设置.fopen
/ fread
/ fclose
.这有效,但不像iostreams那样方便std::getline
.我正在寻找C++解决方案.有没有办法用C++ 14和boost来实现这个目的?
Nia*_*las 18
免责声明:我是AFIO的作者.但正是您正在寻找的是https://ned14.github.io/afio/这是v2库,其中包含了2015年8月Boost同行评审的反馈.请参阅此处的功能列表.
我当然会告诫这是一个alpha质量库,你不应该在生产代码中使用它.但是,不少人已经这样做了.
如何使用AFIO解决OP的问题:
请注意,AFIO是一个非常低级别的库,因此您必须键入更多代码才能实现与iostream相同的功能,另一方面,您不会获得内存分配,没有异常抛出,没有不可预测的延迟峰值:
// Try to read first line from file at path, returning no string if file does not exist,
// throwing exception for any other error
optional<std::string> read_first_line(filesystem::path path)
{
using namespace AFIO_V2_NAMESPACE;
// The result<T> is from WG21 P0762, it looks quite like an `expected<T, std::error_code>` object
// See Outcome v2 at https://ned14.github.io/outcome/ and https://lists.boost.org/boost-announce/2017/06/0510.php
// Open for reading the file at path using a null handle as the base
result<file_handle> _fh = file({}, path);
// If fh represents failure ...
if(!_fh)
{
// Fetch the error code
std::error_code ec = _fh.error();
// Did we fail due to file not found?
// It is *very* important to note that ec contains the *original* error code which could
// be POSIX, or Win32 or NT kernel error code domains. However we can always compare,
// via 100% C++ 11 STL, any error code to a generic error *condition* for equivalence
// So this comparison will work as expected irrespective of original error code.
if(ec == std::errc::no_such_file_or_directory)
{
// Return empty optional
return {};
}
std::cerr << "Opening file " << path << " failed with " << ec.message() << std::endl;
}
// If errored, result<T>.value() throws an error code failure as if `throw std::system_error(fh.error());`
// Otherwise unpack the value containing the valid file_handle
file_handle fh(std::move(_fh.value()));
// Configure the scatter buffers for the read, ideally aligned to a page boundary for DMA
alignas(4096) char buffer[4096];
// There is actually a faster to type shortcut for this, but I thought best to spell it out
file_handle::buffer_type reqs[] = {{buffer, sizeof(buffer)}};
// Do a blocking read from offset 0 possibly filling the scatter buffers passed in
file_handle::io_result<file_handle::buffers_type> _buffers_read = read(fh, {reqs, 0});
if(!_buffers_read)
{
std::error_code ec = _fh.error();
std::cerr << "Reading the file " << path << " failed with " << ec.message() << std::endl;
}
// Same as before, either throw any error or unpack the value returned
file_handle::buffers_type buffers_read(_buffers_read.value());
// Note that buffers returned by AFIO read() may be completely different to buffers submitted
// This lets us skip unnecessary memory copying
// Make a string view of the first buffer returned
string_view v(buffers_read[0].data, buffers_read[0].len);
// Sub view that view with the first line
string_view line(v.substr(0, v.find_first_of('\n')));
// Return a string copying the first line from the file, or all 4096 bytes read if no newline found.
return std::string(line);
}
Run Code Online (Sandbox Code Playgroud)
最好的办法可能是包装Boost WinAPI和/或 POSIX API。
“天真的”C++ 标准库(带有花哨的功能)不会让你走得太远:
#include <iostream>
#include <fstream>
#include <vector>
template <typename Out>
Out read_file(std::string const& path, Out out) {
std::ifstream s;
s.exceptions(std::ios::badbit | std::ios::eofbit | std::ios::failbit);
s.open(path, std::ios::binary);
return out = std::copy(std::istreambuf_iterator<char>{s}, {}, out);
}
void test(std::string const& spec) try {
std::vector<char> data;
read_file(spec, back_inserter(data));
std::cout << spec << ": " << data.size() << " bytes read\n";
} catch(std::ios_base::failure const& f) {
std::cout << spec << ": " << f.what() << " code " << f.code() << " (" << f.code().message() << ")\n";
} catch(std::exception const& e) {
std::cout << spec << ": " << e.what() << "\n";
};
int main() {
test("main.cpp");
test("nonexistent.cpp");
}
Run Code Online (Sandbox Code Playgroud)
印刷...:
main.cpp: 823 bytes read
nonexistent.cpp: basic_ios::clear: iostream error code iostream:1 (iostream error)
Run Code Online (Sandbox Code Playgroud)
当然,您可以添加更多的诊断细读
<filesystem>
,但这很容易受到竞争的影响,如上所述(根据您的应用程序,这些甚至可能会打开安全漏洞,所以只需说“不”)。使用
boost::filesystem::ifstream
不会改变引发的异常更糟糕的是,使用 Boost IOstream 无法引发任何错误:
Run Code Online (Sandbox Code Playgroud)template <typename Out> Out read_file(std::string const& path, Out out) { namespace io = boost::iostreams; io::stream<io::file_source> s; s.exceptions(std::ios::badbit | std::ios::eofbit | std::ios::failbit); s.open(path, std::ios::binary); return out = std::copy(std::istreambuf_iterator<char>{s}, {}, out); }
快乐打印:
Run Code Online (Sandbox Code Playgroud)main.cpp: 956 bytes read nonexistent.cpp: 0 bytes read
归档时间: |
|
查看次数: |
2777 次 |
最近记录: |