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

uklbhaso  于 2022-12-30  发布在  其他
关注(0)|答案(6)|浏览(158)

我知道你不应该把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);

我真的做到了......(注意错误,不是我所期望的那样正确......),唯一困扰我的是,我不明白为什么和如何弄清楚:-(

jckbn6z7

jckbn6z71#

我想你说的是std::ios_base::sync_with_stdio,但IIRC默认情况下是打开的。

bq9c1y66

bq9c1y662#

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

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

可能导致:
你好,这是一个令人惊奇世界
你没有提供足够的信息来诊断你的应用程序中printf()的问题,但是我怀疑你有不止一个c运行时(一个在你的代码中,一个在printf()代码中),并且存在冲突。

camsedfj

camsedfj3#

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

fflush( stdout );
cout.flush();

这将缓冲器转储清除到操作系统,并且一旦这样做,就不存在交织或丢失输出的可能性。

tv6aics1

tv6aics14#

缓冲头痛.通常,你 * 可以 *,虽然,因为他们是同步的.那些告诉你不要这样做的人可能是那些记得使用多个io方法的痛苦,并希望从它中保存你的人.(只是不要混合任何一个与系统调用.那将是痛苦的.)

rhfm7lfc

rhfm7lfc5#

库不应该使用printf、cout或任何其他I/O来处理标准句柄。它们应该使用回调例程来将输出委托给主程序选择的方法。
一个明显的例外是库的唯一目的是输出,但是主程序可以选择使用它,并且这个规则不禁止对库打开的文件描述符进行I/O。
这不仅可以解决这里提出的问题,而且还可以处理断开连接的操作(没有tty的linux程序,例如通过nohup或Win32服务运行)。

yv5phkfx

yv5phkfx6#

我的看法,不一定正确,仅供参考。😄

概述

  • cout/wcout是C++世界的概念,C世界的等价术语是printf/wrpintf,这些机制用于将我们在应用程序中提供的内容转换为标准的输出流。

  • 与C语言兼容是C的设计目标之一,也就是说,C的实现必须保证C++和C代码的混合正确执行。

  • 问题很明显,C和C毕竟是两种不同的语言,C的cout/wcout和C的printf/wprintf都有自己的实现,比如自己的应用层缓冲区、自己的缓冲区刷新策略、自己的locale依赖......总之,cout/wcout和printf/wprintf是两种独立的操作机制,简单的混合会导致意想不到的结果,因此C++标准设计者必须开发一个可行的解决方案。

  • 从编码者的Angular 来看,混合cout和printf的正确行为应该保证执行结果就像只使用cout或printf一样。为了达到这个效果,提出了与stdio同步的概念。在我看来,"stdio"特指C语言的I/O机制。printf/wprintf属于它。所以'sync to stdio'意味着同步C++的I/O机制到C的机制。我们可以使用方法std::ios::sync_with_stdio来启用/禁用同步,默认情况下是启用的。

  • 那么,"sync"到底是什么?如何同步到stdio?这取决于实现,我不知道细节。"sync"的概念似乎更像是"share",printf/wprintf的缓冲区与cout/wcout共享。或者cout/wcout直接使用print/wprintf的缓冲区。简而言之,如果我们启用"sync",cout/wcout就不再是独立的,通常是缓冲区,它依赖于C stdio,"同步"使C++和C的I/O过程就像一种语言一样。

  • 因此,我们可以在C++代码中轻松地使用cout + printf或wcout + wprintf,方法是使用stdio来启用sync(默认情况下是启用的,我们不需要操作)。这个问题通过"sync"来解决。另一个难题是混合cout + wcout或混合printf + wprintf,可以吗?--这个主题在下面讨论。

  • coutprintf工作在char存储单元上,wcoutwprintf工作在wchar_t上,混用cout + wcout或printf + wprintf不涉及语言级别冲突,由于coutwcout属于同一种语言,因此printfwprintf也属于同一种语言,这里问题的关键是'the orientation of the standard output stream'

  • 定向是什么意思?我认为它可以解释为粒度。正如我们所知,OOD是面向对象的设计,这意味着设计的粒度是对象。另一个例子是面向字节流的协议-TCP,这意味着如果我们构建基于TCP的应用程序,我们必须关注字节。回到正题,那么...标准输出流的方向是什么?答案是bytewide byte或其他我不确定的东西...

  • 标准输出流是面向字节的还是面向宽字节的,C ++/C语言如何确定哪一个?策略是查看第一个执行的I/O函数/方法的版本,如果它是字节版本,就像cout或printf,那么标准输出流被设置为面向字节的,否则如果它是宽字节版本,就像wcout或wprintf,则标准输出流是面向宽字节的。

  • 我们为什么要关心标准输出流的方向呢?那是因为标准输出流的不同方向会影响内容的呈现。很容易这样想,方向决定了标准输出流处理/提取/翻译cout/wcout/printf/wprintf内容的粒度。试想一下,我们首先使用WCOUT触发标准输出流被设置为面向宽字节,然后我们通过COUT提供一些内容,结果是面向宽字节的标准输出流接收到一组经过字节处理的内容,最后,打印到连接到标准输出流的设备的内容未定义。

  • 另外,在我的实际开发中,我发现还有另外一种标准输出流定向的情况,在禁用同步开关stdio-std::ios::sync_with_stdio(false)后,无论是先调用wcout再调用cout还是先调用cout再调用wcout,打印内容都是可以的!(当然,我们必须设置locale,但那是另外一个主题),在这种情况下,我调用fwide(stdout, 0)总是返回0,这意味着当前的标准输出流方向未定。未决定的状态是因为标准输出流可以自动切换到合适的方向?还是未决定的状态意味着标准输出流处于万能状态?我不知道...

  • 一个特例。Windows世界中提供了一个名为_setmode的函数,该函数用于设置特定流的翻译模式。经过我的尝试,我猜所谓的"翻译模式"相当于流的方向。但是,使用_setmode设置流方向似乎在底层设置了一些非标准的标志值!因为在我调用_setmode设置某些模式之后(例如:_O_WTEXT,_O_U8TEXT,_O_U16TEXT ...),如果我调用fwide来检查当前的流方向,就会崩溃。可能是fwide查询了一个无法理解的数据,所以崩溃了!

一些演示

我编写了一个函数checkStdoutOrientation来获取标准输出流方向

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();                                                                 
}

