简体   繁体   中英

how to extract numbers from char array and add them up

i want to get input from the user to get the name, then check the input against a char array filled by an external text file, then store the number associated with the name to a variable and be able to add up the number with any more that is associated with the same name

code:

int cnt_space(int i, int count, char input[], char strIndex)
{
strIndex = input[0];

while (strIndex != '\0'){
    strIndex = input[i];
    if (isspace(strIndex))
        count++;
    i++;
}
return (count);
}

/*bool divisable5()
{

}*/

char * dispChange(char name[])
{
enum { MAXL = 40, MAXC = 50 };

char (*lines)[MAXC] = NULL; /* pointer to array of type char [MAXC] */
int i, n = 0, index;
FILE *fp = fopen("coins.txt", "r");

if (!fp) {  /* valdiate file open for reading */
    printf ("failed to open file");
}

if (!(lines = malloc (MAXL * sizeof *lines))) { /* allocate MAXL arrays */
    fprintf (stderr, "error: virtual memory exhausted 'lines'.\n");
}

while (n < MAXL && fgets (lines[n], MAXC, fp)) { /* read each line */
    char *p = lines[n];                  /* assign pointer */
    for (; *p && *p != '\n'; p++) {}     /* find 1st '\n'  */
    *p = 0, n++;                         /* nul-termiante  */
}
if (fp != stdin) fclose (fp);   /* close file if not stdin */

int *change = 0, finalChange = 0;
char changeStr[2];
changeStr[0] = 0;
/* print lines */
for (i = 0; i < n; i++){
    if (strstr(lines[i], name) != NULL){
        for (index = 0; index < strlen(lines[i]); index++){
            if(isdigit(lines[i][index])){
            printf("test %s %d", lines[i], lines[i][index]);
                if(changeStr[0] == 0){
                        changeStr[1] = "1";
    printf("%s", changeStr);
                    strcpy(changeStr, lines[i][index]);
                }
                else{
                    strcat(changeStr, lines[i][index]);
                }
            }
        }
        sscanf(changeStr, "%d", change);
        finalChange += change;
        main();
    }
    else if (i == n-1 && strstr(lines[i], name) == NULL){
        printf("Name not found, please try again\n\n");
        dispChange(name);
    }
}

printf ("Customer:\n%s%dcents\n\n", name, finalChange);
free (lines);   /* free allocated memory */
}

char * getName()
{
char ch;
int i = 0, count = 0;
char name[100];
printf("Enter a name(spaces not accepted): ");
fgets(name, sizeof name, stdin);
count = cnt_space(i, count, name, ch);
if (count > 1){
    printf("Error: Spaces not accepted!\n\n");
    free(name);
    getName();
}
else{
    dispChange(name);
}
}

int main()
{
int choice;
char option[100];
printf("1. Enter Name\n2. Exit\n------------------\nEnter option: ");
fgets(option, sizeof option, stdin);
if (option[1] != '\n'){
    printf("Invalid entry!\n\n");
    main();
}
else{
    choice = (int)option[0];
    if (isdigit(choice)){
        if (choice == 49){
            getName();
        }
        else if (choice == 50){
            printf("Program has ended\n");
            exit(0);
        }
        else{
            printf("Invalid option!\n\n");
            main();
        }
    }
    else{
        printf("Invalid entry!\n\n");
        main();
    }
}
return 0;
}

somehow doing lines[i][index] does not get the number? it should get number 3 but its getting number 51

text file contains:

Justin 60
Jane 30
Jared 90
MinZhan 95
Andreas 80
GuoCong 95
David 45
Ngiap 35
Teng 10
Teow 95

To answer your question, it IS getting 3 as an ASCII character, which has a decimal value of 51. You are printing it with %d, which is the decimal formatter.

There are, however, a lot of things going on in this program.

The first is cnt_space:

