-1

How can be converted integers from string in C?

What is the easiest way? I need programm function like:

fraction create_fraction_from_string(char *str)

Where I will in the main file call function like:

fraction create_fraction_from_string("-12/4") 

And the result should be two integers:

int a = -12;
int b = 4;

I think, that I split the string and after that convert these two strings... I am new in C programming...

EDIT:

fractions is:

typedef struct { int a; int b; } fraction;

And the input always be in "x/y" or "-x/y"

And the result should be:

fraction res = { .a = -12, .b = 4 };
lukkkss
  • 11
  • 4
  • 2
    Do you already know how to convert "-12" to -12? How to read "/" into a `char`? If not first learn that. If yes, what is the problem you encounter with your current goal? – Yunnosch Nov 20 '22 at 20:35
  • What is `fraction`? Is it just `double`? Or, is it (e.g.) `typedef struct { int a; int b; } fraction;`? That is, from `"-12/4"`, do you want the result to be: `double res = -3.0;` or do you want: `fraction res = { .a = -12, .b = 4 };`? Is the input always of the form `"x/y"` or do you want full equation parsing on (e.g.) `"-12/4*37.6+23"`? – Craig Estey Nov 20 '22 at 21:10
  • 1
    Does this answer your question? [How to convert a string to integer in C?](https://stackoverflow.com/questions/7021725/how-to-convert-a-string-to-integer-in-c) – samnoon Nov 21 '22 at 06:04
  • 1
    @SMSamnoonAbrar This seems not to be what the user wants to know because he actually wants to convert a string to two integers. – Martin Rosenau Nov 21 '22 at 06:58
  • You may use the `sscanf` function: `sscanf(str, "%d/%d", &res.a, &res.b);` – Martin Rosenau Nov 21 '22 at 06:59
  • Standard approach: (1) read entire line with `fgets()`, (2) parse what you need from the buffer with `sscanf()` as @MartinRosenau explains. **(3)** DO NOT forget to ***check the return*** of BOTH `fgets()` and `sscanf()` and handle any error accordingly. – David C. Rankin Nov 21 '22 at 07:45

2 Answers2

1

@David wrote a very generic answer, which can be useful in certain situations. But to keep things simple, here is a more specific variant:

int create_fraction_from_string (fraction *frac, const char *str)
{
    const char *fmt = "%d/%d";

    return (sscanf (str, fmt, &frac->a, &frac->b) == 2);
}

This will return 1 for success and 0 on failure, so you need to check if conversion worked before using frac.

klutt
  • 30,332
  • 17
  • 55
  • 95
  • I like the equality as the return, but didn't that come from the advanced class ? `:)` – David C. Rankin Nov 21 '22 at 08:50
  • I don't find that particularly advanced. On the contrary, I find it pretty important that beginners learn such things fairly early because it's very common that C code is written that way. Especially since it's still quite rare to use the `bool` type, beginners need to know how a regular `int` is used for boolean values. – klutt Nov 21 '22 at 08:56
  • I was just teasing a bit. Both are equivalent, one written out, one done all at once with the equality. – David C. Rankin Nov 21 '22 at 09:21
  • @DavidC.Rankin I understood the teasing, but it was still a valid question which I thought deserved a serious answer. :) – klutt Nov 21 '22 at 09:22
0

Collecting the wisdom from the comments, your basic approach to reading any input where you need to parse values from what is entered is to:

  1. read the entire line of input into a sufficiently sized buffer using fgets(), don't skimp on buffer size. Validate the return to ensure you have valid input and the user didn't cancel input by generating a manual EOF by pressing Ctrl + d (or Ctrl + z on windows),
  2. parse the values needed from the input using sscanf(). Validate the return to determine that each conversion requested succeeded. Otherwise, handle the error.

As for your function create_fraction_from_string(), you need to change the return type so the return can indicate whether conversion(s) from the input succeeded or failed. You can use bool for a true/false indication, or simply use an int with 0/1 for the same purpose. You can pass the address of fraction and update the values at the pointer address within the function and have those values available back in the caller.

Then in your caller (main() here), validate the return from create_fraction_from_string() before you attempt to output the values. You can ensure you avoid UB (Undefined Behavior) if you attempt to output your fraction by fully initializing the struct at the time of declaration. Whether you use a named-initialized as below, or just give a value for each member, the result is the same.

