Forever stuck on input request (C)

148 views Asked by At

I'm trying to make a program that reads multiple ISBN-10 codes then calculates the 10th digit for each input, but my issue (at least for now) doesn't seem to be on the calculus itself but on the function that reads the inputs

The function should stop on two occasions: if the input reaches the maximum amount of lines (i=10) or if the current input is 0000000000 (causing a break on the repetition regardless of i)

Following this line of thought I've ended up making isbn_read, but whenever I try to run the program on a compiler the program keeps asking for inputs ignoring BOTH of the conditions I've mentioned above

void isbn_read(int isbn[10][9], int lin )
{
  int i, j, cont=0;
  for (i = 0; i < 10; i++)
  {
    for(j = 0; j < 9; j++)
    {
      scanf("%d", &isbn[i][j]);
      if (isbn[i][j] == 0)
        cont++;
    }
    lin = i;
    if (cont==9)
    {
      lin--;
      break;
    }
  }
}

[Some other functions]

int main ()
{
  int isbn[10][9], lin, d[10];
  isbn_read(isbn, lin);
  isbn_dv(isbn, lin, d);
  isbn_print(isbn, lin, d);
}

An ideal example for inputs and outputs if the programing was running properly should be something like:

Input 089237010 083063637 000000000

Output 089237010-6 083063637-4

1

There are 1 answers

2
Yun On

Various fixes and improvements have been suggested in the comments. Below is a working example of code that fits your requirements, written from scratch.

There are many design choices here:

  • Use char types or (unsigned) int types to store the ISBN. This depends on if you want to (mostly) treat the data as a string (opens up string manipulations) or as numbers (for e.g. performing arithmetic). Either way, one is easily converted to the other.
  • Read 9 characters at a time as a string (scanf("%9s", buffer_of_size_11)) or read one character at a time and convert them to a digit?
  • How much input checking do you want to do? And how would you like to handle broken input?
  • How to read 10 ISBNs and a sentinel value? Make the array hold 11 ISBNs or use a temporary array to read into first?
  • Use dynamic or automatic memory allocation?
  • Check for the sentinel value as the characters are read, or check this afterwards in a new loop? The former method may leads to more efficient and compact code, while the latter is a nicer separation of concerns.

I hope the code and comments below give you an idea of one possible approach.

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

#define ISBN_SIZE 10U
#define ISBN_SIZE_READ (ISBN_SIZE - 1U)
#define ARRAY_SIZE 10U

void isbn_read_uint(unsigned isbn[ARRAY_SIZE][ISBN_SIZE], size_t *arrayLength)
{
    *arrayLength = 0U;
    while (*arrayLength < ARRAY_SIZE)
    {
        // Read ISBN number into a temporary buffer
        unsigned tmp[ISBN_SIZE];
        for (size_t digitIdx = 0U; digitIdx < ISBN_SIZE_READ; ++digitIdx)
        {
            int ret = scanf("%1u", &tmp[digitIdx]);
            if (ret == EOF)
            {
                fprintf(stderr, "Unexpected end of input\n");
                exit(EXIT_FAILURE);
            }
        }
        
        // Check if the buffer contains a valid ISBN or the sentinel value
        bool foundSentinelValue = true;
        for (size_t idx = 0U; idx < ISBN_SIZE_READ; ++idx)
            if (tmp[idx] != 0U)
            {
                foundSentinelValue = false;
                break;
            }

        // If this was the sentinel value, stop reading
        if (foundSentinelValue)
            break;
        
        // If this was a valid ISBN number, copy it to the main array
        if (!memcpy(isbn[(*arrayLength)++], tmp, ISBN_SIZE_READ * sizeof(unsigned int)))
        {
            fprintf(stderr, "memcpy failed\n");
            exit(EXIT_FAILURE);
        }
    }
}

void isbn_read_print_uint(unsigned isbn[ARRAY_SIZE][ISBN_SIZE], size_t arrayLength)
{
    for (size_t idxArray = 0U; idxArray < arrayLength; ++idxArray)
    {
        printf("%zu: ", idxArray);
        for (size_t idxISBN = 0U; idxISBN < ISBN_SIZE_READ; ++idxISBN)
            printf("%d", isbn[idxArray][idxISBN]);
        putchar('\n');
    }
}

int main(void)
{
  unsigned int isbn[ARRAY_SIZE][ISBN_SIZE];
  size_t arrayLength;
  isbn_read_uint(isbn, &arrayLength);
  isbn_read_print_uint(isbn, arrayLength);
}

Example input/output:

echo '089237010 083063637 123456789 000000000' | ./isbn
0: 089237010
1: 083063637
2: 123456789

Edit

Here is another, simpler version, which reads and scans the ISBNs as strings:

#include <stdbool.h>
#include <stdio.h>

// 10 characters and a null-terminator
#define ISBN_SIZE 11U

// Holds 10 ISBNs and 1 sentinel string
#define ARRAY_SIZE 11U

size_t isbn_read_char(char isbn[ARRAY_SIZE][ISBN_SIZE])
{
    size_t arrayLength = 0U; // Start at length 0, and write to array index 0
    while (arrayLength < ARRAY_SIZE)
    {
        // Read ISBN number as a string
        scanf("%9s", isbn[arrayLength]); // Read 9 characters max for safety, indices [0,8]
        isbn[arrayLength][9] = 'X'; // Not read, set to 'X' to indicate "not computed yet"
        isbn[arrayLength][10] = '\0'; // Last character is the null-terminator

        // Check if the last sequence read was a valid ISBN or the sentinel value
        bool foundSentinelValue = true;
        for (size_t idx = 0U; idx < 9; ++idx)
            if (isbn[arrayLength][idx] != '0')
            {
                foundSentinelValue = false;
                break;
            }

        // If this was the sentinel value, stop reading...
        if (foundSentinelValue)
            break;

        // ... else, increment this index for the next string to be read
        ++arrayLength;
    }

    return arrayLength;
}

void isbn_read_print_char(char isbn[ARRAY_SIZE][ISBN_SIZE], size_t arrayLength)
{
    for (size_t idxArray = 0U; idxArray < arrayLength; ++idxArray)
        printf("%zu: %s\n", idxArray, isbn[idxArray]);
}

int main(void)
{
  char isbn[ARRAY_SIZE][ISBN_SIZE];
  size_t arrayLength = isbn_read_char(isbn);
  isbn_read_print_char(isbn, arrayLength);
}

A character digit such as 5 can be converted into the numerical value 5 by "subtracting the character 0 from it". This is actually subtracting two ASCII values from one-another. E.g.:

char ch = '5';
int n = ch - '0'; // n equals 5