演示01:首先调用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;                                                                   
}
debian10 buster + gcc 8.3.0
    • 输出:**

    • 结果文本:**

    • 我的理解是**
  • wcout首先被调用,因此标准输出流是面向宽字节的;
  • 正是由于上述情况,cout处理的内容不能被标准输出流处理,所以"123"不能打印;
Win10 + vs2022
    • 输出:**

    • 结果文本:**

    • 我的理解是**
  • 似乎,窗口不符合标准,标准输出流总是不确定的方向;
  • 正是由于标准输出流的方向未定,所有内容都被打印。

演示02:首先调用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;
}
debian10 buster + gcc 8.3.0
    • 输出:**

    • 结果文本:**

    • 我的理解是**
  • 首先调用cout,因此标准输出流是面向字节的;
  • 由于字节的粒度比宽字节小,所以wcout内容到面向字节的标准输出流最终可以打印到控制台。
Win10 + vs2022
    • 输出:**

    • 结果文本:**

    • 我的理解是**
  • 标准输出流总是不定取向的;
  • 因此所有内容都是打印。

演示03:首先调用wprintf,然后调用printf

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

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

结论与Demo01相同

演示04:首先调用printf,然后调用wprintf

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

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

    return 0;
}
debian10 buster + gcc 8.3.0
    • 输出:**

    • 结果文本:**

    • 我的理解是**
  • 首先调用cout,因此标准输出流是面向字节的;
  • 结果与Demo02不同,我不知道为什么"456"没有显示。
Win10 + vs2022
    • 输出:**

    • 结果文本:**

    • 我的理解是**
  • 结果与Demo02相同。

演示05:禁用同步与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;
}
debian10 buster + gcc 8.3.0
    • 输出:**

    • 结果文本:**

    • 我的理解是**
  • 在禁用与stdio的同步之后,标准输出流总是未确定的方向;
  • 以便可以打印所有内容。
Win10 + vs2022
    • 输出:**

    • 结果文本:**

    • 我的理解是**
  • 结果与Demo01相同。

演示06:混合cout,打印

#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;                                                                   
}
debian10 buster + gcc 8.3.0
    • 输出:**

    • 我的理解是**
  • 与stdio同步是默认启用的,所以混合cout和printf就像调用cout或printf一样工作。
  • 禁用与stdio同步后,cout将独立工作,cout和printf各忙各的,所以打印内容乱序。
Win10 + vs2022
    • 输出:**

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

演示07:打印非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;
}
debian10 buster + gcc 8.3.0
    • 输出:**

    • 我的理解是**
  • 设置全局locale不影响wcout,此wcout的locale仍然是C locale。因为wcout是一个对象,在构造对象时已经设置了它的locale。
  • 那么在这种情况下,为什么wcout可以打印内容呢?不要忘记C++ iostream默认与stdio同步,我们可以简单地认为,wcout在stdio的缓冲区上工作,且stdio已经被global(myloc)代码设置为en_US. UTF-8。
Win10 + vs2022
    • 输出:**

    • 我的理解是**
  • 没什么特别的

演示08:打印非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;
}
debian10 buster + gcc 8.3.0
    • 输出:**

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

小行星

    • 我的理解是**
  • printf和cout总是按顺序打印,这是windows特别的地方,这一点已经提到好几次了;
  • wprintf print empty相当于linux中的"??";
  • 所以,什么是新的是乱码!我尝试uncomment的std::locale::global(myloc);代码,打印内容是ok!所以,我认为windows实现是小特殊,wcout可能依赖于更多的东西,需要改变locale由全局locale设置.

演示09:打印非ASCII字符--方法C

此演示依赖于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;
}
Win10 + vs2022
    • 输出:**

    • 我的理解是**
  • 在_setmode之后,全局和wcout语言环境仍然是C语言环境;

  • 为什么wcout和wprintf都能正确打印内容?我猜可能是windows在标准输出流之后实现了一种机制,通过_setmode指定的模式来翻译内容。

相关问题