Putting it altogether, you could do something similar to:

#include <stdio.h>

#define FRACFMT "%d/%d"   /* if you need a constant, #define one (or more) */
#define FRACCNV      2

typedef struct { 
  int a, b; 
} fraction;

/* fill frac from str using sscanf (str, fmt, ...), validate nconv
 * conversions. Returns 1 on success, O otherwise.
 */
int create_fraction_from_string (fraction *frac, const char *str,
                                 const char *fmt, const int nconv)
{
  /* parse fmt from string into frac->a, frac->b, validate nconv */
  if (sscanf (str, fmt, &frac->a, &frac->b) != nconv) {
    return 0;   /* return failure */
  }
  
  return 1;     /* return success */
}

int main (void) {
                                  /* BUFSIZ 4096 on Linux, 512 on Windows */
  char line[BUFSIZ];              /* buffer to hold line of input */
  fraction frac = { .a = 0 };     /* fraction to hold result */
  
  fputs ("enter fraction (\"int/int\"): ", stdout);   /* prompt */
  
  if (!fgets (line, BUFSIZ, stdin)) {   /* read into buffer, validate */
    puts ("(user canceled input)");     /* handle manual EOF */
    return 0;
  }
  
  /* call create_fraction_from_string(), validate return */
  if (!create_fraction_from_string (&frac, line, FRACFMT, FRACCNV)) {
    fputs ("error: invalid input, must be \"int / int\".\n", stderr);
    return 1;
  }  
  
  /* output result */
  printf ("\nfrac.a : %d\nfrac.b : %d\n", frac.a, frac.b);
}

(note: you obviously should check the denominator is not 0 in create_fraction_from_string() -- that is left to you)

Example Use/Output

$ ./bin/readfraction
enter fraction ("int/int"): -12/4

frac.a : -12
frac.b : 4

Look things over and let me know if you have questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • What is the point of `nconv`? Shouldn't that always be 2? – klutt Nov 21 '22 at 08:26
  • Since the format-string was defined as a macro and passed as a parameter to generalize the parsing function, e.g. in the form `create_X_from_string()`, the number of expected conversions was passed to allow validation in the function to generate either a success or failure return from the function. (Also avoids the *MagicNumbers* issue `:)` – David C. Rankin Nov 21 '22 at 08:27
  • Yeah, that was my second question. What's the point of sending in the format string? It feels like the only actual work `create_fraction_from_string` contains is the error message. – klutt Nov 21 '22 at 08:30
  • Yes, that was the thought process. We could either hard-code it, or write the function in a general way they could model other `create_X_from_string()` functions with. I just went with the general approach. – David C. Rankin Nov 21 '22 at 08:31
  • If you want a general function, then the error message should not be specific, right? – klutt Nov 21 '22 at 08:34
  • Well.... In this case it was the `create_fraction_from_string` function, but you are correct -- for two reasons (1) the error should be output from the caller in order to keep the *Interface* and *Implementation* separate, and yes (2) it could have been passed as a parameter -- but I saw dwindling benefits from a teaching standpoint in it. To handle the error in a generic way, the function could return, e.g. `-2`, `-1`, `0` for 3 different error cases -- you could output the correct error by lookup in the caller. (but when we get to this level -- we start losing the OP's attention `:)` – David C. Rankin Nov 21 '22 at 08:37
  • Yes, I understand that it was some pedagogic reason behind. However, I think it just made things weird and hard to understand. Sorry if I sound harsh. No offense is intended. If the function has that hardcoded error message, it's just plain wrong. And if you would also pass the error message as another parameter, the function is essentially just a pretty useless wrapper around `sscanf` that adds virtually no value at all. – klutt Nov 21 '22 at 08:41
  • If you want to keep the code as it is, I can add another answer with a simpler approach. – klutt Nov 21 '22 at 08:42
  • Well, since there is only 1 possible error (*matching-failure*), we can move that back to the caller in this case. (where I would prefer it be anyway) Glad to have your thoughts and discussions -- I've always believed 4-eyes are always better than two and it results in better examples or code. – David C. Rankin Nov 21 '22 at 08:43