如何转换从管道接收到的二进制数

Posted

技术标签:

【中文标题】如何转换从管道接收到的二进制数【英文标题】:How to convert binary numbers received from a pipe 【发布时间】:2021-09-18 20:12:18 【问题描述】:

我有一个包含一系列数字的管道。 我会阅读它,并且在每次迭代中,我都会将读取的内容写入一个 txt 文件。 如何将二进制数转换为十进制数并将它们记录在我的 txt 文件中,每行一个?

PS 管道在另一个文件中创建并使用以下命令写入数字:

write (fp, &mynum, sizeof (mynum));

主文件

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>

#define MAX_BUF 1024


int main()


    int fd, fp ; 
    int bytesread;
    char * myfifo = "myfifo";
    char buf[MAX_BUF];
    int count = 0; 
    char *filename = "memorizza.txt"; 
    fp = open(filename,O_WRONLY | O_APPEND);
  
    if ((fd = open(myfifo, O_RDONLY))==-1 )   // opening pipe

        perror(myfifo);
        return 1; 
    
 
    while(1)
    
        if((bytesread = read( fd, buf, MAX_BUF - 1)) > 0) //read pipe
        
            buf[bytesread] = '\0';
       
            count++; 
            write(fp , buf , MAX_BUF ); 
       
        
        else 

            printf("Ho ricevuto %d numeri primi \n" , count); 
            break;
        
    
    close(fd);
    close(fp);
    return 0;

【问题讨论】:

如果您有字符,Write 可用于文本文件。它会给二进制数据带来不可读的结果。 好的,所以首先我应该将从管道中读取的内容从二进制转换为字符,然后将其写入文件? 数字是如何写入管道的?作为文本还是二进制值?对于二进制你需要转换 (fprintf),对于文本你需要考虑分隔符。 @LucaLucazzo 请edit您的问题并在此处添加所有要求的信息或说明。添加一个简单的示例程序,将一些硬编码的数字写入管道。从管道读取时,您不能假设获得的字节数与使用单个 write 调用写入的字节数相同。您可能会得到任意数量的字节,一个以上的数字或部分数字,因此您必须检查 read 的返回值以找出它已读取到缓冲区的字节数。而不是写入/读取二进制数据,fprintf 文本格式的数字可能会更好,例如每行一个。 您确定您使用的是管道吗?看起来您正在使用文件。看看documentation about pipes。 【参考方案1】:

要从文件描述符中读取二进制编码的 int(假设主机字节顺序),您必须将指向 int 的指针传递给 read,并准确读取 sizeof(int) 字节。一个简单的实现应该是这样的:

int i;
ssize_t e = read(fd, &i, sizeof(i));

这个实现的问题是 read 不能保证一次性给你所有你要求的字节。所以我们必须继续阅读,直到我们有所有的字节:

int i;
char *buffer = (char *)&i;
size_t left_to_read = sizeof(i);

while (left_to_read)
  
     ssize_t e = read(fd, buffer, left_to_read);
     if (e < 0 && errno != EINTR)
       
          perror("Failure reading from file descriptor");
          exit(EXIT_FAILURE);
       
     else if (e == 0)
       
          /* 
             Handle EOF
          */  
       
     else if (e > 0)
       
         left_to_read -= e;
         buffer += e;
       
   

编辑:

读完int后自然可以正常方式转换为ascii:

fprintf(outfile, "%d\n", i);        

【讨论】:

我只想补充一点,我的解决方案比它需要的要慢。该实现为每个int 调用至少1 个read。在典型的操作系统上,每个read 都会在内核中进行一次上下文切换,这很慢。最好读入比sizeof(int) 更大的缓冲区,然后从该缓冲区中提取单个整数。如果额外的复杂性是值得的,那么获得的加速是你必须决定的。【参考方案2】:

第一个近似值,如果你用

写入管道
write (fp, & mynum, sizeof (mynum));

(引自 cmets),其中mynum 的类型为int,则相应的读取将是

int fp = /* open read end of the pipe */;
int my_read_num;
read(fp, &my_read_num, sizeof(my_read_num));

当它按预期工作时,它会为您提供所需的整数作为int,然后您可以通过fprintf() 发出它而无需进一步操作,或者用它做任何您通常可能用@987654327 做的事情@。

但也有并发症,包括:

    write()read() 都不能保证传输请求的全部字节数。它们的返回值告诉您它们实际传输了多少字节,并且健壮的代码需要考虑短写入和读取,通常通过额外的writeread 调用来传输任何剩余的字节,视情况而定。

    write()read() 的调用不能保证自动配对。也就是说,多个write()s 写入的数据可能会被单个read() 读取,反之亦然。但是,对于您的特定情况,这不是什么大问题(一旦处理(1)),因为您正在传输已知大小的单位。它通常会咬那些试图传输不同长度数据(例如文本行)的人,而没有建立一种方法来传达消息长度或边界。

    总的来说,您不一定要依赖两个端点来使用相同的整数表示。例如,一端可能对int 使用32 位大端,而另一端对int 使用16 位小端。有一些方法可以解决这个问题,但在您的特定情况下,您无需担心它,即两端都在同一台机器上运行,并为它们传输的对象使用相同的 C 数据类型。

【讨论】:

以上是关于如何转换从管道接收到的二进制数的主要内容,如果未能解决你的问题,请参考以下文章

JAVA如何将二进制数转换成文件?

如何将负二进制数转换为int?

进制转换

如何将数值转化为二进制数?

ValueError:无法使用 dtype='numeric' 将字节/字符串数组转换为十进制数

如何在 Redshift 中将十六进制数转换为二进制数?