首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >为什么std::codecvt只被文件I/O流使用?

为什么std::codecvt只被文件I/O流使用?
EN

Stack Overflow用户
提问于 2020-11-23 23:03:14
回答 1查看 353关注 0票数 9

我一直在实现一个codecvt,用于处理输出流的标识。它可以像这样使用,工作良好:

代码语言:javascript
复制
std::cout << indenter::push << "im indentet" << indenter::pop << "\n im not..."

然而,虽然我可以将std::codecvt注入到任何std::ostream中,但当我发现我的代码与std::coutstd::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:

我做了一个很小的例子来说明我的问题:

代码语言:javascript
复制
#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_convertstd::basic_streambuf<char>类型的流缓冲区上的一个包装器,它具有std::basic_streambuf<Elem>的外观。通过std::wbuffer_convert执行的所有I/O都经历了由facet定义的字符转换。..。 此类模板使std::basic_filebuf std::basic_streambuf**.** 的隐式字符转换功能可用于任何

这样,我就可以将facet应用于std::ostringstream

代码语言:javascript
复制
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在标准中集成得很糟糕。也许这就是为什么它被标记为不推荐?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 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接口,您需要提供一个底层streambufstd::ofstream提供了一个filebufstd::ostringstream提供了一个stringbuf (它没有链接到std::codecvt的使用)。请参阅上的这篇文章,其中还突出了以下内容:

在...in的情况下,还有一些额外的函数转发到filebuf接口中的附加函数。

但是,要调用std::codecvt的字符转换功能,当您有一个std::ostringstream (它是一个具有底层std::basic_streambufstd::ostream )时,您可以使用std::wbuffer_convert,如您在文章中所指出的那样。

您只在第二次更新中使用了std::wstring_convert,而没有使用std::wbuffer_convert

在使用std::wbuffer_convert时,可以将原始std::ostringstream包装为std::ostream,如下所示:

代码语言:javascript
复制
// 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";

与完整的示例这里一起,输出如下:

代码语言:javascript
复制
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::ofstreamstd::cout )的流来调用的原因。但是,使用底层stringbuf的流可以使用std::wbuffer_convert包装到一个std::ostream实例中,然后该实例将调用底层std::codecvt

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64977885

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档