我一直在实现一个codecvt,用于处理输出流的标识。它可以像这样使用,工作良好:
std::cout << indenter::push << "im indentet" << indenter::pop << "\n im not..."然而,虽然我可以将std::codecvt注入到任何std::ostream中,但当我发现我的代码与std::cout和std::ofstream一起工作时,我感到非常困惑,但对于std::ostringstream来说,即使所有这些代码都继承自基类std::ostream,也是如此。
面是正常构造的,代码编译,它不会抛出任何异常。只是没有调用std::codecvt的任何成员函数。
对我来说,这是非常令人困惑的,我不得不花很多时间来弄清楚std::codecvt不会在非文件I/O流上做任何事情。
std::codecvt没有被std::ostream继承的所有类使用的原因吗?
此外,有没有人知道我可以依靠哪种结构来实现缩进?
编辑:这是我指的语言的一部分:
通过std::basic_fstream执行的所有文件I/O操作都使用注入流中的区域设置的std::codecvt方面。
来源:https://en.cppreference.com/w/cpp/locale/codecvt
更新1:
我做了一个很小的例子来说明我的问题:
#include <iostream>
#include <locale>
#include <fstream>
#include <sstream>
static auto invocation_counter = 0u;
struct custom_facet : std::codecvt<char, char, std::mbstate_t>
{
using parent_t = std::codecvt<char, char, std::mbstate_t>;
custom_facet() : parent_t(std::size_t { 0u }) {}
using parent_t::intern_type;
using parent_t::extern_type;
using parent_t::state_type;
virtual std::codecvt_base::result do_out (state_type& state, const intern_type* from, const intern_type* from_end, const intern_type*& from_next,
extern_type* to, extern_type* to_end, extern_type*& to_next) const override
{
while (from < from_end && to < to_end)
{
*to = *from;
to++;
from++;
}
invocation_counter++;
from_next = from;
to_next = to;
return std::codecvt_base::noconv;
}
virtual bool do_always_noconv() const throw() override
{
return false;
}
};
std::ostream& imbueFacet (std::ostream& ostream)
{
ostream.imbue(std::locale { ostream.getloc(), new custom_facet{} });
return ostream;
}
int main()
{
std::ios::sync_with_stdio(false);
std::cout << "invocation_counter = " << invocation_counter << "\n";
{
auto ofstream = std::ofstream { "testFile.txt" };
ofstream << imbueFacet << "test\n";
}
std::cout << "invocation_counter = " << invocation_counter << "\n";
{
auto osstream = std::ostringstream {};
osstream << imbueFacet << "test\n";
}
std::cout << "invocation_counter = " << invocation_counter << "\n";
}除了invocation_counter之外,我会在std::ostringstream中增加流媒体,但事实并非如此。
更新2:
经过更多的研究,我发现我可以使用std::wbuffer_converter。引用转换
std::wbuffer_convert是std::basic_streambuf<char>类型的流缓冲区上的一个包装器,它具有std::basic_streambuf<Elem>的外观。通过std::wbuffer_convert执行的所有I/O都经历了由facet定义的字符转换。..。 此类模板使std::basic_filebufstd::basic_streambuf**.** 的隐式字符转换功能可用于任何。
这样,我就可以将facet应用于std::ostringstream。
auto osstream = std::ostringstream {};
osstream << "test\n";
auto facet = custom_facet{};
std::wstring_convert<custom_facet, char> conv;
auto str = conv.to_bytes(osstream.str());但是,我失去了使用流运算符<<连接方面的能力。
这使我更加困惑为什么std::codecvt并不是所有输出流都使用的隐式。所有输出流都从std::basic_streambuf继承而来,其接口适合使用std::codecvt,后者仅使用输入和输出字符序列,完全在std::basic_streambuf中实现。
那么为什么std::codecvt的解析是用std::basic_filebuf而不是std::basic_streambuf实现的呢?std::basic_filebuf毕竟继承了std::basic_streambuf .
要么我对流在C++中的工作方式有一些根本的误解,要么std::codecvt在标准中集成得很糟糕。也许这就是为什么它被标记为不推荐?
发布于 2021-10-13 12:03:36
std::codecvt方面最初用于处理磁盘和内存字符表示之间的I/O转换。引用Bjarne的39.4.6段的C++编程语言第四版:
有时,存储在文件中的字符的表示形式与主内存中这些相同字符的所需表示不同。..。codecvt facet提供了一种在读取或写入字符时将字符从一种表示形式转换为另一种表示形式的机制。
因此,目的是使用std::codecvt只用于调整文件(磁盘)和内存之间的字符,这部分回答了您的问题:
为什么std::codecvt只被文件I/O流使用?
从文档中我们可以看到:
通过
std::basic_fstream<CharT>执行的所有文件I/O操作都使用注入到流中的区域设置的std::codecvt<CharT, char, std::mbstate_t>方面。
这就回答了为什么std::ofstream (使用基于文件的流缓冲区)和std::cout (链接到标准输出文件流)调用std::codecvt的原因。
现在,要使用高级std::ostream接口,您需要提供一个底层streambuf。std::ofstream提供了一个filebuf,std::ostringstream提供了一个stringbuf (它没有链接到std::codecvt的使用)。请参阅流上的这篇文章,其中还突出了以下内容:
在...in的情况下,还有一些额外的函数转发到filebuf接口中的附加函数。
但是,要调用std::codecvt的字符转换功能,当您有一个std::ostringstream (它是一个具有底层std::basic_streambuf的std::ostream )时,您可以使用std::wbuffer_convert,如您在文章中所指出的那样。
您只在第二次更新中使用了std::wstring_convert,而没有使用std::wbuffer_convert。
在使用std::wbuffer_convert时,可以将原始std::ostringstream包装为std::ostream,如下所示:
// Create a std::ostringstream
auto osstream = std::ostringstream{};
// Create the wrapper for the ostringstream
std::wbuffer_convert<custom_facet, char> wrapper(osstream.rdbuf());
// Now create a std::ostream which uses the wrapper to send data to
// the original std::ostringstream
std::ostream normal_ostream(&wrapper);
normal_ostream << "test\n";
// Flush the stream to invoke the conversion
normal_ostream << std::flush;
// Check the invocation_counter
std::cout << "invocation_counter after wrapping std::ostringstream with "
"std::wbuffer_convert = "
<< invocation_counter << "\n";与完整的示例这里一起,输出如下:
invocation_counter start of test1 = 0
invocation_counter after std::ofstream = 1
> test printed to std::cout
invocation_counter after std::cout = 2
invocation_counter after std::ostringstream (should not have changed)= 2
ic after test1 = 2
invocation_counter after std::ostringstream with std::wstring_convert = 3
ic after test2 = 3
invocation_counter after wrapping std::ostringstream with std::wbuffer_convert = 4
ic after test3 = 4结论
std::codecvt的目的是在磁盘和内存表示之间进行转换。这就是为什么std::codecvt实现只通过使用底层filebuf (如std::ofstream和std::cout )的流来调用的原因。但是,使用底层stringbuf的流可以使用std::wbuffer_convert包装到一个std::ostream实例中,然后该实例将调用底层std::codecvt。
https://stackoverflow.com/questions/64977885
复制相似问题