0

I have the following input file:

3(3,3)
1(5,4)
2(7,7)
V
H

And I have the following code to print everything in it:

      char const * const fileName = argv[1]; /* should check that argc > 1 */
      FILE *file = fopen(fileName, "r"); /* should check the result */
      char line[256];

      while (fgets(line, sizeof(line), file)) {
         printf("%s", line);
      }

      fclose(file);

But I want to only retrieve the char or the int from each line and not the brackets. How can I write something to do that?

chqrlie
  • 131,814
  • 10
  • 121
  • 189
fresh42juice
  • 117
  • 6
  • You will want to look at `sscanf` to parse the values from each line. You can always read the first character with `" %c"` (note the `' '` space before the `"%c"`) and you can then use `isdigit()` to determine if it is a digit and just subtract `'0'` from the ASCII value of the digit to get the decimal value. For the rest you can use `"(%d,%d"` and since you are ***checking the return*** for `sscanf`, if it is `1`, you have a single character, if it is `3` you have all 3 values. – David C. Rankin Mar 08 '20 at 14:50
  • What do you mean by "only retrieve"? You need to read the line, and then examine it. Discard the part you don't want. This could be as simple as `if( (c = strchr(line, '(')) != NULL) *c = '\0'`; – William Pursell Mar 08 '20 at 14:52
  • The simple way to do it is after `fgets()` then `int c = *line;` (to get the 1st character of `line`. `line[0]` is the same as `*(line + 0)` is the same as simply `*line`). You can then test if `c` is a digit with `isdigit(c)` or simply `if ('0' <= c && c <= '9')`. – David C. Rankin Mar 08 '20 at 14:56
  • fresh42juice, When the line is like `2(7,7)`, might the `2` instead have a multi-digit value like `42`? – chux - Reinstate Monica Mar 08 '20 at 17:44
  • Keep your loop condition just like it is. Change only the body of the loop --- `while (fgets(line, sizeof(line), file)) { printf("%c", *line); }` – pmg Mar 08 '20 at 18:10

7 Answers7

1

Just The 1st Character

If you really just want the first character, then *line will provide you with the first character from each line. It is equivalent to line[0] and you can use either interchangeably. A short example would be:

#include <stdio.h>

#define MAXC 1024   /* if you need a constant, #define one (or more) */

int main (int argc, char **argv) {

    char buf[MAXC];
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fgets (buf, MAXC, fp))
        printf ("'%c'\n", *buf);

    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);

Example Use/Output

$ ./bin/fgets_sscanf_first dat/multivals.txt
'3'
'1'
'2'
'V'
'H'

You can save and then test whether the first character is a digit with isdigit(*line) (or as above isdigit(*buf)). With a single digit, you can subtract '0' to get the numeric value. See ASCII Table

Separating All Values

You can use sscanf to separate one or all values in the line by attempting to parse all 3-values from the line and then checking the return to know whether you had a line with parenthesis (return of 3) or a single character (return of 1). You can use a format string of " %c(%d,%d" (the space before "%c" ensures leading whitespace is ignored). You can either use if/else or a simple switch statement to handle the different cases, e.g.

#include <stdio.h>

#define MAXC 1024   /* if you need a constant, #define one (or more) */

int main (int argc, char **argv) {

    char buf[MAXC];
    /* use filename provided as 1st argument (stdin by default) */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {  /* validate file open for reading */
        perror ("file open failed");
        return 1;
    }

    while (fgets (buf, MAXC, fp)) {
        char c;
        int v1, v2;
        switch (sscanf (buf, " %c(%d,%d", &c, &v1, &v2)) {
            case 1 : printf ("single char/digit: '%c'\n", c);
                    break;
            case 3 : printf ("all values: %c  %d  %d\n", c, v1, v2);
                    break;
            default : fputs ("invalid line format\n", stderr);
        }
    }
    if (fp != stdin)   /* close file if not stdin */
        fclose (fp);
}

Example Use/Output

$ ./bin/fgets_sscanf_multival dat/multivals.txt
all values: 3  3  3
all values: 1  5  4
all values: 2  7  7
single char/digit: 'V'
single char/digit: 'H'

There are many, many ways to do this, and this is just one. Look over all your answers and let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
1

You just want to check on every character and number and accept only the chars and number:

const char *fileName = "C:\\file.txt";
FILE *file = fopen(fileName, "r");
int c = 0;
while ((c = fgetc(file)) != EOF) {
    if (isdigit(c) || isalpha(c) || c == '\n')
        printf("%c", c);
}
fclose(file);

The result:

