0

Following on from my previous question, it now appears that my code only outputs the first occurrence of cArray from cInput. Is there a way to get strstr to return all of the occurrences instead of stopping the program at the first? Much appreciated.

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

#define MAX_STR_LEN 120

int main(){
char *cArray[MAX_STR_LEN] = {"example", "this"};
char cInput[MAX_STR_LEN] = "";
char cOutput[MAX_STR_LEN] = "";

printf("Type your message:\n");
for (int y=0; y<1; y++){
    fgets(cInput, MAX_STR_LEN, stdin);
    char * ptr = cInput;
    while((ptr=strstr(ptr, *cArray)) != NULL){
        strncpy(cOutput, ptr, strlen(*cArray));
        printf("Initialised string array:\n%s\n", cOutput);
        ptr++;
        }
    }
}

Output:

Type your message:
this is an example
Initialised string array:
example
Program ended with exit code: 0

Edited code:

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

#define MAX_STR_LEN 120

int main() {

char *cArray[MAX_STR_LEN] = { "example", "this", "is", "an" };
char cInput[MAX_STR_LEN] = { 0 };
int y = 0;

printf("Type your message:\n");
fgets(cInput, MAX_STR_LEN, stdin);
cInput[strlen(cInput) - 1] = 0;     /* strip newline from input */
printf("\nInitialised string array:\n");

while (cArray[y])
{
    char * ptr = cInput;
    while ((ptr = strstr(ptr, cArray[y])) != NULL)
    {
        char *ep = strchr (ptr, ' ');
        if (ep) *ep = 0;              /* null-terminate at space */
        printf("%s\n", ptr++);
        if (ep) *ep = ' ';            /* put the space back      */
    }
    y++;
}

return 0;
}

New output:

Type your message:
this is an example

Initialised string array:
example
this
is
is
an
Program ended with exit code: 0
masteryupa
  • 101
  • 13
  • You can repeat the search starting at one character after the first character returned by the first search. Often, you could start much later than that, but for seriously psychopathic patterns (`aaaa`, for example) and equally psychopathic strings (`aaaaaaaa`, for example), there are multiple matches that will be missed if you aren't careful. – Jonathan Leffler Feb 27 '15 at 03:23
  • What's the point of this: `strncpy(cOutput, ptr, strlen(*cArray))`? You just found `ptr` based on the fact that it starts with a copy of `*cArray`, so you could replace that strncpy with `strncpy(cOutput, *cArray, strlen(*cArray));` but that demonstrates that you could just `printf(..., *cArray)` instead of the copy. – rici Feb 27 '15 at 03:51
  • I'm very new to this so excuse me if this program seems a little tedious. You make a good point though as it does appear that there are some arbitrary steps in this process. I think that by having the strings copied from ptr then that ensures that only the strings found are printed? – masteryupa Feb 27 '15 at 04:07
  • @masteryupa: As I said, there is no point making a copy because you already have the original word available to print. (It might make sense to print the offset of the match in the search string, but you wouldn't need a copy to do that either.) – rici Feb 27 '15 at 04:17

2 Answers2

0

here is the corrected program:

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

#define MAX_STR_LEN 120

int main(){
    char *cArray[MAX_STR_LEN] = { "example", "this" };
    char cInput[MAX_STR_LEN] = "";
    char cOutput[MAX_STR_LEN] = "";

    printf("Type your message:\n");
    fgets(cInput, MAX_STR_LEN, stdin);
    printf("Initialised string array:\n");
    for (int y = 0; y <= 1; y++){
        char * ptr = cInput;
        while ((ptr = strstr(ptr, cArray[y])) != NULL){
            strncpy(cOutput, ptr, strlen(cArray[y]));
            printf("[%d] - %s\n", y, cOutput);
            memset(cOutput, 0, sizeof(cOutput));
            ptr++;
        }
    }
    getchar();
    return 0;
}

Changes:

  1. Used memset to reset the cOutput array so that during comparison in the next iteration, it was empty.
  2. Used the index y to keep track of each word from the array
  3. Chagned array range from y<1 to y<=1
  • Unfortunately this is still returning the same result. There is also an issue: "incompatible pointer to integer conversion passing void to parameter of type int" with regard to "NULL". I can see what you're attempting to accomplish though so thank you. – masteryupa Feb 27 '15 at 03:42
  • @masteryupa, i have updated the code considering your comment and made some changes to make the code more expressive. Let me know if it solves the problem. – Ganesh Kamath - 'Code Frenzy' Feb 27 '15 at 04:11
  • `char *cArray[MAX_STR_LEN] = {"example", "this"};` should probably read `char *cArray[2] = `... since the code only uses 2 strings. The max length of one string has nothing to do with how many strings can be in `cArray`. – M.M Feb 27 '15 at 04:27
  • So far this seems to be the code which has produced the best results. @MattMcNabb you are correct and I have made the change you recommended. Only problem left it seems is that with a third string the last output is printed twice `Initialised string array: [0] - example [1] - this [2] - is [2] - is` – masteryupa Feb 27 '15 at 04:43
0

In addition to the other answers, here is a variation that eliminates intermediate variables and rearranges the loop logic slightly. There are many ways to approach the issue of returning all occurrences of strstr matches, so you have flexibility to use any manner that fits your needs:

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

#define MAX_STR_LEN 120

int main() {

    char *cArray[MAX_STR_LEN] = { "example", "this" };
    char cInput[MAX_STR_LEN] = { 0 };
    int y = 0;

    printf("Type your message:\n");
    fgets(cInput, MAX_STR_LEN, stdin);
    cInput[strlen(cInput) - 1] = 0;     /* strip newline from input */

    while (cArray[y]) 
    {
        char * ptr = cInput;
        while ((ptr = strstr(ptr, cArray[y])) != NULL)
            printf("\nInitialised string array:\n%s\n", ptr++);
        y++;
    }

    return 0;
}

output:

alchemy:~/dev/src-c/tmp> ./bin/strstrmulti
Type your message:
my example of this example for fun

Initialised string array:
example of this example for fun

Initialised string array:
example for fun

Initialised string array:
this example for fun

To isolate only the word matching in cArray, you can use strchr to test if a space remains in the input string following the pointer returned the strstr. If so, just null-terminate the string where the next space appears which will isolate just the searched for word. If I misunderstood the comment, just leave another, I'm happy to help. (code changes only)

    while (cArray[y]) 
    {
        char * ptr = cInput;
        while ((ptr = strstr(ptr, cArray[y])) != NULL)
        {
            char *ep = strchr (ptr, ' ');
            if (ep) *ep = 0;              /* null-terminate at space */
            printf("\nInitialised string array:\n%s\n", ptr++);
            if (ep) *ep = ' ';            /* put the space back      */
        }
        y++;
    }

isolated output:

alchemy:~/dev/src-c/tmp> ./bin/strstrmulti
Type your message:
my example of this example for fun

Initialised string array:
example

Initialised string array:
example

Initialised string array:
this

Here is a bit of code to look at whole words only using the inch-worm method. I haven't had a lot of time to look at all potential gotchas, but this should get you going. Just replace the code in your last version:

    while (cArray[y])
    {
        char *ptr = cInput;
        char *ep = NULL;

        while ((ep = strchr (ptr, ' ')))  /* set end-pointer to each space in input */
        {
            *ep = 0;                      /* null-terminate and compare word        */
            if (strcmp (ptr, cArray[y]) == 0)
                printf("\nInitialised string array:\n%s\n", ptr);
            *ep = ' ';                    /* restore space in string                */
            ptr = ++ep;                   /* set pointer to next char after space   */
        }
        if (strcmp (ptr, cArray[y]) == 0) /* compare last word in input             */
            printf("\nInitialised string array:\n%s\n", ptr);
        y++;
    }

output w/updated cArray

alchemy:~/dev/src-c/tmp> ./bin/strstrmulti
Type your message:
this is an example to test the example for fun

Initialised string array:
example

Initialised string array:
example

Initialised string array:
this

Initialised string array:
is

Initialised string array:
an
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • The logic here seems much more fluid however the output is incorrect. Would it be possible to include a way for the output to include only the specific words in the array provided and nothing more? – masteryupa Feb 27 '15 at 04:37
  • Sure, give me a second and I'll add it. – David C. Rankin Feb 27 '15 at 04:42
  • This works perfectly! I moved some things around for aesthetic's sake but that's nothing. The only other thing I'm seeing now is that the third occurrence is being printed twice. Not sure why, any idea? `Initialised string array: example this is is an Program ended with exit code: 0` – masteryupa Feb 27 '15 at 05:08
  • Third occurrence? I'm trying to get my head around what you are saying. Are you saying 'this' in my example prints twice? If you have a string or can show me your current code (don't erase your original question though -- just edit/add to the end), I'm happy to sort it out. Show me what you've got and we will see if we can fix it. (I suspect if you have added an array to hold the results, something is probably left there) – David C. Rankin Feb 27 '15 at 05:13
  • Sorry for being unclear, I should have said the third output is repeated so I guess that refers to position [2] in cArray. I've added the edited code anyway. Hope it helps. – masteryupa Feb 27 '15 at 05:30
  • Nope -- it is doing **exactly** what you told it to : ) `th(is) is an example` -- there really are 2 `is`'s in there... Perhaps you want it to only start at the beginning of the word on matching? This may be a case where you need to isolate each word in the input and then test `cArray[y]` against each word in `cInput` without using `strstr` by using `strcmp` – David C. Rankin Feb 27 '15 at 05:35
  • Oh! That makes so much sense, yeah ok so I just supplement `strstr` for `strcmp`? – masteryupa Feb 27 '15 at 05:48
  • Well, you will need to either use `the inch-worm` method (that's a start-pointer and end-pointer) to walk down the input string or use `strtok` to separate the words, then compare each word in the input against each word in `cArray[y]`. (both are fine, the inch-worm will teach you pointers much better) I'll drop an example later tonight, if not it will be in the morning (got kids to get in bed, etc...) – David C. Rankin Feb 27 '15 at 05:57
  • Thanks David , you've been a great help. I'll have a go at this myself; otherwise I'll check on your example later on. – masteryupa Feb 27 '15 at 06:00
  • When you start walking down strings, until you get used to it, it helps to write the string out on paper and draw arrows marking where `ptr` and `ep` are pointing and work through the loops manually, changing the `ptr` and `ep` arrows with each loop iteration. Once you have this method down, there is nothing you cannot do with strings (or arrays generally).... Just a little learning curve required -- as with all things... – David C. Rankin Feb 27 '15 at 06:24