繁体   English   中英

c++ 混合 printf 与 wprintf(或 cout 与 wcout)

[英]c++ Mixing printf with wprintf (or cout with wcout)

我知道您不应该将打印与 printf、cout 和 wprintf、wcout 混合使用,但是很难找到一个好的答案,为什么以及是否可以绕过它。 问题是我使用外部库打印 printf 而我自己使用 wcout。 如果我做一个简单的例子,它工作正常,但从我的完整应用程序来看,它根本不打印 printf 语句。 如果这真的是一个限制,那么就会有许多库无法与广泛的打印应用程序一起工作。 对此的任何见解都非常受欢迎。

更新:

我把它归结为:

#include <stdio.h>
#include <stdlib.h>
#include <iostream>

#include <readline/readline.h>
#include <readline/history.h>

int main()
{
    char *buf;

    std::wcout << std::endl; /* ADDING THIS LINE MAKES PRINTF VANISH!!! */

    rl_bind_key('\t',rl_abort);//disable auto-complete

    while((buf = readline("my-command : "))!=NULL)
    {
        if (strcmp(buf,"quit")==0)
            break;

        std::wcout<<buf<< std::endl;

        if (buf[0]!=0)
            add_history(buf);
    }

    free(buf);

    return 0;
}

所以我想这可能是冲洗问题,但我觉得它仍然很奇怪,我必须检查一下。

更新 -> 解决方法:

首先,wprintf 会出现同样的问题。 但我发现添加:

std::ios::sync_with_stdio(false);