int cnt_space(int i, int count, char input[], char strIndex)
{
  strIndex = input[0];

  while (strIndex != '\0'){
    strIndex = input[i];
    if (isspace(strIndex))
      count++;
    i++;
  }
  return (count);
}

You are always passing 0 for i, the start of the string, so you do not need to pass i at all. You are also always passing 0 for count, and thus do not need to pass count at all. You are also passing ch, which is never read but only written to, so should be declared only in cnt_space. But you do not even need an actual count, you just need to know if the name contains a space character at all, which you can determine with a function like strpbrk. I would remove your cnt_space function entirely and replace it with:

// these values came from the man page for isspace
if (strpbrk(name, " \f\n\r\t\v")) {
  printf("Error: Spaces not accepted!\n\n");
  continue;
}

You'll notice I put continue in the code, which doesn't quite fit with what you've got because you recursively call main. That's typically frowned upon, and I changed it to continue in anticipation of replacing this with some kind of loop.

Going to skip over dispChange for a minute and look at your getName function:

char * getName()
{
  char ch;
  int i = 0, count = 0;
  char name[100];
  printf("Enter a name(spaces not accepted): ");
  fgets(name, sizeof name, stdin);
  count = cnt_space(i, count, name, ch);
  if (count > 1){
    printf("Error: Spaces not accepted!\n\n");
    free(name);
    getName();
  }
  else{
    dispChange(name);
  }
}

Good practice would be to make this function static, as it is only called within this file. You also specify that it returns a character pointer, but it does not actually return anything. If you want to keep this as the caller to dispChange, I would recommend changing the return type to void since there is no return, and changing the function name to reflect this. The recursive call should be replaced with a loop in this case to prevent a literal stackoverflow. Also, you free name but free is to return memory to the heap (from malloc or some other memory allocation). You are calling it on the stack, which I would imagine crashes. I would rewrite it to look more like this:

static void getNameAndDisplayChange()
{
  char name[100];
  do {
    printf("Enter a name(spaces not accepted): ");
    fgets(name, sizeof(name), stdin);

    // these values came from the man page for isspace
    if (strpbrk(name, " \f\n\r\t\v")) {
      printf("Error: Spaces not accepted!\n\n");
      continue;
    }

    dispChange(name);
  } while (0);
}

Now let's take a look at main, before we get into the weeds of dispChange. You had:

int main()
{
  int choice;
  char option[100];
  printf("1. Enter Name\n2. Exit\n------------------\nEnter option: ");
  fgets(option, sizeof option, stdin);
  if (option[1] != '\n'){
    printf("Invalid entry!\n\n");
    main();
  }
  else{
    choice = (int)option[0];
    if (isdigit(choice)){
      if (choice == 49){
        getName();
      }
      else if (choice == 50){
        printf("Program has ended\n");
        exit(0);
      }
      else{
        printf("Invalid option!\n\n");
        main();
      }
    }
    else{
      printf("Invalid entry!\n\n");
      main();
    }
  }
  return 0;
}

The C standard allows void arguments to main, though you may make your program more flexible by taking argc and *argv[] to accept a name on input. There are better ways to validate the input here, but we can clean it up a lot just by changing the recursion. I would also recommend using defines such as EXIT_SUCCESS and EXIT_FAILURE instead of return 0. Instead of converting the input to an integer and using the ASCII values to determine the action, you can use strcmp to keep it more readable. I would also recommend creating a "readline" function that strips the newline from user input to make it even more readable, but you can do that later. The refactored main would look something like:

int main()
{
  char option[100] = "\0";
  while (strcmp(option, "2") != 0) {
    printf("1. Enter Name\n2. Exit\n------------------\nEnter option: ");
    fgets(option, sizeof(option), stdin);
    if (strcmp(option, "1\n") != 0 && strcmp(option, "2\n") != 0) {
      printf("Invalid entry: %s!\n\n", option);
      continue;
    }

    if (strcmp(option, "2\n") == 0) {
      printf("Program has ended\n");
      break;
    }

    getNameAndDisplayChange();
  }

  return EXIT_SUCCESS;
}

