读取管道(C/C++),没有错误,但不是所有数据
Read pipe (C/C++), no error, but not all data
在 C++ 程序中,我想获取一些 python 程序可以轻松提供的数据。 C++ 程序调用 popen()
,读取数据(序列化的 protobuf)并继续。这工作正常,但最近开始失败,收到的字符串比发送的字符串短。
我试图理解为什么我不阅读我写的内容(尽管没有报告错误)以及如何生成进一步的假设。 Fwiw,这是在 linux(64 位)上,并且两个进程都是本地的。 Python 是 2.7。
(确实数据量变大了(现在是 17MB,以前是 500KB),但这不应该导致失败,尽管这是一个明确的信号,我需要为此做一些改变效率。)
在 python 方面,我计算了一个 group_id 映射到组的字典(一个 RegistrationProgress
,参见下面):
payload = RegistrationProgressArray()
for group_id, group in groups.items():
payload.group.add().CopyFrom(group)
payload.num_entries = len(groups)
print('{a}, {p}'.format(a=len(groups), p=len(payload.group)),
file=sys.stderr)
print(payload.SerializeToString())
print('size={s}'.format(s=len(payload.SerializeToString())),
file=sys.stderr)
请注意 a
和 p
在 python 一侧匹配(正确!)。大小约为 17MB。在 C++ 方面,
string FetchProtoFromXXXXX<string>(const string& command_name) {
ostringstream fetch_command;
fetch_command << /* ... */ ;
if (GetMode(kVerbose)) {
cout << "FetchProtoFromXXXXX()" << endl;
cout << endl << fetch_command.str() << endl << endl;
}
FILE* fp = popen(fetch_command.str().c_str(), "r");
if (!fp) {
perror(command_name.c_str());
return "";
}
// There is, sadly, no even remotely portable way to create an
// ifstream from a FILE* or a file descriptor. So we do this the
// C way, which is of course just fine.
const int kBufferSize = 1 << 16;
char c_buffer[kBufferSize];
ostringstream buffer;
while (!feof(fp) && !ferror(fp)) {
size_t bytes_read = fread(c_buffer, 1, kBufferSize, fp);
if (bytes_read < kBufferSize && ferror(fp)) {
perror("FetchProtoFromXXXXX() failed");
// Can we even continue? Let's try, but expect that it
// may set us up for future sadness when the protobuf
// isn't readable.
}
buffer << c_buffer;
}
if (feof(fp) && GetMode(kVerbose)) {
cout << "Read EOF from pipe" << endl;
}
int ret = pclose(fp);
const string out_buffer(buffer.str());
if (ret || GetMode(kVerbose)) {
cout << "Pipe closed with exit status " << ret << endl;
cout << "Read " << out_buffer.size() << " bytes." << endl;
}
return out_buffer;
}
)
大小约为 144KB。
我发送的 protobuf 看起来像这样。 num_entries
有点偏执,因为它应该与 group_size()
相同,而 group_size()
与 group().size()
.
相同
message RegistrationProgress { ... }
message RegistrationProgressArray {
required int32 num_entries = 1;
repeated RegistrationProgress group = 2;
}
那我运行就是
array = FetchProtoFromXXXXX("my_command.py");
cout << "size=" << array.num_entries() << endl;
if (array.num_entries() != array.group_size()) {
cout << "Something is wrong: array.num_entries() == "
<< array.num_entries()
<< " != array.group_size() == " << array.group_size()
<< " " << array.group().size()
<< endl;
throw MyExceptionType();
}
而 运行ning 的输出是
122, 122
size=17106774
Read EOF from pipe
Pipe closed with exit status 0
Read 144831 bytes.
size=122
Something is wrong: array.num_entries() == 122 != array.focus_group_size() == 1 1
检查反序列化的 protobuf,group 似乎是一个长度为 1 的数组,仅包含我预期的数组的第一个元素。
这个...
buffer << c_buffer;
...要求 c_buffer
包含 ASCIIZ 内容,但在您的情况下您不是 NUL 终止它。
相反,请确保捕获读取的确切字节数(即使嵌入了 NUL
s):
buffer.write(c_buffer, bytes_read);
您将每个块连接到输出 buffer
中:
buffer << c_buffer;
正如 Tony D 在他的回答中解释的那样,您不会在 c_buffer
之前执行 null 终止,因此如果 c_buffer
不包含嵌入的空字符,您将调用未定义的行为。
相反,如果 c_buffer
包含嵌入的空字符,流的部分将被剥离和忽略。
您确定流式传输协议不包含嵌入的 '[=15=]'
字节吗?
您还应该阅读 Why is “while ( !feof (file) )” always wrong?,尽管在您的情况下,我认为这不会导致您的问题。
在 C++ 程序中,我想获取一些 python 程序可以轻松提供的数据。 C++ 程序调用 popen()
,读取数据(序列化的 protobuf)并继续。这工作正常,但最近开始失败,收到的字符串比发送的字符串短。
我试图理解为什么我不阅读我写的内容(尽管没有报告错误)以及如何生成进一步的假设。 Fwiw,这是在 linux(64 位)上,并且两个进程都是本地的。 Python 是 2.7。
(确实数据量变大了(现在是 17MB,以前是 500KB),但这不应该导致失败,尽管这是一个明确的信号,我需要为此做一些改变效率。)
在 python 方面,我计算了一个 group_id 映射到组的字典(一个 RegistrationProgress
,参见下面):
payload = RegistrationProgressArray()
for group_id, group in groups.items():
payload.group.add().CopyFrom(group)
payload.num_entries = len(groups)
print('{a}, {p}'.format(a=len(groups), p=len(payload.group)),
file=sys.stderr)
print(payload.SerializeToString())
print('size={s}'.format(s=len(payload.SerializeToString())),
file=sys.stderr)
请注意 a
和 p
在 python 一侧匹配(正确!)。大小约为 17MB。在 C++ 方面,
string FetchProtoFromXXXXX<string>(const string& command_name) {
ostringstream fetch_command;
fetch_command << /* ... */ ;
if (GetMode(kVerbose)) {
cout << "FetchProtoFromXXXXX()" << endl;
cout << endl << fetch_command.str() << endl << endl;
}
FILE* fp = popen(fetch_command.str().c_str(), "r");
if (!fp) {
perror(command_name.c_str());
return "";
}
// There is, sadly, no even remotely portable way to create an
// ifstream from a FILE* or a file descriptor. So we do this the
// C way, which is of course just fine.
const int kBufferSize = 1 << 16;
char c_buffer[kBufferSize];
ostringstream buffer;
while (!feof(fp) && !ferror(fp)) {
size_t bytes_read = fread(c_buffer, 1, kBufferSize, fp);
if (bytes_read < kBufferSize && ferror(fp)) {
perror("FetchProtoFromXXXXX() failed");
// Can we even continue? Let's try, but expect that it
// may set us up for future sadness when the protobuf
// isn't readable.
}
buffer << c_buffer;
}
if (feof(fp) && GetMode(kVerbose)) {
cout << "Read EOF from pipe" << endl;
}
int ret = pclose(fp);
const string out_buffer(buffer.str());
if (ret || GetMode(kVerbose)) {
cout << "Pipe closed with exit status " << ret << endl;
cout << "Read " << out_buffer.size() << " bytes." << endl;
}
return out_buffer;
}
)
大小约为 144KB。
我发送的 protobuf 看起来像这样。 num_entries
有点偏执,因为它应该与 group_size()
相同,而 group_size()
与 group().size()
.
message RegistrationProgress { ... }
message RegistrationProgressArray {
required int32 num_entries = 1;
repeated RegistrationProgress group = 2;
}
那我运行就是
array = FetchProtoFromXXXXX("my_command.py");
cout << "size=" << array.num_entries() << endl;
if (array.num_entries() != array.group_size()) {
cout << "Something is wrong: array.num_entries() == "
<< array.num_entries()
<< " != array.group_size() == " << array.group_size()
<< " " << array.group().size()
<< endl;
throw MyExceptionType();
}
而 运行ning 的输出是
122, 122
size=17106774
Read EOF from pipe
Pipe closed with exit status 0
Read 144831 bytes.
size=122
Something is wrong: array.num_entries() == 122 != array.focus_group_size() == 1 1
检查反序列化的 protobuf,group 似乎是一个长度为 1 的数组,仅包含我预期的数组的第一个元素。
这个...
buffer << c_buffer;
...要求 c_buffer
包含 ASCIIZ 内容,但在您的情况下您不是 NUL 终止它。
相反,请确保捕获读取的确切字节数(即使嵌入了 NUL
s):
buffer.write(c_buffer, bytes_read);
您将每个块连接到输出 buffer
中:
buffer << c_buffer;
正如 Tony D 在他的回答中解释的那样,您不会在 c_buffer
之前执行 null 终止,因此如果 c_buffer
不包含嵌入的空字符,您将调用未定义的行为。
相反,如果 c_buffer
包含嵌入的空字符,流的部分将被剥离和忽略。
您确定流式传输协议不包含嵌入的 '[=15=]'
字节吗?
您还应该阅读 Why is “while ( !feof (file) )” always wrong?,尽管在您的情况下,我认为这不会导致您的问题。