3

我正在尝试使用来自 C++ 应用程序的 gnuplot,gcc 版本 9.3.0(Ubuntu 9.3.0-17ubuntu1~20.04)。我遇到了关于打印到文件的奇怪行为。

因此,可重现的示例是:

#include <iostream>
#include <filesystem>

int main()
{
    // whatever valid filename
    std::string name1 = "/tmp/1.png";
    // open gnuplot pipe
    auto gp = popen("gnuplot", "w");
    // plot sin(x) to file. Note "unset output" in the end.
    std::string cmd="set term png\nset output '"+name1+"'\nplot sin(x)\nunset output\n";
    // send the command to gnuplot
    fwrite(cmd.c_str(), sizeof(char), cmd.length(), gp);
    std::error_code ec;
    // removing the file
    if (!std::filesystem::remove(name1, ec))
        std::cout<<"unsuccesfully: "<<ec.value()<<"\s"<<ec.message()<<"\n";
    pclose(gp);
    return 0;
}

输出是(很奇怪):

unsuccesfully: 0 Success

会发生什么:gnuplot 成功地将有效的 png 文件写入所需的目的地。但是,std::filesystem::remove不会删除文件,返回false并(因此)打印有关成功的神秘消息,错误代码为 0。在解决问题pclose(gp);之前移动行std::filesystem::remove,因此看起来 gnuplot 确实保存了文件。奇怪的是,如果我手动执行相同操作,我的意思是,我启动 gnuplot,发出相同的命令,而不是退出,我可以使用unlink /tmp/1.png. 我知道 gnuplot 的set outputunset output要求,并尝试了这两种变体。

为什么std::filesystem::remove行为如此奇怪?

4

1 回答 1

1

为什么 std::filesystem::remove 行为如此奇怪?

您似乎误解了 . 的返回值和错误值(ec在您的代码中)std::filesyste::remove()。即使没有您要删除的文件,该函数也不会引发错误。false如果没有您要删除的文件,该函数将返回;否则,它返回true。参见C++17 草案中'std::filesystem::remove()' 的文档。

效果:如果存在(symlink_status(p, ec)),则文件 p 被删除,就像 POSIX remove() 一样。

返回:如果 p 不存在则返回 false,否则返回 true。如果发生错误,带有参数 ec 的签名将返回 false。

由于没有因为没有要删除的文件而引发错误,因此ec.value()在您的代码中将返回0,表示成功完成。

这有点像 UNIX 命令“rm -f”的行为。

std::filesyste::remove()您可以通过将以下内容插入代码来检查的行为。

    std::error_code ec;
    int retval = std::filesystem::remove(name1, ec);
    if ( ! ec ) { // Success
      std::cout<<"succesfully: \n";
      if ( retval ) {
        std::cout<<"file existed and removed\n";  
      }
      else {
        std::cout<<"file didn't exist\n";
      }
    } 
    else {        // No error
      std::cout<<"unsuccesfully: "<<ec.value()<<" "<<ec.message()<<"\n";
    }

添加

之所以会pclose()改变结果的位置,是因为打开的流popen()被缓冲了。

当被调用时,由于缓冲,gnuplot 尚未接收到所std::filesystem::remove()写入的命令。fwrite()因此,在这一步中,还没有创建文件“/tmp/1.png”。

然后,当pclose()被调用时,gnuplot 接收命令,文件“/tmp/1.png”由gnuplot 创建。您查看的文件是调用后创建的文件std::filesystem::remove()

您可以使用函数显式刷新缓冲区fflush()。但是,即使您使用fflush(),由于 popen 的异步特性,仍然有可能std::filesystem::remove()在 gnuplot 命令完成之前调用它。

为确保 gnuplot 进程完成后文件被擦除,您将需要 gnuplot 和 c++ 程序可以同步的实现(或包装库)。

于 2020-11-21T01:43:35.787 回答