#python #c #linux #named-pipes
#python #c #linux #именованные каналы
Вопрос:
Я хотел бы отправлять значения с плавающей запятой из кода C в код Python, используя именованные каналы. Я печатаю полученные значения в терминал на стороне Python, однако наряду с самим значением также отображаются символы тарабарщины.
Открытие канала:
void Init_FIFO(void)
{
// FIFO file path
char * bldc_fifo = "/tmp/bldc_fifo";
// Creating the named FIFO -- mkfifo(<pathname>, <permission>)
mkfifo(bldc_fifo, 0666);
// Open FIFO to write/read data
fd_fifo = open(bldc_fifo, O_RDWR | O_NONBLOCK);
//fd_fifo = open(bldc_fifo, O_WRONLY | O_RDONLY | O_NONBLOCK);
}
Для преобразования float в строку я использую sprintf, и код приведен ниже,
void SendDataOverFifo(float angle)
{
char str[64];
unsigned char writeBuffer[] = "Hello!";
Init_FIFO();
sprintf(str, "%fn", angle);
write(fd_fifo, str, sizeof(str));
//write(fd_fifo, writeBuffer, sizeof(writeBuffer));
close(fd_fifo);
}
Затем для получения кода на стороне Python я использую это
#!/usr/bin/python
import os
import errno
import time
FIFO = '/tmp/bldc_fifo'
try:
os.mkfifo(FIFO)
except OSError as oe:
if oe.errno != errno.EEXIST:
raise
print("Opening FIFO...")
with open(FIFO, encoding='utf-8', errors='ignore') as fifo:
print("FIFO opened")
while True:
time.sleep(0.1)
data = fifo.read()
print(data)
Результат, который я получаю, выглядит примерно так
i-W?UOeiEU11.417070
Где правильный результат должен быть:
11.417070
Примечание: Если я попытаюсь отправить только «Привет!», это сработает без каких-либо проблем.
Чего мне здесь не хватает? Заранее спасибо.
Комментарии:
1. Можете ли вы использовать экспоненциальный формат, подобный
sprintf(str, "%en", angle);
?
Ответ №1:
Первый красный флаг находится в sprintf
вызове; он не знает, насколько велик ваш целевой буфер str
, поэтому может переполниться, если вы не будете осторожны. С одним float и 64 байтами этот шаг должен быть выполнен нормально.
Однако вы не сохранили возвращаемое значение, поэтому на данный момент вы не знаете, насколько велик форматированный текст. Затем вы использовали sizeof
, который сообщает вам, насколько велик буфер, а не сколько данных вы только что поместили в него. Вы могли бы использовать функцию на основе строк (поскольку sprintf
написана строка, заканчивающаяся нулем), такую как strlen
(для измерения строки) или fputs
(для записи строки в файл).
Гораздо более простым способом могло бы быть использование fprintf
в первую очередь, и не нужно выделять отдельный буфер (скорее всего, он использует встроенный в FILE
) для хранения форматированной строки.
Возможно, хотя и не обязательно переносимое или безопасное, преобразование между файловыми дескрипторами (такими как write
и close
use) и FILE
(такими как fprintf
uses) с помощью таких функций, как fdopen
.
Комментарии:
1. Я думаю, что я не могу использовать fprintf, потому что я использую именованные каналы, а не ФАЙЛ
2. … вот почему в последнем абзаце объясняется, что вы можете обернуть файловый дескриптор. Вы находитесь в системе типа Unix, все является файлом (именованный канал — это просто немного особенный файл).
Ответ №2:
Строка:
write(fd_fifo, str, sizeof(str));
вызывает запись неинициализированной памяти в fifo. Вы не хотите записывать весь str
буфер, только размер строки, которую вы хотите передать. И вы можете узнать это по snprintf
возвращаемому значению с помощью strlen(str)
.
int ret = sprintf(str, "%f", ...);
assert(ret > 0); // just to be safe
write(fd_fifo, str, ret);
Использование sprintf
небезопасно для вас, потому что. Используйте snprintf
для защиты от переполнения стека.
int ret = snprintf(str, sizeof(str), ....
// no changes
Таким образом, sprintf
в буфер никогда не будет записано больше sizeof(str)
символов.
Однако лучший способ — не иметь статически выделенного буфера. Вы можете использовать fdopen
:
FILE *f = fdopen(fd_fifo, "w");
if (f == NULL) {
// handle error
}
int ret = fprintf(f, "%f", ...);
if (ret < 0) {
// handle error
}
fclose(f);
или заранее узнайте размер буфера, снова вызовите malloc и snprintf:
int ret = sprintf(NULL, "%f", ...);
assert(ret > 0);
char *str = malloc(ret * sizeof(char));
if (str == NULL) {
// handler error
}
ret = snprintf(str, "%f", ...);
write(fd_fifo, str, ret);
free(str);
Комментарии:
1.
snprintf
возвращает количество символов, которое было бы записано, поэтому, если вы действительно хотите защититься от переполнения буфера, вы также должны проверитьret <= sizeof str
.asprintf
, если доступно, позволяет выделить достаточный буфер за один вызов.
Ответ №3:
Я решил проблему, решением было изменение этой строки
write(fd_fifo, str, sizeof(str));
Для
write(fd_fifo, str, strlen(str));
Комментарии:
1. Это одно из примерно трех решений, указанных мной и Камилом.
2. @YannVernier извините, я не понял предложения strlen, которое вы, ребята, дали, я хотел спать, я думаю, моя вина…