0

How can I read integer value from stdin using read() system call?

#include<unistd.h>
#include<stdio.h>
#include<sys/types.h>
int main(){
    int n;
    read(0,&n,sizeof(n));
    printf("[%d]\n",n);
    return 0;
}

OUTPUT:

54
[668725]
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
  • 3
    With `read()` you can read raw bytes representing characters that might represent an integer. You will have to do all of the processing of the conversion from these bytes to integer. Maybe you first tell us *why* would you want to do that? – Eugene Sh. Apr 24 '23 at 19:25
  • You can't "read()" a binary integer directly. You read the digits (byte characters) into a buffer, then you translate the digits, for example using [atoi()](https://linux.die.net/man/3/atoi). If "int" on your platform is 32 bits, then your code can read at most 4 digits. – paulsm4 Apr 24 '23 at 19:29
  • Why do you want to do that? It's much better to use `scanf` or even better `fgets` and `sscanf` – klutt Apr 24 '23 at 19:31

3 Answers3

5

It looks like your input was the ASCII characters '5' and '4' followed by a new line character '\n'. You read them into a four-byte buffer, represented by your integer n. Apparently that integer was initialized to zero (not assured in C), so the fourth byte of the buffer, not set by the read() call, was zero and not a random number.

The decimal values of "54\n\0" are 53, 52, 10, 0. On your machine architecture, integers are stored in little-endian order (also not assured in C), so those four bytes become 53 + 256*52 + 65536*10 + 16777216*0 = 668725.

If you want to read a string of ASCII decimal digits from your input and convert that to the corresponding integer, you could write your own code to read a digit at a time, check that is a digit, i.e. in the range '0' to '9', convert it to its value by subtracting '0', and then multiplying your integer value (initialized to 0) by 10 for each digit and then adding that digit.

Or you can use scanf(), or strtol() after reading the characters into a buffer, to do that conversion for you. E.g.:

scanf("%d", &n);
Mark Adler
  • 101,978
  • 13
  • 118
  • 158
1

The function read will read raw characters from the file descriptor.

If you want to attempt to convert a sequence of characters to an int, you can use the function strtol. However, using that function requires that the sequence of characters is a valid string, which means that it must be terminated by a null character. The function read will not add a terminating null character at the end of the input for you. Therefore, you will have to add it yourself in the appropriate place.

Also, when dealing with line-based user input, it is usually recommended to use the function fgets or getline. The function read is not designed for reading one line at a time, because there is not way to tell it to stop once it encounters the newline character.

Simple programs often use scanf, like this:

int n;
if ( scanf( "%d", &n ) == 1 )
{
    printf( "Input was successfully converted to %d.\n", n );
}
else
{
    printf( "Input conversion failed!\n" );
}

to read and convert one integer from standard input. However, this has several disadvantages. For example:

  • scanf will not match the entire line. It will leave at least the newline character on the line, which can cause trouble.
  • it will accept input such as 6abc as valid input for the number 6.
  • if the number is too large or to small to be representable as an int, the behavior is undefined and you cannot detect this.

For these reasons, is it is generally not recommended to use scanf for user input. It is usually better to use fgets/getline in combination with strtol.

Here is an example program which prompts the user to enter a number and attempt to convert it. If this conversion fails, it automatically reprompts the user. This program will perform extensive input validation. I am not sure if this is what you are looking for.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <errno.h>

//forward declaration
int get_int_from_user( const char *prompt );

int main( void )
{
    int num;

    num = get_int_from_user( "Please enter an integer: " );

    printf( "Input successfully converted to %d.\n", num );
}

