1
int main() {

char userInput[100];    //Store user input

//Take user input
//scanf(" %s",&userInput);
//scanf("%[^\n]s",&userInput);
//scanf("%[^\n]", &userInput);
//gets(userInput);
scanf("%[]s", &userInput); //This takes input but doesnt leave input loop

printf(" %s",userInput);

//i = index to start for looping through the string, starting at the beginning
//count = Stores occurrences of '$'
//inputLength = length of the input, used for limit of loop
int i =0,count =0;
int inputLength = strlen(userInput);

//Loop through the user input, if the character is '$', the integer count will be incremented
for (i; i < inputLength; i++){
    if (userInput[i] == '$'){
        count++;
    }
}

printf("%d", count);
return 0;
}

Hi i'm having some issues with my code, i need to take an input of 3 lines and count the number of'$' in the input. The input method not commented "scanf("%[]s", &userInput);" is the one only i have discovered to take all 3 lines of input, BUT i can't break the input loop and continue with my program.

Any help would be greatly appreciateed

7
  • What input loop? Commented Aug 2, 2018 at 19:18
  • Do not use scanf. Commented Aug 2, 2018 at 19:27
  • 2
    You're better off just using fgets to read one line at a time, and keep a running total of the number of $ Commented Aug 2, 2018 at 19:27
  • 5
    If your goal is to determine the number of $ in the first 3 lines, read the data one character at a time. Count the $ and the \n. When you read the 3rd \n, stop. Commented Aug 2, 2018 at 19:28
  • 2
    Yes, @Tom, scanf is a pain in the ass. It is powerful, but it has a lot of pitfalls and routinely trips people up. Among the pitfalls is that it can very easily produce buffer overruns if you do not take care. You do not take care. Commented Aug 2, 2018 at 19:38

5 Answers 5

2

To read 3 lines with the cumbersome scanf(), code needs to look for '$', '\n', and EOF. The rest of input is discardable.

int count = 0;
int line = 0;
while (line < 3) {
  scanf("%*[^$\n]");  // Scan for any amount of characters that are not $ nor \n,
                      // "*" implies - do not save. 
  char ch;
  if (scanf("%c", &ch) != 1) {  // Read next character.
    break;
  }
  if (ch == '$') count++;
  else line++;
}

printf("$ count %d\n", count);
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for your reply, how come you can just have "scanf("%*[^$\n]");" without storing it somewhere such as input[100]?
@tom Per the question, there was no requirement to save the line, just "take an input of 3 lines and count the number of'$" - nothing about saving the lines. Also as there was no stated upper bound to the maximum length of the line, coding to some artificial upper bound might not meet the coding goals anywasy. IAC, a max line length was not needed to fulfill the stated goals.
the first if statement with the break in it, does this mean if the character is whitespace, read the next line?
@Tom research scanf(). scanf("%c", &ch) returns 1 to indicate 1 field successfully scanned ( a character was read and it must be a '&' or '\n' due to the previous scanf("%*[^$\n]");). Else EOF is returned (no more data or rare input error) in which case, code here simply quits attempting to read and then directly proceeds to print the results.
2

As @chux suggested, reading with fgets provides a convenient way to protect from buffer overrun and without having to hard code field-width modifiers in scanf conversion specifiers.

Here, if all you need to do is count the number of '$' characters found in your input (regardless of how many lines), you can simply read ALL the input in fixed sized chunks of data. fgets does just that. It doesn't matter if you have one line, or one million lines of input. It also doesn't matter if your input lines are one-character or one million characters long. You can simply read each line and count the number of '$' found within each chunks of data read, keeping a count of the total found.

You can do this for any character. If you wanted to also count the number of line, you can simply check for '\n' characters and keep a total there as well. The only corner-case in counting lines with fgets is to insure you protect against a non-POSIX end-of-file (meaning a file with no '\n' as the final character). There are a couple of ways to handle this. Checking that the last character read was a '\n' is as good as any.

Putting the pieces together, and protecting against a non-POSIX eof, you could do something similar to the following, which simply reads all data available on stdin and outputs a final '$' and line count:

#include <stdio.h>

#define MAXC 100

int main (void) {

    char buf[MAXC] = "";  /* buffer to hold input in up to MAXC size chunks */
    size_t lines = 0, dollars = 0;  /* counters for lines and dollar chars */
    int i = 0;

    while (fgets (buf, MAXC, stdin))    /* read all data */
        for (i = 0; buf[i]; i++)        /* check each char in buf */
            if (buf[i] == '$')          /* if '$' found */
                dollars++;              /* increment dollars count */
            else if (buf[i] == '\n')    /* if '\n' found */
                lines++;                /* increment line count */

    if (i && buf[i-1] != '\n')          /* protect against non-POSIX eof */
        lines++;

    /* output results */
    printf ("input contained %zu lines and %zu '$' characters.\n", 
            lines, dollars); 

    return 0;
}

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

4 Comments