实际上做了这个把戏......(注意是假的,而不是我期望的那样......),唯一困扰我的是,我不明白为什么以及如何弄清楚:-(

您应该能够混合它们,但它们通常使用单独的缓冲机制,因此它们相互重叠:

printf("hello world");
cout << "this is a suprise";

可能导致:

hellothis是一个令人惊讶的世界

您没有提供足够的信息来诊断应用程序中printf()的问题,但我怀疑您有多个c运行时(代码中有一个,printf()代码中有一个)并且存在冲突。

我想你在谈论std::ios_base::sync_with_stdio ,但是IIRC默认是打开的。

printf()和cout缓冲区默认是同步的,或者实际上是相同的缓冲区。 如果您遇到缓冲问题,显而易见的解决方案是在每次输出后刷新缓冲区:

fflush( stdout );
cout.flush();

这会将缓冲区刷新到操作系统,一旦完成,就不会有交错或输出丢失的可能性。

缓解头痛。 通常,你可以 ,因为它们是同步的。 告诉你不要的人可能是那些记得使用多种io方法并希望从中拯救你的痛苦的人。 (只是不要与系统调用混合。这将是痛苦的。)

库不应将printf,cout或任何其他I / O用于标准句柄。 他们应该使用回调例程将输出委托给主程序选择的方法。

一个明显的例外是一个库,其唯一目的是输出,但它是主程序使用它的选择。 此规则不禁止I / O到库打开的文件描述符。

这不仅可以解决此处提出的问题,而且还可以解决断开连接的操作(没有tty的linux程序,例如通过nohup或Win32服务运行)。

我的看法,不一定对,仅供大家参考。


概述

  • cout/wcout是C++世界的概念,C世界的等价项是printf/wrpintf。 这些机制用于将我们在应用程序中提供的内容传输到标准 output stream。
  • 兼容C语言是C++的设计目标之一。 也就是说,C++的实现必须保证C++和C混合代码正确执行。
  • 问题很明显,C和C++毕竟是两种不同的语言。 C++的cout/wcout和C的printf/wprintf有自己的实现,比如有自己的应用层缓冲区,有自己的缓冲区flush策略,有自己的locale依赖……总之,cout/wcout和printf/wprintf是两种独立的运行机制,简单的混合会导致意想不到的结果,因此C++标准的设计者必须制定一个可行的解决方案。
  • 从编码者的角度来看,混合cout和printf的正确行为应该保证执行结果就像只使用cout或printf一样。为了达到这个效果,提出了与stdio同步的概念。 在我看来,'stdio'特指C语言的I/O机制,printf/wprintf属于它。 所以“sync to stdio”意味着将 C++ 的 I/O 机制同步到 C 的机制。 我们可以使用方法std::ios::sync_with_stdio来启用/禁用同步,默认情况下启用。
  • 那么,什么是“同步”呢? 如何同步到stdio? 这取决于实施,我不知道细节。 似乎'sync'概念更像是'share',printf/wprintf的缓冲区与cout/wcout共享。 或者 cout/wcout 直接使用 print/wprintf 的缓冲区。 简而言之,如果我们启用'sync',cout/wcout 不再独立,通常是缓冲区,它依赖于 C stdio。 'sync' 使 C++ 和 C 的 I/O 过程就像一种语言。
  • 因此,我们可以在C++代码中通过启用与stdio的同步来放心使用cout+printf或wcout+wprintf(默认启用,我们不需要操作)。 此问题已通过“同步”解决。 另一个难题是 mix cout+wcout 或 mix printf+wprintf,可以吗? ——这个话题在下面讨论。
  • cout printfchar存储单元上工作,而wcout wprintfwchar_t上工作。 混合使用 cout+wcout 或 printf+wprintf 不涉及语言层面的冲突。 因为cout wcout属于同一种语言,所以printfwprintf也是如此。 这里问题的关键是'the orientation of the standard output stream'
  • 定向是什么意思? 我认为可以将其解释为粒度。 我们知道,OOD是面向object的设计,也就是说设计的粒度是object。再比如面向字节的stream协议——TCP,也就是说如果我们基于TCP构建应用程序,就必须关注字节。点,所以...标准output stream的方向是什么? 答案是bytewide byte或其他我不确定的东西......
  • 标准的 output stream 要么是面向字节的,要么是面向宽字节的,C++/C语言是如何判断是哪一个的? 策略是看第一个执行的I/O函数/方法的版本,如果是byte版本,比如cout或者printf,那么标准的output stream设置为byte-oriented,否则如果是wide byte版本,像 wcout 或 wprintf,那么标准 output stream 是面向宽字节的。
  • 为什么要关心标准 output stream 导向? 那是因为标准 output stream 的不同朝向会影响内容的呈现。 这样想起来很容易,方向决定了标准的粒度 output stream 来处理/提取/翻译来自cout/wcout/printf/wprintf的内容。 试想,我们先用wcout触发标准output stream被设置为面向宽字节,然后我们通过cout提供一些内容,结果是面向宽字节的标准output stream收到了一组经过字节处理的内容,最后,打印到连接到标准 output stream 的设备的内容未定义。
  • 另外,我在实际开发中,发现还有一种情况是标准output stream方向。 禁用sync witch stdio - std::ios::sync_with_stdio(false)后,无论我先调用wcout再调用cout还是调用cout再调用wcout,打印内容都可以,(当然,我们必须设置locale。但那是另一个主题),在这种情况下,我调用fwide(stdout, 0)总是返回 0,这意味着当前标准 output stream 方向未定,是否由于标准 output stream 而未定 state 可以自动切换到合适的方向? 还是未定意味着万能的state中的标准output stream? 我不知道...
  • 特例。 在 Windows 世界中提供了一个名为_setmode的 function,这个 function 用于设置特定流的转换模式。 经过我的尝试,我猜所谓的“翻译模式”相当于 stream 方向。 但是,使用_setmode设置 stream 方向似乎在底层设置了一些非标准标志值! 因为在我调用_setmode设置某些模式(例如 _O_WTEXT、_O_U8TEXT、_O_U16TEXT...)之后,如果我调用fwide尝试检查当前的 stream 方向,就会发生崩溃。 可能是fwide查询了一个看不懂的数据,所以崩溃了!

一些演示

我写了一个 function checkStdoutOrientation来获得标准的 output stream 方向

void checkStdoutOrientation()                                                   
{                                                                               
    std::fstream fs("result.txt", std::ios::app);                               
    int ret = fwide(stdout, 0);                                                 
    if (ret > 0) {                                                              
        fs << "wide byte oriented\n";                                           
    } else if (ret < 0) {                                                       
        fs << "byte oriented\n";                                                
    } else {                                                                    
        fs << "undecided oriented\n";                                           
    }                                                                           
    fs.close();                                                                 
}    

Demo01:先调用wcout,再调用cout

#include <cstdio>                                                               
#include <iostream>                                                             
#include <fstream>  

int main()                                                                      
{                                                                               
    checkStdoutOrientation();                                                   
                                                                                
    std::wcout << "456" << std::endl;                                           
                                                                                
    checkStdoutOrientation();                                                   
                                                                                
    std::cout << "123" << std::endl;                                            
                                                                                
    checkStdoutOrientation();                                                   
                                                                                
    return 0;                                                                   
}          

Output:
在此处输入图像描述

结果.txt:
在此处输入图像描述

我的理解:

  • 首先调用 wcout,因此标准 output stream 是面向宽字节的;
  • 正因为上述情况,cout处理的内容不能用标准output stream处理,所以“123”无法打印;

Output:
在此处输入图像描述

结果.txt:
在此处输入图像描述

我的理解:

  • 看来,windows不符合标准,标准output stream始终未定方向;
  • 只是因为标准output stream的未定方向,所有内容都是打印出来的。

Demo02:先调用cout,再调用wcout

#include <cstdio>                                                               
#include <iostream>                                                             
#include <fstream>  

int main()                                                                      
{                                                                               
    checkStdoutOrientation();                                                   
                                                                                
    std::cout << "123" << std::endl;                                            
                                                                                
    checkStdoutOrientation();                                                   
                                                                                
    std::wcout << "456" << std::endl;                                           
                                                                                
    checkStdoutOrientation();

    return 0;
} 

Output:
在此处输入图像描述

结果.txt:
在此处输入图像描述

我的理解:

  • cout 首先被调用,所以标准 output stream 是面向字节的;
  • 因为字节是比宽字节更小的粒度,所以 wcout 内容到字节导向标准 output stream 最终可以打印到控制台。

Output:
在此处输入图像描述

结果.txt:
在此处输入图像描述

我的理解:

  • 标准 output stream 始终未定方向;
  • 所以所有内容都是打印的。

Demo03:先调用wprintf,再调用printf

#include <cstdio>                                                               
#include <iostream>                                                             
#include <fstream>  

int main()                                                                                                                                                                                                       
{                                                                               
    checkStdoutOrientation();                                                   
                                                                                
    wprintf(L"456\n");                                                          
                                                                                
    checkStdoutOrientation();                                                   
                                                                                
    printf("123\n");                                                            
                                                                                
    checkStdoutOrientation();                                                 
                                                                                
    return 0;                                                                   
}          

和Demo01的结论一样

Demo04:先调用printf,再调用wprintf

#include <cstdio>                                                               
#include <iostream>                                                             
#include <fstream>  

int main()                                                                      
{                                                                               
    checkStdoutOrientation();                                                   
                                                                               
    printf("123\n");                                                                                                    
                                                                                
    checkStdoutOrientation();                                                   
                                                                                
    wprintf(L"456\n");                                                          
                                                                                
    checkStdoutOrientation();

    return 0;
} 

Output:
在此处输入图像描述

结果.txt:
在此处输入图像描述

我的理解:

  • cout 首先被调用,所以标准 output stream 是面向字节的;
  • 结果与Demo02不同,我不知道为什么“456”不显示。

Output:
在此处输入图像描述

结果.txt:
在此处输入图像描述

我的理解:

  • 与 Demo02 相同的结果。

Demo05:禁用与 stdio 的同步,然后是 wcout、cout

#include <cstdio>                                                               
#include <iostream>                                                             
#include <fstream>  

int main()                                                                      
{                                                                               
    std::ios::sync_with_stdio(false);                                           
                                                                                
    checkStdoutOrientation();                                                   
                                                                                
    std::wcout << "456" << std::endl;                                           
                                                                                
    checkStdoutOrientation();                                                   
                                                                                
    std::cout << "123" << std::endl;                                            
                                                                                
    checkStdoutOrientation(); 

    return 0;
} 

Output:
在此处输入图像描述

结果.txt:
在此处输入图像描述

我的理解:

  • 禁用与 stdio 同步后,标准 output stream 始终未定方向;
  • 所以所有内容都可以打印。

Output:
在此处输入图像描述

结果.txt:
在此处输入图像描述

我的理解:

  • 结果与 Demo01 相同。

Demo06:混合cout,printf

#include <cstdio>                                                               
#include <iostream> 

int main()                                                                      
{                                                                               
    printf("1\n");                                                              
    std::cout << "2\n";                                                         
    printf("3\n");                                                              
    std::cout << "4\n";                                                         
    printf("5\n");                                                              
                                                                                
    printf("\n");                                                               
                                                                                
    std::ios::sync_with_stdio(false);                                           
                                                                                
    printf("1\n");                                                              
    std::cout << "2\n";                                                         
    printf("3\n");                                                              
    std::cout << "4\n";                                                         
    printf("5\n");                                                              
                                                                                                                                                        
    return 0;                                                                   
}   

Output:
在此处输入图像描述

我的理解:

  • 默认情况下启用与 stdio 的同步,因此混合 cout 和 printf 就像调用 cout 或 printf 一样工作。
  • 禁用与 stdio 同步后,cout 将独立工作,cout 和 printf 各司其职,因此打印内容乱序。

Output:
在此处输入图像描述

我的理解:

  • Windows 仍然很特别,无论是否禁用与 stdio 的同步,混合 cout 和 printf 就像调用 cout 或 printf 一样工作。

Demo07:打印非 ASCII 字符——方法 A

#include <cstdio>                                                               
#include <iostream>                                                             

int main()                                                                      
{                                                                               
    std::locale myloc("en_US.UTF-8");                                           
    std::locale::global(myloc); // this setting does not affect wcout                                         
                                                                                
    std::wcout << L"漢字\n";  
    wprintf(L"漢字\n");                                                         

    return 0;
} 

Output:
在此处输入图像描述

我的理解:

  • 设置全局语言环境不影响 wcout,这个 wcout 的语言环境仍然是 C 语言环境。 因为wcout是一个object,它的locale在object构建的时候就已经设置好了。
  • 那么既然如此,为什么wcout可以打印出内容呢? 不要忘记 C++ 的 iostream 默认与 stdio 同步,我们可以简单地认为,wcout 在 stdio 的缓冲区上工作,并且 stdio 已经由global(myloc)代码设置为 en_US.UTF-8。

Output:
在此处输入图像描述

我的理解:

  • 没什么特别的;

Demo08:打印非 ASCII 字符——方法 B

#include <cstdio>                                                               
#include <iostream>                                                             

int main()                                                                      
{                                                                               
    std::ios::sync_with_stdio(false);                                           
                                                                                
    std::locale myloc("en_US.UTF-8");                                           
    // std::locale::global(myloc);                                              
                                                                                
    std::wcout.imbue(myloc);                                                    
    std::wcout << "wcout> " << L"漢字\n";                                       
    wprintf(L"wprintf> 漢字\n");                                                         

    return 0;
} 

Output:
在此处输入图像描述

我的理解:

  • 由于禁用了与 stdio 的同步,wprintf 和 wcout 分开工作,所以打印顺序不对;
  • 仍然由于同步禁用,wcout 应该独立工作,所以我们必须通过imbue方法将 wout 的语言环境设置为 en_US.UTF-8,如果不这样做,wcout 将打印类似“??”的内容;
  • wprintf print "??", 那是因为我们注释掉了std::locale::global(myloc); ,因此 stdio 的语言环境仍然是 C 语言环境。

Output:
在此处输入图像描述

我的理解:

  • printf和cout print always in order,这是windows特别的地方,这个已经讲过好几次了;
  • wprintf print empty 相当于“??” 在 linux;
  • 所以,新的是乱码! 我尝试取消注释std::locale::global(myloc); 代码,打印内容没问题,所以,我认为 windows 实现没什么特别的。 wcout 可能取决于更多需要通过全局区域设置更改区域设置的东西。

Demo09:打印非ASCII字符——方法C

本demo依赖windows平台。

#include <cstdio>                                                               
#include <iostream>                                                             

int main()                                                                      
{                                                                               
    _setmode(_fileno(stdout), _O_WTEXT); // Unique function to windows 

    std::wcout << "wcout> " << L"漢字\n";
    wprintf(L"wprintf> 漢字\n");                                                  

    return 0;
} 

Output:
在此处输入图像描述

我的理解:

  • _setmode 之后,globale 和 wcout locale 仍然是 C locale;
  • 为什么wcout和wprintf都能正确打印内容? 我想也许 windows 在标准 output stream 之后实施了一种机制,以通过 _setmode 指定模式翻译内容。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM