1

I was wondering how to use an edit conversion code in a scanf function to stop scanning a string at a specific word (as the title says). I had an idea of how this would most likey work but it's not working how I intended it to.

#include <stdio.h>  

int main(void) 
{ 
    char name[50]; 
    printf("Say your message: ");
    scanf("%[^ over]", name); 
    printf("Received message: %s\n", name);

    return 0;
}

Which gives me to following output with the input of "I'm almost there over"

/* 
Say your message: I'm almost there over
Received message: I'm
*/

I understand that what it's doing is checking where the first time any of the chars ' '(space char),'o','v','e' or 'r' come first and stop scanning once it hits any of these characters. This is why it stops at the end at I'm, it runs into the space char and stops scanning.

I don't know how to properly write this in a way where the output is "I'm almost there". I know there's definitely a not hard way to do this with for loops but I was wondering how you would do only using this method. Does anyone know how to do this?

2 Answers2

1

use an edit conversion code in a scanf function to stop scanning a string at a specific word

Although getting input with fgets() is usually preferred, how about a hacky/sneaky use of "%n" and taking advantage the "over" does not repeat letters?

char name[50] = {0};
int i = 0;
while (i<(50-1)) {
  int n = 0;
  if (scanf("o%nv%ne%nr%n", &n, &n, &n, &n) == EOF) break;
  if (n == 4) break;
  if (n > 0) {
    i += snprintf(name + i, 50 - i, "%.*s", n, "over");
  } else {
    // read one character - it is not an 'o'
    if (scanf("%1s", name + i) == EOF) break;
    i++;
  }
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
0

You can't go this with scanf alone. You need to find the terminating string and then do some pointer arithmetic.

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

char *strrev(char *str) {
    if (!str || ! *str) return str;

    int i = strlen(str) - 1, j = 0;
    char ch;
    while (i > j) {
        ch = str[i];
        str[i] = str[j];
        str[j] = ch;
        i--;
        j++;
    }
    return str;
}

int main(void) 
{
    while(1) {
        char name[50];
        printf("Say your message: ");
        scanf("%49[^\n]", name);
        char c; while((c = getchar()) != '\n' && c != EOF);
        // flush input buffer (didn't consume \n)

        name[49] = '\0';
        char *e = NULL;

        printf("(1) Received message: `%s`\n", name);

        char delim[] = " over";
        strrev(name);
        strrev(delim);
        e = strstr(name, delim);
        int len = (e) ? strlen(name) - (int)(e-name) - strlen(delim) : strlen(name);
        strrev(name);
        printf("(2) Received message: `%.*s`\n", len, name);

        strrev(delim);
        e = strstr(name, delim);
        if(e) *e = '\0';
        printf("(3) Received message: `%s`\n", name);
    }

    return 0;
}
Say your message: I'm coming over over.
(1) Received message: `I'm coming over over.`
(2) Received message: `I'm coming over`
(3) Received message: `I'm coming`
Say your message: I'm coming over?
(1) Received message: `I'm coming over?`
(2) Received message: `I'm coming`
(3) Received message: `I'm coming`
Say your message: I'm coming over? over
(1) Received message: `I'm coming over? over`
(2) Received message: `I'm coming over?`
(3) Received message: `I'm coming`

(1) is the entered string

%49[^\n] is used since %s always ignored whitespace, which we want to keep. It doesn't nul-terminate the string, so we reserve an element to do that.

(2) we search backwards to get the last instance of over. (strrev from here since strrstr is non-standard.) The ternary operator is used to prevent issues when the string doesn't actually contain delim. %.*s only prints the first len characters of the string.

(3) is the entered string with the first instance of over replaced with '\0', the nul terminator.

This is one of the simplest ways to achieve the task.

LegendofPedro
  • 1,393
  • 2
  • 11
  • 23