Lion King
  • 32,851
  • 25
  • 81
  • 143
  • What C compiler do you use to do this? – S.S. Anne Mar 08 '20 at 15:33
  • 1
    `while (!feof(file)) {` is always wrong: https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong – chqrlie Mar 08 '20 at 15:38
  • 1
    @S.S.Anne: I have tested on Microsoft compiler and GCC. – Lion King Mar 08 '20 at 16:04
  • @chqrlie: maybe in some cases but not always. – Lion King Mar 08 '20 at 16:06
  • 1
    As far as beginners are concerned, always wrong. And in your case, you would be safe because `isdigit(EOF)` and `isalpha(EOF)` are always false, but there is another stupid mistake that may produce incorrect output: `c` has type `char` instead of `int`, so `EOF` is indistinguishable from 255 on platforms with signed char by default, and `isalpha(255)` might be true depending on your current locale. – chqrlie Mar 08 '20 at 16:08
  • Much better! how about replacing `printf("%c", c);` with `putchar(c);`? – chqrlie Mar 08 '20 at 18:56
  • @chqrlie Possible [idea](https://stackoverflow.com/questions/60588632/how-to-read-file-line-by-line-and-get-specific-chars-or-ints/60589042?noredirect=1#comment107194526_60588982), yet good compilers can analyze `printf("%c", c);` and `putchar(c);`, see they are the same function here (with return value not used) and emit optimal code regardless which was it is coded. I am surprised how good many compilers handle `printf()` these days when the format is simple. Of course for a weak compiler, a very good idea. – chux - Reinstate Monica Mar 08 '20 at 22:16
  • @chux-ReinstateMonica: good compilers indeed do this in special cases such as `printf("Hello world\n")` -> `puts("Hello world")`, possibly to reduce the executable size of the classic K&R sample program. I have not seen one that would convert simple `printf()` calls with argument conversions. – chqrlie Mar 08 '20 at 22:20
1

I want to only retrieve the char or the int from each line and not the brackets. How can I write something to do that?

After reading the line of input into the string at line[], try parsing it various ways. Use "%n" to record the offset of scanning and detect if scan completed.

This "%n" technique is useful to know that 1(5,4) included a trailing ) and ended there.

int index, x, y;
int n = 0;
sscanf(line, "%d (%d ,%d ) %n", &index, &x, &y, &n);
// Did scanning make it to the end and was there no extra junk at the end?
if (n > 0 && line[n] ==  '\0') {
  // Entire line scanned into index,x,y
  printf("index:%d, x:%d, y:%d\n", x, y, n);
} else
  char ch; 
  n = 0;
  sscanf(line, " %c %n", &ch, &n);
  if (n > 0 && line[n] ==  '\0') {
    // Entire line scanned into ch
    printf("ch:%c\n", ch);
  } else {
    printf("'%s' failed to scan into either\n", line);
  }
} 
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • you are correct, `sscanf_s` does support `%n`, no problem there. OTOH, MSVC users are plagued with a non standard version that expects `unsigned int` size arguments instead of `size_t` for the `c`, `s` and `[` conversion specifiers. – chqrlie Mar 08 '20 at 15:47
  • 1
    @chqrlie True about incompatibility, yet `sscanf_s()` is not the issue here nor pertains to `"%n"`. The "safer" `*scanf_s()`, yet incompatibility with C99, hints to me of a failed [EEE](https://en.wikipedia.org/wiki/Embrace,_extend,_and_extinguish). – chux - Reinstate Monica Mar 08 '20 at 17:35
  • Despicable but plausible `:(` – chqrlie Mar 08 '20 at 18:55
0

sscanf does formatted input, same as printf does formatted output.

const char *line = "1(5,4)";
int one;
int five;
int four;
sscanf(line, "%d(%d,%d)", &one, &five, &four);
Tarek Dakhran
  • 2,021
  • 11
  • 21
0

I think you can use fscanf(file, "%c", ch). then use fgets to get the to the next line.

T-Zocker
  • 1
  • 1
  • 1
0

A quick solution can be just ignoring the characters you don't want. As here you just don't want to include the brackets you can use this. You can also check for if the character you're iterating is a digit or character and include it in the new string.

while (fgets(line, sizeof(line), file)) {
char modified[256];
int j = 0;

for (int i = 0; line[i] != '\0'; i++) 
    if (line[i] != '(' && line[i] != ')')
        modified[j++] = line[i];
modified[j] = '\0';
printf("%s", modified);
}
Afif Al Mamun
  • 199
  • 1
  • 11
0

If you want to discard everything in the line after and including the first (, then just do:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

FILE * xfopen(const char *path, const char *mode);

int
main(int argc, char **argv)
{
        FILE* file = argc > 1 ? xfopen(argv[1], "r") : stdin;
        char line[256];
        while( fgets(line, sizeof line, file) != NULL ) {
                char *c = strchr(line, '(');
                if( c != NULL )
                        *c = '\0';
                printf("%s%s", line, c ? "\n" : "");
        }
        fclose(file);
        return 0;
}


FILE *
xfopen(const char *path, const char *mode)
{
        FILE *fp = strcmp(path, "-") ? fopen(path, mode) : stdin;
        if( fp == NULL ) {
                perror(path);
                exit(EXIT_FAILURE);
        }
        return fp;
}

Using scanf is overly complicated, and there's no need to be copying strings around.

William Pursell
  • 204,365
  • 48
  • 270
  • 300