1

I was trying to read in data from a txt file. The txt file has multiple entries each occupies a new line, and each entry has variable length of hex byte data delimited by some symbols (say space ' '). A sample txt file looks like the below

e4 e2 e2 e1 ff\n
f2 a2 22 34\n
ff ee dd\n

Using scanf(fp,"%2x",buffer+offset) in a loop, I was trying to load each byte into a byte buffer until the end of each line, marking a complete record. The main problem is to detect the newline character as scanf ignores it completely and jump into the next line. My original code was

do{
    counter=fscanf(datain,"%2x",buffer1+offset);
    fprintf(stdout,"%#2x ",buffer1[offset]);
    offset+=counter;
}while(!feof(datain));
HarryQ
  • 1,281
  • 2
  • 10
  • 25

2 Answers2

2

An alternative and usually simpler way to do such work is to read the entire line with fgets() or getline() and then process the symbols on the line using sscanf(). In many ways, this is similar to the current scheme, but you need to be able to progress through the string, so the %n conversion specification often comes in helpful here:

while (fgets(line, sizeof(line), datain) != 0)
{
    int line_offset = 0;
    int new_offset;
    while (sscanf(line + line_offset, "%2x%n", &buffer1[offset], &new_offset) == 1)
    {
        printf("%#.2x ", buffer1[offset]);
        offset++;
        line_offset += new_offset;
    }
}

The %n conversion is not counted in the return from sscanf().

Note that this avoids some other problems seen in another answer. It doesn't try processing data when there wasn't a line to read, and it doesn't try processing data when there isn't any left.

Also, one of the advantages of reading a line at a time is that error reporting is often easier/better when you can give the entire context (line) where the error occurred, rather then being stuck with what's left of the line after some indeterminate number of successful conversions. If there's an erroneous character in the sixth field on the line, you can show the previous five fields as well as where the error is quite simply.

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

by appending a %c after each hex specifier, I could extract the space and newline character from the stream. By testing on the this character, I could know a new line has reached.

do{
    counter=fscanf(datain,"%2x%c",buffer1+offset,&followsymbol);
    fprintf(stdout,"Counter:%i\n",counter);
    if (counter==2)
         {fprintf(stdout,"data:%5x\tfollowsymbol:%5x\n",buffer1[offset],followsymbol);
          offset+=1;
         }
    if(followsymbol==0x0a && counter==2)
          printf("a nl symbol has been detected\n");
}while(!feof(datain));

The output from the terminal

Counter:2
data:ffffffe4   followsymbol:   20
Counter:2
data:ffffffe2   followsymbol:   20
Counter:2
data:ffffffe2   followsymbol:   20
Counter:2
data:ffffffe1   followsymbol:   20
Counter:2
data:ffffffff   followsymbol:    a
a nl symbol has been detected
Counter:2
data:fffffff2   followsymbol:   20
Counter:2
data:ffffffa2   followsymbol:   20
Counter:2
data:   22      followsymbol:   20
Counter:2
data:   34      followsymbol:    a
a nl symbol has been detected
Counter:2
data:ffffffff   followsymbol:   20
Counter:2
data:ffffffee   followsymbol:   20
Counter:2
data:ffffffdd   followsymbol:    a
a nl symbol has been detected
Counter:65535
HarryQ
  • 1,281
  • 2
  • 10
  • 25
  • Why are you hardcoding the 0x0a, might be better to use the `\n` instead? – t0mm13b Feb 26 '14 at 18:09
  • Need `if(counter==2 && followsymbol==0x0a)`. Otherwise `followsymbol` could be the previous value should `counter` have the value EOF or 1. – chux - Reinstate Monica Feb 26 '14 at 18:12
  • `fscanf(...,"%2x%c",...,followsymbol);` should be `fscanf...,"%2x%c",...,&followsymbol);` (missing &) – chux - Reinstate Monica Feb 26 '14 at 18:23
  • @chux Sorry for the ambiguity, changed as suggested. – HarryQ Feb 26 '14 at 18:27
  • 2
    Be very leery of code that prints the 'newly arrived data' when in fact the conversion failed. Be equally very leery of code that uses `feof()` to detect EOF; that is almost invariably wrong. Note, in particular, that when the last number in the file is successfully read, the `feof()` won't say EOF. There's a whole question on [`while (!feof(file))` is always wrong](http://stackoverflow.com/questions/5431941/while-feof-file-is-always-wrong) that you should read. Using `do { … } while (!feof(file));` is not an improvement. – Jonathan Leffler Feb 26 '14 at 18:33
  • 1
    The output does not match the code/input file. Either 1) there is no `\n` in the last line of the file or 2) The posted output is does not show what comes after the last "a new line has been detected". The `feof(datain)` does not become true until _after_ `counter=fscanf()` returns something other than 2. Try printing `counter` after each `fscnaf()` to see. – chux - Reinstate Monica Feb 26 '14 at 18:34
  • Minor: `counter` should be type `int` and printed with `"%d"`. – chux - Reinstate Monica Feb 26 '14 at 19:16
  • @chux I think the difference in the output was originated from the feof(datain). After the last \n, there was something on the next line, although we viewed it as empty; as a result, feof(datain) didnt return non-zero value, leading to the counter value 63335. I changed to print the result only if counter==2 (which indicates a meaningful read in). – HarryQ Feb 26 '14 at 19:17
  • 1
    `feof(datain)` does _not_ return true right after the last `char` is read. Instead it returns true _after_ an attempt was made to read _past_ the last `char`. Using the return value from `fgets()`, `fgetc()` or `fscanf()` is a better approach than using `feof()` to determine when to stop. Use `feof()` _after_ receiving a failed read to determine if the failure was EOF or IO Error. – chux - Reinstate Monica Feb 26 '14 at 19:32