0

My current code seems to work fine on DOS but not on Unix, and I need to make it work on both. From what I have found so far, it seems I should use strtol(). However, I cannot seem to figure out how to get strtol() to get only the first two integers.

The input is a text file that looks like this:

45x7
(1,0)
(10,2)

And I need the output to be "There are 45 rows and 7 columns."

This is my current code:

int rows=0, columns=0;
scanf("%d%*c%d%*c", &rows, &columns);
printf("There are %d rows and %d columns.", rows, columns);
return 0;

I do not want to discard the remaining text file as I will need to process that as well.

  • `not on Unix` - how does it "not work"? `my current code` - you are missing `int main() {` and `#include ` and `}`. After I fix your code, I get exactly the output you specified for the input you specified. – KamilCuk Oct 07 '19 at 09:25
  • Append `\n` to the `printf`. – Paul Ogilvie Oct 07 '19 at 09:26
  • Running your code on Linux (Mint 19) works OK. Is it possible input file has BOM, Unicode or similar ? – dash-o Oct 07 '19 at 09:50
  • Presumably the `"%d%*c%d%*c"` in your `scanf` call is supposed to read an int, skip the `x`, read another int, and skip the `\n`. If anything I'd expect this to have more problems under DOS or Windows (where lines are terminated with `\r\n`, although in text mode this wouldn't matter). In any case, you might consider other techniques. My first suggestions would be `%dx%d` to explicitly match the `x`, and a check on `scanf`'s return value to make sure it's 2. – Steve Summit Oct 07 '19 at 10:29
  • 1
    Many people (myself included) recommend avoiding `scanf`, and `strtol` is often part of the alternative -- but only part. The first step is usually to read exactly one *line* of text, using `fgets` or the like. Then, you can either (a) parse the line by calling `sscanf`, or (b1) break the line up into "fields" by calling `strtol` or the like (here with a delimiter of `'x'`) and (b2) call something like `strtol` on each field, or (c) use various other ad-hoc parsing techniques. – Steve Summit Oct 07 '19 at 10:34
  • Another possibility would be to call `strtol` on the while line to get `rows`, look at the returned end pointer to see if it points to an `'x'`, increment the pointer, and call `strtol` again to get `columns`. – Steve Summit Oct 07 '19 at 10:36
  • But your code works perfectly, as-is, for me, too, under MacOS, which is enough like Unix these days that it's a useful test. So there's something else going on for you, that we can't see, and it would be good to figure that out. – Steve Summit Oct 07 '19 at 10:43
  • @SteveSummit I did attempt to use fgets() to read in a line, then tried strtol() twice on the line. The problem I found was that it would only read the first integer twice, ignoring the second completely. How would you suggest implementing it in a way that I can set the delimiter to 'x'? – Jade Mckenzie Oct 07 '19 at 20:33
  • To everyone saying it works on their Unix, that just puzzles me even more.. I believe the output I got was "There are 4 rows and . columns." Not 100% sure thats exact and I don't have access to a unix shell at this moment; but it was definitely messed up like that. I put it down to scanf being unreliable between the two systems.. I am not sure what else to say on that other than maybe somewhere else in my code characters are being consumed ? That would be weird though, as this is the first thing in my main function that happens. – Jade Mckenzie Oct 07 '19 at 20:33

1 Answers1

0

Here's one way of using fgets and strtol. It is admittedly more verbose than the scanf solution.

int rows=0, columns=0;
char *endp;
char firstline[100];
if(fgets(firstline, sizeof(firstline), stdin) == NULL) {
    fprintf(stderr, "premature EOF\n");
    exit(1);
}
rows = strtol(firstline, &endp, 10);
if(*endp != 'x') {
    fprintf(stderr, "syntax error\n");
    exit(1);
}
columns = strtol(endp + 1, &endp, 10);
if(*endp != '\n') {
    fprintf(stderr, "syntax error\n");
    exit(1);
}
printf("There are %d rows and %d columns.\n", rows, columns);

The key is that strtol's second argument is a pointer to a pointer which is filled in with a pointer to the first character strtol didn't use. That is, when you pass the string 45x7\n to the first strtol call, it returns 45, and sets endp to point to the x.

Steve Summit
  • 45,437
  • 7
  • 70
  • 103