【问题标题】:Convert float into String and send from C code to Python through Named Pipe将浮点数转换为字符串并通过命名管道从 C 代码发送到 Python
【发布时间】:2019-08-21 20:13:29
【问题描述】:

我想使用命名管道将浮点值从 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);
}

我使用 sprintf 将浮点数转换为字符串,代码如下,

void SendDataOverFifo(float angle)
{
    char str[64];
    unsigned char writeBuffer[] = "Hello!";

    Init_FIFO();

    sprintf(str, "%f\n", 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

注意:如果我尝试只发送“你好!”,它可以正常工作。

我在这里缺少什么?提前致谢。

【问题讨论】:

  • 你能使用像sprintf(str, "%e\n", angle);这样的指数格式吗?

标签: python c linux named-pipes


【解决方案1】:

第一个红旗在sprintf 调用中;它不知道你的目标缓冲区str 有多大,所以如果你不小心可能会溢出。使用单个浮点数和 64 个字节,该步骤应该没问题。

但是,您没有存储返回值,所以此时您不知道格式化文本有多大。然后你使用了sizeof,它告诉你缓冲区有多大,而不是你刚刚放入了多少数据。您可以使用基于字符串的函数(因为sprintf 编写了一个以空结尾的字符串),例如strlen(测量字符串)或fputs(将字符串写入文件)。

一个更简单的捷径可能是首先使用fprintf,而不需要分配一个单独的缓冲区(它可能使用一个内置在FILE 中的缓冲区)来存储格式化的字符串。

虽然不一定可移植或安全,但可以使用 fdopen 等函数在文件描述符(例如 writeclose 使用)和 FILE(例如 fprintf 使用)之间进行转换.

【讨论】:

  • 我认为我不能使用 fprintf 因为我使用的是命名管道而不是文件
  • ...这就是最后一段解释你可以包装文件描述符的原因。你在一个 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);

或者事先知道buffer的大小,再调用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);

【讨论】:

  • snprintf 返回要写入的字符数,所以如果你真的想防止缓冲区溢出,你还必须检查ret &lt;= sizeof strasprintf,如果可用,允许在一次调用中分配足够的缓冲区。
【解决方案3】:

我解决了这个问题,解决方案是改变这一行

write(fd_fifo, str, sizeof(str));

write(fd_fifo, str, strlen(str));

【讨论】:

  • 这是我和卡米尔提出的大约三种解决方案之一。
  • @YannVernier 抱歉,我没有意识到你们给我的 strlen 建议我想我很困,我的错...
猜你喜欢
  • 1970-01-01
  • 2011-03-01
  • 2011-11-25
  • 2019-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多