0

I have a file called test.txt the file contains:

<this is a test = 1>

<more tests = 42 and more "34">

<start>10.213123 41.21231 23.15323</start>

<random stuff = "4">

<blah 234>

When I see <start> I want to scan in the 3 numbers after into a double like this:

x = 10.213123
y = 41.21231
z = 23.15323

I'm sort of confused because here, fgets scans the entire line, how can I scan in the 3 numbers into a double? Because the numbers can be of various lengths? I made this to print out what it reads from a file but I can't wrap my head around it.

void  print_lines(FILE *stream) {
    char line[MAX_LINE_LENGTH];
    while (fgets(line, MAX_LINE_LENGTH, stream) != NULL) {
        fputs(line, stdout);
    }
}
Sarah Chan
  • 41
  • 1
  • 6
  • 1
    This might be helpful: [How to read / parse input in C? The FAQ](https://stackoverflow.com/questions/35178520/how-to-read-parse-input-in-c-the-faq). Particularly the section "Read, then parse". You can fgets as in your code and then iterate through the line with strtod. – Lundin Jun 15 '18 at 11:05
  • You can create a function to check if [line starts with `""`](https://stackoverflow.com/questions/15515088/how-to-check-if-string-starts-with-certain-string-in-c) and if it is then parse this line only. – Yonlif Jun 15 '18 at 11:34
  • 1
    Is it guaranteed that `` and `` will be on the same line? What if the three numbers are on two or more lines? If you want to handle that case you'll have to use a different approach. – Steve Summit Jun 15 '18 at 12:41

1 Answers1

1

Just when you see <start> then scan 3 numbers into double. You have the line content in line variable, you can use strtod to scan string into double. You can even use sscanf(line, "<start>%lf %lf %lf</start>", &x, &y, &z);, but using strtod is better for error handling.

#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>

#define MAX_LINE_LENGTH 1024

void  print_lines(FILE *stream) {
    double a, b, c;
    char line[MAX_LINE_LENGTH];
    while (fgets(line, MAX_LINE_LENGTH, stream) != NULL) {
        char *pnt;
        // locate substring `<start>` in the line
        if ((pnt = strstr(line, "<start>") != NULL)) {
            // advance pnt to point to the characters after the `<start>`
            pnt = &pnt[sizeof("<start>") - 1];
            char *pntend;

            // scan first number
            a = strtod(pnt, &pntend);
            if (pnt == pntend) {
                fprintf(stderr, "Error converting a value.\n");
                // well, handle error some better way than ignoring.
            }
            pnt = pntend;
            // scan second number
            b = strtod(pnt, &pntend);
            if (pnt == pntend) {
                fprintf(stderr, "Error converting a value.\n");
                // well, handle error some better way than ignoring.
            }
            pnt = pntend;
            // scan third number
            c = strtod(pnt, &pntend);
            if (pnt == pntend) {
                fprintf(stderr, "Error converting a value.\n");
                // well, handle error some better way than ignoring.
            }

            printf("Read values are %lf %lf %lf\n", a, b, c);
        } else {
            // normal line
            //fputs(line, stdout);
        }
    }
}

int main()
{
    print_lines(stdin);
    return 0;
}
KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 1
    I've spent about 10 minutes studying this code (I'm still learning code). I just have 2 questions. First one is, I don't understand what "errno" is/does. Second question is, I see how you are using memcmp to find , but what if start was located like this `9.12 9 231.2` (with a bunch of spaces in front if `` or `hdf 9.12 9 231.2`. Is there a way to specifically find it? – Sarah Chan Jun 15 '18 at 12:09
  • 1
    1. [errno](http://en.cppreference.com/w/c/error/errno) is, well, errno, you can read about it on the net (why it is bad, why it is good, why it still exists). [strtod](https://linux.die.net/man/3/strtod) set's errno to ERANGE on conversion error. I wanted to show how to handle strtod conversion errors. 2. Well, you decide what format your program will support. There are many standard function which can help you. Probably [strstr(line, "")](https://linux.die.net/man/3/strstr) will be helpful. – KamilCuk Jun 15 '18 at 12:50
  • `a = strtod(pnt, &pnt); if (errno) {` is insufficient to detect if no conversion occurred. `a = strtod(pnt, &endptr); if (pnt == endptr)` does. `strtod()` is not specified by C to set `errno` on no conversion. – chux - Reinstate Monica Jun 15 '18 at 12:58
  • `strtod()` and `errno` on _underflow_ is annoyingly _implementation-defined behavior_. – chux - Reinstate Monica Jun 15 '18 at 13:00
  • @KamilCuk I read up on `strstr` function. It finds the first occurrence of the chosen string. It returns NULL if it didn't find it or a pointer if it did. However when I replace the `if (!memcmp(line, "", sizeof("")-1)) {` with `if (strstr(line, "") != NULL) {` it doesn't work. – Sarah Chan Jun 15 '18 at 22:31
  • It works if is on a a new line and has no spaces or characters in from of it – Sarah Chan Jun 15 '18 at 22:38