'How to convert binary numbers received from a pipe

I have a pipe that contains a series of numbers. I will read it and at each iteration I will write what has been read in a txt file. How can I convert binary numbers to decimal numbers and record them, one per line, in my txt file?

PS The pipe is created in another file and the numbers are written using this command:

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

Main file

#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;
}


Solution 1:[1]

To read a binary encoded int from a file descriptor (assuming host byte order), you have to pass a a pointer to int to read, and read exactly sizeof(int) bytes. A naive implementation would look like this:

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

The problem with this implementation is that read is not guaranteed to give you all the bytes you asked for in one go. So we have to keep reading until we have all the bytes:

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;
       }
   }

EDIT:

Naturally after reading the int can be converted to ascii the normal way:

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

Solution 2:[2]

To a first approximation, if you write to the pipe with

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

(quoted from comments), where mynum has type int, then the corresponding read would be

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

When that works as intended, it gets you the wanted integer as an int, and you can then emit it without further manipulation via fprintf(), or otherwise do anything with it that you might ordinarily do with an int.

But there are complications, including:

  1. Neither write() nor read() is guaranteed to transfer the full number of bytes requested. Their return values tell you how many bytes they actually did transfer, and robust code needs to account for short writes and reads, generally by transferring any remaining bytes via additional write or read calls, as appropriate.

  2. Calls to write() and read() are not guaranteed to pair up automatically. That is, data written by multiple write()s might be read by a single read(), and vice versa. That's not so much a problem for your particular case, however, (once you handle (1)) because you are transferring units of known size. It normally bites people who try to transfer varying length data, such as lines of text, without establishing a means to also convey message lengths or boundaries.

  3. In the fullest generality, you cannot necessarily rely on the two endpoints to use the same integer representation. For instance, one end might use 32-bit big-endian for int, whereas the other end uses 16-bit little-endian for int. There are ways to account for that, but you do not need to worry about it in your specific case, where both ends are running on the same machine and use the same C data type for the object they transfer.

Solution 3:[3]

The issue comes with the use of rowMeans with use of the pipes (%>%). rowMeans expects you’ll be giving it a matrix with more than one column. By default, dplyr works columnwise so rowMeans is only seeing the first element of x1, x2, x3, and x4 so it looks like the dimensions are 1x1. You can refer to the columns within the data you are piping by using .:

library(tidyverse)
data %>%
  mutate(means_of_my_rows = rowMeans(.[,c("x1", "x2", "x3", "x4")]))
#>   x1 x2 x3 x4 means_of_my_rows
#> 1  1  3  5  7             4.00
#> 2  2  1  6  4             3.25
#> 3  3  7  7  6             5.75
#> 4  4  4  8  4             5.00
#> 5  5  4  9  9             6.75

Or use the rowwise function in dplyr:

data %>%
  rowwise() %>%
  mutate(means_of_my_rows = mean(c(x1, x2, x3, x4)))
#> # A tibble: 5 x 5
#> # Rowwise: 
#>      x1    x2    x3    x4 means_of_my_rows
#>   <int> <dbl> <int> <dbl>            <dbl>
#> 1     1     3     5     7             4   
#> 2     2     1     6     4             3.25
#> 3     3     7     7     6             5.75
#> 4     4     4     8     4             5   
#> 5     5     4     9     9             6.75

Or you can avoid dplyr and just use base:

data$means_of_my_rows <- rowMeans(data[ , c("x1", "x2", "x3", "x4")])

Created on 2022-01-17 by the reprex package (v2.0.1)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3