//This function will attempt to read one integer from the user. If
//the input is invalid, it will automatically reprompt the user,
//until the input is valid.
int get_int_from_user( const char *prompt )
{
    //loop forever until user enters a valid number
    for (;;)
    {
        char buffer[1024], *p;
        long l;

        //prompt user for input
        fputs( prompt, stdout );

        //get one line of input from input stream
        if ( fgets( buffer, sizeof buffer, stdin ) == NULL )
        {
            fprintf( stderr, "Unrecoverable input error!\n" );
            exit( EXIT_FAILURE );
        }

        //make sure that entire line was read in (i.e. that
        //the buffer was not too small)
        if ( strchr( buffer, '\n' ) == NULL && !feof( stdin ) )
        {
            int c;

            printf( "Line input was too long!\n" );

            //discard remainder of line
            do
            {
                c = getchar();

                if ( c == EOF )
                {
                    fprintf( stderr, "Unrecoverable error reading from input!\n" );
                    exit( EXIT_FAILURE );
                }

            } while ( c != '\n' );

            continue;
        }

        //attempt to convert string to number
        errno = 0;
        l = strtol( buffer, &p, 10 );
        if ( p == buffer )
        {
            printf( "Error converting string to number!\n" );
            continue;
        }

        //make sure that number is representable as an "int"
        if ( errno == ERANGE || l < INT_MIN || l > INT_MAX )
        {
            printf( "Number out of range error!\n" );
            continue;
        }

        //make sure that remainder of line contains only whitespace,
        //so that input such as "6abc" gets rejected
        for ( ; *p != '\0'; p++ )
        {
            if ( !isspace( (unsigned char)*p ) )
            {
                printf( "Unexpected input encountered!\n" );

                //cannot use `continue` here, because that would go to
                //the next iteration of the innermost loop, but we
                //want to go to the next iteration of the outer loop
                goto continue_outer_loop;
            }
        }

        return l;

    continue_outer_loop:
        continue;
    }
}

This program has the following behavior:

Please enter an integer: abc
Error converting string to number!
Please enter an integer: 10000000000000000
Number out of range error!
Please enter an integer: 60.5
Unexpected input encountered!
Please enter an integer: 6abc
Unexpected input encountered!
Please enter an integer: 25
Input successfully converted to 25.
Andreas Wenzel
  • 22,760
  • 4
  • 24
  • 39
1

When reading integers from a file, you have to be very, very clear on how the integers are stored in that file. Is it a human-readable, text or "ASCII" file? Or is it a machine-readable, binary file?

In a text file, an integer will be represented by characters matching the digits of the decimal (or perhaps hexadecimal or some other base) representation of the number.

But in a binary file, an integer will be represented by bytes which exactly match the integer's binary representation in memory.

Which kind of file are you trying to read? It looks like you're trying to read text, but read() is usually more useful for reading binary data files.

Let's consider an example. Let's think about the number 9281. In a text file, this number would be represented by four characters (four bytes): 9 2 8 1. In ASCII, the characters 9, 2, 8, and 1 have hexadecimal values 0x39, 0x32, 0x38 and 0x31, so if we looked at the file using some kind of a hex dump program, those are the values we'd see.

On the other hand, a binary file containing the number 9281 would contain two bytes with the values 0x24 and 0x41. Where do those values come from? Well, 9281 in base 16 is 0x2441. (If we looked at the file's text, we'd see the characters A and $, corresponding to the hex values 0x41 and 0x24, although that's not very interesting, and the specific characters A $ are almost random/meaningless in this context.)

Depending on whether integers are two bytes or four bytes (16 bits or 32 bits), an int might take up more space in your file. Also the bytes might appear in the other order than you expected.

Finally, in answer to your question: If it's a binary file, you can read it more or less like the code in your question. But it it's a text file, it will be much easier to read it using fscanf, or fgets to read a line of text, followed by strtol or some other function to convert a string to an integer. To read and convert an integer from a text file, using read, might look something like this:

char buf[11];
int r;
r = read(0, buf, 10);
if(r > 0) {
    buf[r] = '\0';
    int n = atoi(buf);
    printf("[%d]\n",n);
}
Steve Summit
  • 45,437
  • 7
  • 70
  • 103