Reputation: 23
can anyone tell me how to print each word one by one inside this string? like I have a string which has random words but has fixed pattern which contains the word I want to print. However, my code only print the first word "apple" then it stoped.
this is the string:
Svnsv am /apple/ rv dbndkbrb am /orange/ rv dbundib am /bestfruit/ rv drbrnboie am /watermelon/ rv
and I want to print "apple" "orange" "bestfruit" "watermelon"
char * v = b;
char * m;
char * p;
int t = 0;
int i = 0;
while(strstr(v, "am") != NULL){
m = strstr(v, "am");
//printf("%s\n",m);
p = strtok(m, "/");
p = strtok(NULL , "/");
printf("%s\n", p);
v+=5;
}
Upvotes: 0
Views: 1961
Reputation: 153407
can anyone tell me how to print each word one by one inside this string?
Avoid strtok()
as it modifies the string and is not needed.
Consider how to parse the input string (as if it were constant), one sub-string at a time. Look for the "am"
, as you did, then look for the 2 delimiters '/'
with strchr()
. If all that was found, update where in the input string to look again and return a pointer to the sub-string and its length.
const char *HL_parse(const char **v, const char *key, int delimiter, int *length) {
*length = 0;
char *token = strstr(*v, key);
if (token == NULL) {
return NULL;
}
char *start = strchr(token + strlen(key), delimiter);
if (start == NULL) {
return NULL;
}
start++;
char *end = strchr(start, delimiter);
if (end == NULL) {
return NULL;
}
*v = end + 1;
*length = (int)(end-start);
return start;
}
Print the sub-string using "%.*s"
. This allows printing a character array without a null character up to so many characters.
void HL_print_words(const char *v, const char *key, int delimiter) {
char *sep = "";
int length;
const char *token;
while ((token = HL_parse(&v, key, delimiter, &length)) != NULL) {
printf("%s\"%.*s\"%s", sep, length, token);
sep = " ";
}
printf("\n");
}
Sample
int main(void) {
HL_print_words(
"Svnsv am /apple/ rv dbndkbrb am /orange/ rv dbundib am /bestfruit/ rv drbrnboie am /watermelon/ rv",
"am", '/');
}
Output
"apple" "orange" "bestfruit" "watermelon"
Upvotes: 0
Reputation: 123458
FWIW, here's an example using the GNU regex library (which you may or may not have available, depending on your platform). It's probably overkill for what you're trying to do, but it's an alternative to the methods the other people have shown you. Depending on the complexity of the pattern you're trying to match, it can come in handy.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <regex.h>
int main( void )
{
/**
* Text we want to search.
*/
const char *text = "Svnsv am /apple/ rv dbndkbrb am /orange/ rv dbundib am /bestfruit/ rv drbrnboie am /watermelon/ rv";
/**
* The following pattern will isolate the strings between the '/'
* characters. ".*" matches any sequence of characters, so basically
* the pattern reads, "skip over any characters until we see an opening
* '/', then match everything up to the next '/', then repeat 3 more
* times".
*/
const char *ptn = ".*/(.*)/.*/(.*)/.*/(.*)/.*/(.*)/.*";
/**
* Regular expression type.
*/
regex_t regex;
/**
* Compile the regular expression
*/
if ( regcomp(®ex, ptn, REG_EXTENDED) != 0 )
{
fprintf( stderr, "regcomp failed on %s\n", ptn );
exit( 0 );
}
/**
* Set up an array to store the start and end positions of the
* matched substrings within text.
*/
fprintf( stdout, "Number of subexpressions: %zu\n", regex.re_nsub );
size_t matchCount = regex.re_nsub + 1;
regmatch_t pmatch[matchCount];
int ret;
/**
* Execute the regular expression, then print out the matched expressions
*/
if ( ( ret = regexec( ®ex, text, matchCount, pmatch, 0)) != 0 )
{
fprintf( stderr, "%s does not match %s, return code %d\n", text, ptn, ret );
}
else
{
fprintf( stdout, "Sucessful match\n" );
for ( size_t i = 0; i < matchCount; i++ )
{
if ( pmatch[i].rm_so >= 0 )
{
fprintf( stdout, "match %zu (start: %3lu; end: %3lu): %*.*s\n", i,
(unsigned long) pmatch[i].rm_so,
(unsigned long) pmatch[i].rm_eo,
(int) ( pmatch[i].rm_eo - pmatch[i].rm_so ),
(int) ( pmatch[i].rm_eo - pmatch[i].rm_so ),
text + pmatch[i].rm_so );
}
}
}
return 0;
}
And here's the output:
Number of subexpressions: 4
Sucessful match
match 0 (start: 0; end: 98): Svnsv am /apple/ rv dbndkbrb am /orange/ rv dbundib am /bestfruit/ rv drbrnboie am /watermelon/ rv
match 1 (start: 10; end: 15): apple
match 2 (start: 33; end: 39): orange
match 3 (start: 56; end: 65): bestfruit
match 4 (start: 84; end: 94): watermelon
If you want to copy the matched strings, you'll need to use strncpy
and make sure you terminate the string properly:
char matched_string[MAX_STRING_LENGTH + 1] = {0};
...
size_t length = pmatch[1].rm_eo - pmatch[1].rm_so;
strncpy( matched_string, text + pmatch[1].rm_so, length );
/**
* Make sure string is 0 terminated
*/
matched_string[ length ] = 0;
Upvotes: 0
Reputation: 9855
Function strtok()
modifies the original string by inserting '\0' at the position of the delimiter. (Read the documentation of strtok.) As your string gets truncated by this '\0', the next strstr
will not find the pattern.
Here is a slightly modified version of the original code.
#include <stdio.h>
#include <string.h>
int main(void) {
char str[] = "Svnsv am /apple/ rv dbndkbrb am /orange/ rv dbundib am /bestfruit/ rv drbrnboie am /watermelon/ rv";
char * v = str;
char * m;
char * p;
int t = 0;
int i = 0;
while(strstr(v, "am /") != NULL){
m = strstr(v, "am");
//printf("%s\n",m);
p = strtok(m, "/");
p = strtok(NULL , "/");
if(p != NULL)
{
printf("%s\n", p);
v = p + strlen(p)+1;
}
else
{
v+=5;
}
}
return 0;
}
Upvotes: 0
Reputation: 2571
You are only finding the first match because of your use of strtok
. Try:
char string[] = "Svnsv am /apple/ rv dbndkbrb am /orange/ rv dbundib am /bestfruit/ rv drbrnboie am /watermelon/ rv";
for (char *i = string; i != NULL; i = strstr(i, "am")) {
char *start;
char *end;
char *match;
if (start = strstr(i, "/")) {
if (end = strstr(start + 1, "/")) {
int start_index = start - string;
int end_index = end - string;
printf("%.*s\n", end_index - start_index - 1, string + start_index + 1);
}
}
i += 2;
}
Upvotes: 0
Reputation: 44274
Using putchar
it can be done like:
#include <stdio.h>
#include <string.h>
int main(void) {
char str[128] = "Svnsv am /apple/ rv dbndkbrb am /orange/ rv dbundib am /bestfruit/ rv drbrnboie am /watermelon/ rv";
char* p; // A help pointer for parsing the string
p = strstr(str, "am /"); // Find the first match
while (p)
{
p += 4; // Increment p to point just after "am /"
// print the word
putchar('"');
while (*p && *p != '/') putchar(*p++); // notice the increment of p
putchar('"');
putchar(' ');
if (p) p = strstr(p, "am /"); // find next match
}
return 0;
}
Output:
"apple" "orange" "bestfruit" "watermelon"
Upvotes: 0
Reputation: 8142
It's only finding the first one because you're using strtok
.
strtok
alters the string you tokenise by adding NUL characters to generate the individual tokens so by the end of the loop your string will look like "am \0apple\0 rv dbndkbrb am /orange/"
. When you advanced v
by 5 characters you don't skip far enough past what you've just processed to get to the rest of the string.
Instead of using strtok
, use strchr
which will find the next instance of the specified character. Use it to find the starting and ending slashes and replace the ending one with a NUL. You can then use it (in my code represented by p2
) to correctly place v
at the start of the next block of text to process.
while(strstr(v, "am") != NULL){
m = strstr(v, "am");
p = strchr(m, '/'); // Start
if (!p) {
v += 2;
continue;
}
p++;
p2 = strchr(p , '/'); // End
if (!p2) {
v = p;
continue;
}
*p2 = '\0';
printf("%s\n", p);
v = p2+1;
}
Upvotes: 1