You are heading in the right direction, and you are starting to think about stepping through stings correctly, but you are getting wrapped up in your use of any additional string -- there is no need for that.
When you are analyzing stings and substrings, all you care about is:
- the index (position) within the original string (to iterate);
- whether you have found the beginning of the substring yet (if so set flag indicating in word matching chars); and
- whether you make it all the way to the end of the substring while your flag is set (if you do, you have found your substring). Otherwise, if there is a mismatch in characters before reaching the end, unset your flag and keep going. Repeat until you run out of
str1
.
Complicating your case here is the Red-Herring '?'
character. You can either use it as a test for end-of-substring, or ignore it altogether. Your goal is to locate str2
"street"
within str1
"... streets?"
. You would generally use ispunct()
from ctype.h
to identify '?'
as a non-word character and not part of the string anyway. Here, you can simply check for '?'
in a similar way that you would check for '\0'
to mark end-of-string.
Putting it altogether and not using any of the string.h
or ctype.h
functions, you could do:
#include <stdio.h>
const char *qstr_strstr (const char *str1, const char *str2)
{
int i = 0, instr2 = 0, ndx = 0; /* str1 index, in-str2 flag, str2 index */
if (str1 == NULL || str2 == NULL) /* validte both str1 and str2 not NULL */
return NULL;
do { /* loop over each char in str1 */
/* if in str2 and (at end or at '?') */
if (instr2 && (!str2[ndx] || str2[ndx] == '?'))
return &str1[i-ndx]; /* return address in str1 to start of str2 */
/* if char in str1 and str2 equal */
if (str1[i] == str2[ndx]) {
ndx += 1; /* increment str2 index */
instr2 = 1; /* set in-str2 flag true (done each time) */
}
else /* otherwise chars not equal */
ndx = instr2 = 0; /* reset str2 index, set in-str2 flag false */
} while (str1[i++]); /* includes \0, allows match at end of both */
return NULL;
}
int main (void) {
const char *str1 = "Watch of the streets?",
*str2 = "street?",
*ptr = qstr_strstr (str1, str2);
if (ptr) {
size_t i = ptr - str1;
printf ("ptr : %s\nptr addr : %p\n---\n"
"&str1[%zu] : %s\n&str1[%zu] : %p\n",
ptr, (void*)ptr, i, str1 + i, i, (void*)(str1 + i));
}
}
Example Use/Output
Outputting the string and adderss retuned by qstr_strstr()
as well as the address of that string and address located within the original str1
you would have:
$ ./bin/strstrawk
ptr : streets?
ptr addr : 0x4006c1
---
&str1[13] : streets?
&str1[13] : 0x4006c1
You can further copy just the substring street
from the address in str1
or whatever else you need to do. Look things over and let me know if you have further questions.
Edit Based On Picture Added
If you want to output just the characters before the '?'
from the pointer returned by qstr_strstr()
, then you can do that trivially by using the precision modifier with the "%s"
output conversion specifier in your printf
format string and limit the number of characters output to just the number before the '?'
in str2
. Your format conversion specifier would look like "%.*s"
where it expects 2 arguments, the first of type int
being the number of characters, and the second the string.
In the case above the arguments would be:
(int)strcspn (str2, "?"), ptr
Where strcspn()
returns the number of characters before the '?'
in str2
(cast to int
) and ptr
the return from qstr_strstr()
. Changing the code above you would have:
...
#include <string.h>
...
printf ("ptr : %.*s\nptr addr : %p\n---\n"
"&str1[%zu] : %s\n&str1[%zu] : %p\n",
(int)strcspn (str2, "?"), ptr, (void*)ptr,
i, str1 + i, i, (void*)(str1 + i));
(the remaining code is unchanged)
$ ./bin/strstrawk
ptr : street
ptr addr : 0x4006c1
---
&str1[13] : streets?
&str1[13] : 0x4006c1
Where the output using ptr
as the string and the length from str2
is not "street"
.
Let me know if you have further questions.