tags:

views:

987

answers:

8

I have to read a txt file with lines formated like this:

1: (G, 2), (F, 3)
2: (G, 2), (F, 3)
3: (F, 4), (G, 5)
4: (F, 4), (G, 5)
5: (F, 6), (c, w)
6: (p, f), (G, 7)
7: (G, 7), (G, 7)
w: (c, w), (c, w)

Each line will feed a struct with its data (the 5 numbers or letters in it).
What's the best way to read the line and get the strings I want?
I'm currently using a long sequence of conditions using fgetc but that seems ugly and not very smart.
I can't use arrays because the lines may vary in size if the numbers have two digits.

A: 

fgets() and sscanf() as I remember

kenny
+2  A: 

Use fgets():

#include <stdio.h>

int main(void)
{
  char line[256];
  while(fgets(line, sizeof(line), stdin) != NULL)  // fgets returns NULL on EOF
  {
    // process line; line is guaranteed to be null-terminated, but it might not end in a
    // newline character '\n' if the line was longer than the buffer size (in this case,
    // 256 characters)
  }

  return 0;
}
Adam Rosenfield
+6  A: 

I think you could parse it along the lines of:

fscanf(file,"%c: (%c, %c), (%c, %c)", &first,&second,&third,&fourth,&fifth);
FlySwat
I tried doing that, but got only segmentation faults, I'll try it again just in case I overlooked something
Gabe
actually, if first, second, third etc. aren't allocated pointers you need to do: fscanf(file, "%c: (%c, %c)", or you will get seg faults because you'll be dereferencing invalid addresses...
Jason Coco
Good call, fixed...I'm not a C developer by any means.
FlySwat
This doesn't handle values that are more than one character long, @Gabe indicated that the numbers may have at least 2 digits.
Robert Gamble
It also runs into trouble at the first newline!
Jonathan Leffler
A: 

fscanf works pretty nice, but you'll have to use string conversions, because chars wouldn't work for numbers with more than one digit.

Isn't this some kind of homework?

#include <stdio.h>

void main(int argc, char **argv) {
 FILE * f;
 f = fopen(argv[1], "r");

 while (1) {
  char char_or_num[32][5]; // five string arrays, up to 32 chars
  int i;
  int did_read;

  did_read = fscanf(f, "%32[0-9a-zA-Z]: (%32[0-9a-zA-Z], %32[0-9a-zA-Z]), (%32[0-9a-zA-Z], %32[0-9a-zA-Z])\n", char_or_num[0], char_or_num[1], char_or_num[2], char_or_num[3], char_or_num[4]);
  if (did_read != 5) {
   break;
  }
  printf("%s, %s, %s, %s, %s\n", char_or_num[0], char_or_num[1], char_or_num[2], char_or_num[3], char_or_num[4]);
 }

 fclose(f);
}
che
Don't use 'void main(...)'; it is non-standard.
Jonathan Leffler
A: 
A: 

Basically you'll have to save the position of the file ptr via fgetpos, walk to the end of the line (however you define it), save that size, fsetpos to the previous position, allocate a buffer big enough to hold the line, and then call fread with the new buffer.

Jeff Hubbard
You don't have to do that at all. fgets() works perfectly fine. If you don't mind all the caveats of fscanf() that works too. Both are demonstrated here.
Jason Coco
Wow - that would be hard work; way, unnecessarily hard work. Use fgets() for first choice; fscanf() if you're feeling brave and deal with newlines.
Jonathan Leffler
The problem with using fgets() is it makes you guess at the line size, and if you don't carefully and dutifully check return values to make sure you get the entire line, your single function call can turn into a multitude of calls until you get the right size.
Jeff Hubbard
A: 

Assuming you have the correct variables, this should work:

 fscanf(fp, "%[^:]: (%[^,], %[^)]), (%[^,], %[^)])", a, b, c, d, e);

fp is a file pointer and "a" to "e" are char pointers

+2  A: 
#include <stdio.h>

int main (void)
{
  char buf[81];       /* Support lines up to 80 characters */
  char parts[5][11];  /* Support up to 10 characters in each part */

  while (fgets(buf, sizeof(buf), stdin) != NULL)
  {
    if (sscanf(buf, "%10[^:]: (%10[^,], %10[^)]), (%10[^,], %10[^)])",
               parts[0], parts[1], parts[2], parts[3], parts[4]) == 5)
    {
      printf("parts: %s, %s, %s, %s, %s\n",
             parts[0], parts[1], parts[2], parts[3], parts[4]);
    }
    else
    {
      printf("Invalid input: %s", buf);
    }
  }
  return 0;
}

Sample run:

$ ./test
1: (G, 2), (F, 3)
2: (G, 2), (F, 3)
3: (F, 4), (G, 5)
4: (F, 4), (G, 5)
5: (F, 6), (c, w)
6: (p, f), (G, 7)
7: (G, 7), (G, 7)
w: (c, w), (c, w)
parts: 1, G, 2, F, 3
parts: 2, G, 2, F, 3
parts: 3, F, 4, G, 5
parts: 4, F, 4, G, 5
parts: 5, F, 6, c, w
parts: 6, p, f, G, 7
parts: 7, G, 7, G, 7
parts: w, c, w, c, w

If the last value in the input is more than 10 characters it will be truncated with no indication of error, if this is not acceptable you can use the %c conversion specifier as a sixth argument to capture the next character after the last value and make sure it is a closing parenthesis.

Robert Gamble