Search code examples
cnanopb

Input string consists of null terminating chars


I'm using NanoPB to send an encoded data (array of unsigned char) from a server to a client. I'm mapping each byte as a single char, concatenate them, and next, send as a whole string through a network. In the client side, I have a serial interface that can read the server's response with getc or gets. Problem is the buffer might have null-terminating chars and gets would fail. For example, suppose buffer contains something like this:

unsigned char buffer[] = {72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 0, 24, 1, 32, 1, 40, 0};

For simplicity, I wrote the buffer to a file and trying to read it back and reconstruct it (with help of this):

#include <stdio.h>

void strInput(FILE *fp, char str[], int nchars) {
    int i = 0;
    int ch;
    while ((ch = fgetc(fp)) != '\n' && ch != EOF) {
        if (i < nchars) {
            str[i++] = ch;
        }
    }
    str[i] = '\0';
}

void readChars(FILE *fp)
{
    char c = fgetc(fp);
    while (c != EOF)
    {
        printf("%c", c);
        c = fgetc(fp);
    }
}


int main() {
    FILE *fp;
    const char* filepath = "mybuffer.txt";
    char c;
    char buffer[100];

    fp = fopen(filepath, "r+");    
    strInput(fp, buffer, sizeof(buffer));
    printf("Reading with strInput (WRONG): %s\r\n", buffer);
    fclose(fp);

    fp = fopen(filepath, "r+");
    printf("Reading char by char: ");
    readChars(fp);
    printf("\r\n");
    fclose(fp);

    getchar();
    return 0;
}

And here is the output:

Reading with strInput (WRONG): Hello world
Reading char by char: Hello world  (

How can I reconstruct the buffer from that file? Why readChars print all the buffer but strInput not?


Solution

  • "Why readChars print all the buffer but strInput not?"

    The readChars() function actually prints all characters as they are read, one at a time, in the function:

    while (c != EOF)
        {
            printf("%c", c);
            c = fgetc(fp);
        }
    

    But, the strInput() function prints the contents of buffer[] as a string using the %s conversion specifier:

    strInput(fp, buffer, sizeof(buffer));
    printf("Reading with strInput (WRONG): %s\r\n", buffer);
    

    Printing stops when an embedded \0 character is encountered this time, because that is what %s does.

    Note that c in the readChars() function should be an int, not a char. The fgetc() function returns an int value, and EOF may not be representable in a char.

    If you want to see the embedded null bytes, print the characters from buffer[] one at a time:

    #include <stdio.h>
    
    int main(void)
    {
        FILE *fp = fopen("mybuffer.txt", "r");  // should check for open failure
    
        char buffer[100] = { '\0' };  // zero to avoid UB when printing all chars
        fgets(buffer, sizeof buffer, fp);
    //  could just as well use:
    //  strInput(fp, buffer, sizeof(buffer));
    
        for (size_t i = 0; i < sizeof buffer; i++) {
            if (buffer[i] == '\0') {
                putchar('*');        // some character not expected in input
            }
            else {
                putchar(buffer[i]);
            }
        }
        putchar('\n');
    
        return 0;
    }