@chux Neither intended, size_t could very well be unsigned long, but just chose size_t for counting type -- (it and unsigned long should be sufficient on most platforms) Will tweak the flag. (actually just reworked test to test last char read)
Good enough - UV. Note that fgets() Achilles heal is when it reads a null character. The ability to detect those is the only advantage I see with scanf(). A last lines of "\0xyz" fools your otherwise nifty line count protection.
@chux Now you got me there. When I play with fgets reading the nul-character, fgets reads an empty-string and then throws EOF on the next iteration. That does appear to be an Achilles heal of fgets. I'm not even sure it is worth trying to trap that condition. More the rule of "If embedded nul-characters can be present in your input -- don't use fgets." will have to stand...
@chux, I guess you could trap !i, e.g. if ((i && buf[i-1] != '\n') || !i) which would at least preserve the line count to the point the nul-character is encountered.
1

scanf("%[]s", &userInput);" is the one only i have discovered to take all 3 lines of input, BUT i can't break the input loop and continue with my program.

"%[]" is an invalid scanf() specifier. Anything may happen, it is undefined behavior, including taking all lines in and not returning.

The 's' in the format serves no purpose here - drop it.


Yes fgets() is best but let us abuse scanf() to read 3 lines and look for '$'.

char line[3][100] = {0};
//                 v--------- Consume all leading whitespace 
//                 | vv ----- limit input to 99 characters as scan() appends a \0
//                 | || v-v-- Look for "not \n" 
#define FMT_1LINE " %99[^\n]"

// Let the compiler concatenate the 3 formats into 1 string for scanf
int scan_count = scanf(FMT_1LINE FMT_1LINE FMT_1LINE, line[0], line[1], line[2]);
// Check return value
if (scan_count == 3) {
  // Successfully read 3 lines
  int count = 0;
  for (int line_index = 0; line_index < 3; line_index++) {
    char *s = line[line_index];
    while (*s) {  // no need for strlen(), just loop until the null character
      count += *s == '$';
      s++;
    }
  }
  printf("$ count %d\n", count);
}

4 Comments

@john " %99[^\n]" consumes the previous line's new-line as well as spaces and empty lines. Yes there is no provision here for lines > 99 characters nor lines of only "\n".
Back to my original comment, more or less: this may read more than three lines if some of them do not contain any non-whitespace characters, and it may read fewer if some are longer than 99 characters. scanf can be used for this, but it's a lot messier than that.
@JohnBollinger True. I may try a robust version later.
@chux thanks for your reply it's helped me understand this problem more.
1

You write:

scanf("%[]s", &userInput); //This takes input but doesnt leave input loop

but the comment is at best misleading. Your format string is malformed, so the behavior of the scanf call is undefined. An empty scan set (between the [] in the format) does not make sense, because the resulting field could never match anything. Therefore, a ] appearing immediately after the opening ] of the scan set is interpreted as a literal character not the ending delimiter. Your scan set is therefore unterminated.

Note, too, that %[ is its own field type, separate from %s. An 's' following the closing ] of the scan set is not part of such a field descriptor, but rather an ordinary character to match.

A trivial way to do this with scanf would be to read characters one at a time in a loop via a %c field. This is probably not what the exercise is looking for, and it's a hack to use scanf() instead of getchar() for this purpose, but perhaps it would serve:

int nl_count = 0;
int dollar_count = 0;

do {
    char c;
    int result = scanf("%c", &c);

    if (result != 1) {
        break;
    }
    switch (c) {
        case '\n':
            nl_count++;
            break;
        case '$':
            dollar_count++;
            break;
    }
} while (nl_count < 3);

I'm afraid it would be much more complicated to do it safely reading multiple characters at a time with a %[ field, and there is no safe way to read all three lines in one scanf call, unless you can rely on the input lines not to exceed a line length limit known to you.

Comments

1
int readMatrix() {

char userInput[100][3];    //Store user input
int j = 0, m = 0;

for(m = 0; m < 3; m++){
    scanf("%s", &userInput[j][m]); //This takes input (Ex: 22 *(enter)* 33$ *(enter)* 66$ *(enter)*
    j++; //increase the column
}



int i =0,count =0;
m = 0;
//Loop through the user input, if the character is '$', the integer count will be incremented
for (i = 0; i < 100; i++){
    for(m = 0; m < 3; m++){
        if (userInput[i][m] == '$'){
            count++;
        }
    }
}
printf("%d", count);
return 0;
}

3 Comments

Good use of a loop, yet scanf("%s", &userInput[j][m]) is akin to gets(&userInput[j][m]) with its lack of buffer overrun protection. Better to use scanf("%99s", ... .
if (userInput[i][m] == '$') is a problem once code reads past userInput[i][m] == '\0'. "your input will always have 100, because you declared char userInput[100][3];" is not correct. scanf("%s", ... does not necessarily fill the entire 100.
Thanks for your reply i just have a few questions if you dont mind. In the first for loop for taking user input, I see how its looping from the first line up to the 3rd, taking input each time and placing it in the array, but why is there 'j++' at the end of the loop?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.