For dispChange, typically with a function this big I would recommend breaking it into smaller functions, but I think we can shrink it quite a bit:

char * dispChange(char name[])
{
  enum { MAXL = 40, MAXC = 50 };

  char (*lines)[MAXC] = NULL; /* pointer to array of type char [MAXC] */
  int i, n = 0, index;
  FILE *fp = fopen("coins.txt", "r");

  if (!fp) {  /* valdiate file open for reading */
    printf ("failed to open file");
  }

  if (!(lines = malloc (MAXL * sizeof *lines))) { /* allocate MAXL arrays */
    fprintf (stderr, "error: virtual memory exhausted 'lines'.\n");
  }

  while (n < MAXL && fgets (lines[n], MAXC, fp)) { /* read each line */
    char *p = lines[n];                  /* assign pointer */
    for (; *p && *p != '\n'; p++) {}     /* find 1st '\n'  */
    *p = 0, n++;                         /* nul-termiante  */
  }
  if (fp != stdin) fclose (fp);   /* close file if not stdin */

  int *change = 0, finalChange = 0;
  char changeStr[2];
  changeStr[0] = 0;
  /* print lines */
  for (i = 0; i < n; i++){
    if (strstr(lines[i], name) != NULL){
      for (index = 0; index < strlen(lines[i]); index++){
        if(isdigit(lines[i][index])){
          printf("test %s %d", lines[i], lines[i][index]);
          if(changeStr[0] == 0){
            changeStr[1] = "1";
            printf("%s", changeStr);
            strcpy(changeStr, lines[i][index]);
          }
          else{
            strcat(changeStr, lines[i][index]);
          }
        }
      }
      sscanf(changeStr, "%d", change);
      finalChange += change;
      main();
    }
    else if (i == n-1 && strstr(lines[i], name) == NULL){
      printf("Name not found, please try again\n\n");
      dispChange(name);
    }
  }

  printf ("Customer:\n%s%dcents\n\n", name, finalChange);
  free (lines);   /* free allocated memory */
}

Again, I would make it static as it is confined to this file. It also does not return anything, so I would change the return type to void. Since we aren't changing name, I would let the caller know that by defining it as const as well.

There are some flaws in this function, you read from fp even if it is determined the file could not be opened. You compare fp to stdin, which I don't see a way for that to happen right now (maybe a feature enhancement?). If you have to read from stdin, it makes sense that you would want to load it all into memory, but you'll definitely want to do that once and outside of this function. strstr is used in such a way that it may match unintended names, such as "Charley" will match "O'Charley". To fix that, you will want to make sure that the name matches the start of the line, and that it ends in a space. strlen is computed inside of a loop, which is just wasted CPU cycles, since the length won't change.

You can reduce a lot of complexity and increase scalability by operating on the file one line at a time, instead of loading it all into memory. Again, if you have to support stdin, you will not want to make this change, but rather have a separate function that loads a FILE * into an array, something like loadData(FILE *fp, lines). This refactor is based on how your sample works, iterating through the file for every name.

static void dispChange(const char name[])
{
  enum { MAXC = 50 };

  char line[MAXC];
  int nameLen = strlen(name);
  FILE *fp = fopen("coins.txt", "r");

  if (!fp) {  /* valdiate file open for reading */
    printf ("failed to open file\n");
    return;
  }

  int finalChange = 0;
  while (fgets(line, MAXC, fp)) { /* read each line */
    if (strstr(line, name) == line && line[nameLen] == ' ') {
      int change;

      sscanf(&line[nameLen + 1], "%d", &change);
      finalChange += change;
    }
  }

  fclose(fp);

  printf ("Customer:\n%s %d cents\n\n", name, finalChange);
}

I hope this is helpful, and not just me doing the assignment for you.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM