我正在尝试从C++应用程序gcc版本9.3.0(Ubuntu 9.3.0-17ubuntu1~20.04(中使用gnuplot。我遇到了关于打印到文件的奇怪行为。
因此,可重复的例子是:
#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 pngnset output '"+name1+"'nplot sin(x)nunset outputn";
// 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 output
或unset output
需求,并尝试了这两种变体。
为什么std::filesystem::remove
的行为如此奇怪?
为什么std::filesystem::remove的行为如此奇怪?
您似乎误解了std::filesystem::remove()
的返回值和错误代码(代码中的ec
(。如果要删除的文件不存在(ec
将为零(,则函数不会引发错误。如果要删除的文件不存在,则只有不带error_code&
的函数才会返回false
,如果要删除,则返回true
。请参阅cppreference.com上的std::filesystem::remove()
说明。
效果:路径p标识的文件或空目录被删除,就像POSIX删除一样。未遵循符号链接(符号链接已删除,而不是其目标(。
如果文件已删除,则返回:
true
;如果文件不存在,则返回false
。采用error_code&
参数的重载在出现错误时返回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<<"successful: n";
if ( retval ) {
std::cout<<"file existed and removedn";
}
else {
std::cout<<"file didn't existn";
}
}
else { // Error
std::cout<<"unsuccessful: "<<ec.value()<<" "<<ec.message()<<"n";
}
添加
CCD_ 22的位置改变结果的原因是CCD_ 23打开的流被缓冲。
当调用std::filesystem::remove()
时,由于缓冲,gnuplot尚未接收到fwrite()
编写的命令。因此,在该步骤中/tmp/1.png";尚未创建。
然后,当调用pclose()
时,gnuplot接收命令,并且文件"/tmp/1.png";由gnuplot创建。您查看的文件是在调用std::filesystem::remove()
之后创建的。
您可以使用函数fflush()
显式地刷新缓冲区。然而,即使使用fflush()
,由于popen的异步性质,仍然有可能在gnuplot命令完成之前调用std::filesystem::remove()
。
为了确保在gnuplot过程完成后擦除该文件,您将需要gnuplot和c++程序可以同步的实现(或包装器库(。