1

I have an issue with writing a uint32_t value to a file and reading it back. For writing it into a file, I use

uint32_t num = 2036465665 ; 
FILE *fp = fopen("test.dat", "w");
fprintf(fp,"value = %" PRIu32 "\n", num);
fclose(fp);

For reading it, I first copy the contents of file into an array data[] and then extract values line by line.

int len = 100;
char *line = malloc(len * sizeof(char));
char field[256], tmp[2];

FILE *fp = fopen("test.dat", "r");
while ( -1 != getline(&line, &len, fp)){
            char *value=malloc(sizeof(char)*256);
            sscanf( line, "%s %s %s", field, tmp, value);
            data[i] = value;
            i++;
        }
 fclose(fp);

To read the value of uint32_t variable, I get different values with atoi and strtoul with different bases, but not the exact value written into the file.

uint32_t read_num;
read_num = strtoul (data[0], NULL, 32);

This gives value of read_num as 1345324165.

read_num = (uint32_t) atoi(data[0]);

gives 3226523632

How do I get the correct value saved in the file. Is the error in (i) reading the file contents using sscanf into string or (ii) the strtoul vs atoi (iii) base in strtoul().

marc
  • 949
  • 14
  • 33
  • 1
    strtoul (data[0], NULL, 32); -- Are your values in base 32? I would look up how to use this function properly; based on what I have glanced at it should be 10, not 32. – MateoConLechuga Apr 14 '17 at 23:47
  • Also I would recommend using EOF rather than -1 as the return check for getline. – MateoConLechuga Apr 14 '17 at 23:49
  • 2
    @MateoConLechuga: Actually, [`getline()`](http://pubs.opengroup.org/onlinepubs/9699919799/functions/getline.html) is defined in POSIX to return `-1` and not `EOF`, so using `-1` is correct. – Jonathan Leffler Apr 14 '17 at 23:50
  • Oops I forgot; thank you – MateoConLechuga Apr 14 '17 at 23:50
  • @MateoConLechuga: base 10 doesn't work either. – marc Apr 14 '17 at 23:51
  • 1
    @kris: have you printed out the string that you do read? What is the definion of the `data` array? Did you check the return value from `sscanf()`? – Jonathan Leffler Apr 14 '17 at 23:51
  • 1
    Is your test.dat file ascii? Please post a sample of it if possible. – Chimera Apr 14 '17 at 23:54
  • Add width limits and check return value `if (3 != sscanf( line, "%255s %1s %255s", field, tmp, value) puts("oops");` – chux - Reinstate Monica Apr 14 '17 at 23:56
  • @JonathanLeffler, yes when I read the string as printf("%s %s %s\n", field, tmp, value); it prints out value = 3226523632. – marc Apr 14 '17 at 23:57
  • @chux, I've tested with the return value, it doesn't print 'oops'. – marc Apr 14 '17 at 23:59
  • 1
    My compiler complained about `int len;` not being a good match for the `size_t *` that `getline()` expects. Is that a factor in your problem? With that fixed (I tell the compiler to treat warnings like errors), I got your code to work for me (macOS Sierra 10.12.4, GCC 6.3.0 — 64-bit compilation). – Jonathan Leffler Apr 15 '17 at 00:03
  • @JonathanLeffler, I've changed `int len` to `size_t len`. Mine is 64-bit Apple LLVM version 6.1.0 (clang-602.0.49). It doesn't work for me yet. – marc Apr 15 '17 at 00:11
  • Gosh! That's an old XCode release; I have 8.1.0 clang-802.0.41. However, I don't think the compiler version is an issue here. Try the code from my "it doesn't reproduce" answer (it was too big to try placing it as a string of comments). – Jonathan Leffler Apr 15 '17 at 00:14

1 Answers1

0

I adapted your code fragments into this close-to-MCVE (Minimum, Complete, Verifiable Example), and it seems to work correctly on my Mac running macOS Sierra 10.12.4 using GCC 6.3.0 as the compiler. (I also get the same results with clang — Apple LLVM version 8.1.0 (clang-802.0.41).)

#include <assert.h>
#include <errno.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>

static void write_data(const char *filename)
{
    uint32_t num = 2036465665; 
    FILE *fp = fopen(filename, "w");
    assert(fp != NULL);
    fprintf(fp,"value = %" PRIu32 "\n", num);
    printf("Wrote %" PRIu32 "\n", num);
    fclose(fp);
}

int main(void)
{
    const char filename[] = "test.dat";

    write_data(filename);

    char *data[10];
    size_t len = 100;
    char *line = malloc(len * sizeof(char));
    assert(line != 0);
    char field[256], tmp[2];

    FILE *fp = fopen(filename, "r");
    assert(fp != NULL);
    int i = 0;
    while ( -1 != getline(&line, &len, fp))
    {
        printf("Read: [%s]\n", line);
        char *value = malloc(sizeof(char)*256);
        assert(value != 0);
        if (sscanf(line, "%s %1s %s", field, tmp, value) != 3)
            assert(0);
        printf("Field [%s] tmp [%s] value [%s]\n", field, tmp, value);
        data[i] = value;
        i++;
    }
    free(line);
    fclose(fp);

    char *endptr;
    errno = 0;
    printf("Processing [%s]\n", data[0]);
    uint32_t read_num = strtoul(data[0], &endptr, 10);
    assert(endptr != data[0]);
    free(data[0]);

    printf("Number read: %" PRIu32 "\n", read_num);
    return 0;
}

The use of assert for error handling is lazy in the extreme, but better than no checking. The error checking after strtoul() is also sloppy — there are unchecked conditions (read_num == ULONG_MAX and errno == ERANGE, etc.)

When compiled and run, I get:

$ gcc -O3 -g -std=c11 -Wall -Wextra -Werror -Wmissing-prototypes \
>     -Wstrict-prototypes -Wold-style-definition rd53.c -o rd53
$ ./rd53
Wrote 2036465665
Read: [value = 2036465665
]
Field [value] tmp [=] value [2036465665]
Processing [2036465665]
Number read: 2036465665
$

If the use of size_t len vs int len is not your problem, maybe you can try this code and see what it gives you.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278