在 Windows 中使用 fmt 获取额外的换行符

发布于 2025-01-17 21:50:29 字数 1777 浏览 0 评论 0原文

我最近开始使用FMT进行打印。我真的很喜欢LIB,快速,易于使用。但是,当我完成转换时,我的程序可以通过多种其他新线进行渲染。并非每种情况,所以这会变得有些深。

我拥有的是编译器和构建经理。 Build Manager(图片Ninja,尽管这是一种自定义工具)启动编译过程,缓冲输出并立即打印出来。这两个程序已转换为使用FMT。所谓的关键函数是FMT :: vprint(流,格式,args)。当构建经理直接打印时,情况就很好。但是,当我读取子过程输出时,数据中的任何\ n都以\ r为前缀。 Windows Terminal会渲染这一良好,但是某些外壳(例如Visual Studio输出窗口)却没有,并且会显示一堆额外的新线。

FMT是开源的,因此我能够在它上砍掉它,看看它的所作所为和我的程序最初在做什么之间有什么不同。关键是这样:

namespace detail {
FMT_FUNC void print(std::FILE* f, string_view text) {
#ifdef _WIN32
  auto fd = _fileno(f);
  if (_isatty(fd)) {
    detail::utf8_to_utf16 u16(string_view(text.data(), text.size()));
    auto written = detail::dword();
    if (detail::WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)),
                              u16.c_str(), static_cast<uint32_t>(u16.size()),
                              &written, nullptr)) {
      return;
    }
    // Fallback to fwrite on failure. It can happen if the output has been
    // redirected to NUL.
  }
#endif
  detail::fwrite_fully(text.data(), 1, text.size(), f);
}
}  // namespace detail

作为一个子过程,_isatty()函数将带有false,因此我们返回到fwrite()函数,并且触发了\ r逃脱。在我的原始程序中,我也有一个fwrite()后备,但是只有当getStdhandle(std_output_handle)返回nullptr时,它才能拾取。在儿童过程案例中,仍然有一个控制台,我们可以writefile() to。

我看到的另一个副作用是,如果我使用FMT注入颜色的方式,例如:

fmt::print(fmt::emphasis::bold | fg(fmt::color::red), "Elapsed time: {0:.2f} seconds", 1.23);

Windows Terminal将其正确呈现,但是在Visual Studio的输出窗口中,这将变成垃圾汤。这样做的本机方法 - setConsoletextatTattribute(控制台,foreground_red | foreground_green | foreground_intsente); - 不会触发该问题。

我尝试将FMT源黑客攻击更像是我的原始控制台打印代码。关键区别是_isatty()函数。我怀疑对于主机打印可能失败的情况而言,这太广泛了。

I started using fmt for printing recently. I really like the lib, fast, easy to use. But when I completed my conversion, there are ways that my program can run that will render with a bunch of additional newlines. It's not every case, so this will get a bit deep.

What I have is a compiler and a build manager. The build manager (picture Ninja, although this is a custom tool) launches compile processes, buffers the output, and prints it all at once. Both programs have been converted to use fmt. The key function being called is fmt::vprint(stream, format, args). When the build manager prints directly, things are fine. But when I'm reading the child process output, any \n in the data has been prefixed with \r. Windows Terminal will render that fine, but some shells (such as the Visual Studio output window) do not, and will show a bunch of extra newlines.

fmt is open source so I was able to hack on it a bunch and see what is different between what it did and what my program was doing originally. The crux is this:

namespace detail {
FMT_FUNC void print(std::FILE* f, string_view text) {
#ifdef _WIN32
  auto fd = _fileno(f);
  if (_isatty(fd)) {
    detail::utf8_to_utf16 u16(string_view(text.data(), text.size()));
    auto written = detail::dword();
    if (detail::WriteConsoleW(reinterpret_cast<void*>(_get_osfhandle(fd)),
                              u16.c_str(), static_cast<uint32_t>(u16.size()),
                              &written, nullptr)) {
      return;
    }
    // Fallback to fwrite on failure. It can happen if the output has been
    // redirected to NUL.
  }
#endif
  detail::fwrite_fully(text.data(), 1, text.size(), f);
}
}  // namespace detail

As a child process, the _isatty() function will come back with false, so we fall back to the fwrite() function, and that triggers the \r escaping. In my original program, I have an fwrite() fallback as well, but it only picks up if GetStdHandle(STD_OUTPUT_HANDLE) returns nullptr. In the child process case, there is still a console we can WriteFile() to.

The other side-effect I see happening is if I use the fmt way of injecting color, eg:

fmt::print(fmt::emphasis::bold | fg(fmt::color::red), "Elapsed time: {0:.2f} seconds", 1.23);

Again Windows Terminal renders it correctly, but in Visual Studio's output window this turns into a soup of garbage. The native way of doing it -- SetConsoleTextAttribute(console, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY);-- does not trigger that problem.

I tried hacking up the fmt source to be more like my original console printing code. The key difference was the _isatty() function. I suspect that's too broad of a question for the cases where console printing might fail.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

情仇皆在手 2025-01-24 21:50:29

\ r是因为文件以文本模式打开。您可以在二进制模式下尝试(重新)打开(重新)或忽略读取侧的\ r

\r is added because the file is opened in text mode. You could try (re)opening in binary mode or ignore \r on the read side.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文