1

I want to input an ISBN number separated by hyphens, and at first, I thought of scanf(), so I wrote

scanf("%d-%d%d%d-%d%d%d%d%d-%d", &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7], &a[8], &a[9], &a[10]);

but scanf reads the hyphen as a minus character, and when I input

0-670-82162-4

what it actually puts into the array is

0 670 -82162 -4 0 0 0 0 0 0

I expected what it reads to be like this:

0 6 7 0 8 2 1 6 2 4
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 2
    Read the whole thing as a string, erase the hyphens, and either use each character as a digit or convert the character digits to integers. – Retired Ninja Jul 15 '23 at 00:44
  • 2
    `%1d` to read a single digit. – Eljay Jul 15 '23 at 01:11
  • 4
    Might be worth noting that array indices start at 0. Seems odd to start with `&a[1]`. – Retired Ninja Jul 15 '23 at 01:20
  • 1
    by the way, if it is ISBN, don't we want to keep the format? maybe i'd read them as strings, like https://stackoverflow.com/a/10464355/5920627 – kay27 Jul 15 '23 at 01:55
  • You should treat the entire entity as a string. The individual numbers in an ISBN have no numerical meaning. – user207421 Jul 15 '23 at 01:59
  • @user207421: They totally have numerical meaning, if only to check the validity by the control digit. And different parts have semantic meanings too (ie. ```0-``` at the beginning means it's from English speaking country). – jpalecek Jul 15 '23 at 02:30
  • You could read the numbers as strings: `getline(std::cout, my_number, '-');` And then convert the strings to numbers. – Thomas Matthews Jul 15 '23 at 02:47
  • This appears to be an example of an [XY Problem](https://xyproblem.info/). Why are you trying to do this? If you want the individual digits, ignoring any dashes, there are far simpler means than using `scanf`. – Kurtis Rader Jul 15 '23 at 06:03

3 Answers3

1

Your problem is that %d reads the longest integer possible, so while parsing you end up in this situation:

input: 0-670-82162...
            ^

pattern: %d-%d%d%d
              ^

ie. it has read 670, but the next thing in the pattern is a conversion to int (and not a dash that would be skipped). But the dash can be interpreted as the minus sign of the next int, and so it is.

Looking at the ISBN format however, it looks that while the dashes aren't necessary to make sense of the number, they appear at various, albeit not random positions (so approaches like %1d-%1d%1d%1d-... aren't exactly gonna work. I wonder whether %d-%d-%d-%d would be good enough for you though - then you would have to track the number of digits of the parts to assemble the number, but you would have the semantic structure at hand.

Reading from cin and parsing character-by-character would be roughly like this:

// read from cin
istream::sentry s(cin); // to skip whitespace, if you don't need it just delete this line
int digits[10];
int *out = digits, *end = digits + sizeof(digits)/sizeof(*digits);

while(cin && out != end) {
    int c = cin.get();
    if (cin.eof()) return; // error
    if (c >= '0' && c <= '9') *++out = c - '0';
    else if(std::toupper(c) == 'X') do_something_with_X; // probably assign 10 to *++out
    else if(c == '-') ; // skip
    else {
        report_syntax_error; // some rogue character
        return;
    }
}
if(out != end) ...; // not enough characters read
jpalecek
  • 47,058
  • 7
  • 102
  • 144
0

The issue is you have not specified single-digit numbers, this is as simple as changing all the %d to %1d, as seen in the following:

scanf("%1d-%1d%1d%1d-%1d%1d%1d%1d%1d-%1d", &a[0], &a[1], &a[2], &a[3], &a[4], &a[5], &a[6], &a[7], &a[8], &a[9]);

Note that I have also changed your array reference to start at 0.

MorganS42
  • 656
  • 4
  • 21
0

When you're parsing things like this in C++, it's often quite a bit easier if you start with a little stream extractor that just matches literal characters, about like non-conversion characters in a scanf format string do. For example:

std::istream &operator>>(std::istream &is, char const *sep) {
    while (*sep && *sep == is.peek()) {
        is.ignore(1);
        ++sep;
    }
    if (*sep)
        is.setstate(std::ios::failbit);
    return is;
}

Using this is pretty straightforward, something along this line:

int main() {
    std::stringstream infile(std::string("0-670-82162-4"));

    int a, b, c, d;

    // read four numbers, separated by dashes:
    infile >> a >> "-" >> b >> "-" >> c >> "-" >> d;

    // print out the numbers we read:
    std::cout << a << '\n' << b << '\n' << c << '\n' << d << '\n';
}

That won't necessarily read an ISBN correctly, as the check digit isn't necessary a normal digit (one non-digit is possible, typically encoded as an 'X').

To deal with that you might want to (for one possibility) read into strings instead. With the code above, you'd just change int a, b, c, d; to std::string a, b, c, d;, and it'll be able to read the check digit correctly.

An ISBN is supposed to always be 4 groups of digits, separated by dashes, but the length of each group isn't specified, so trying to read a specific number of other characters between dashes is likely to lead to problems.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111