Both C and C++ provide access to a set of library functions that are
standard accross implementations. This library includes a set of
string and character manipulation functions. One function, strstr(),
provides the information needed to create a function that would do
what you need.
You state that you need the following:
int ism_countstr(const char *buf, const char * str)
The basic function would take on this form:
int ism_countstr(const char *buf, const char * str)
{
return (0);
}
This is now valid C/C++ code that could be called by another function.
Using your test data, a simple calling program which would write a
command windows would look like this:
#include <stdio.h>
int main()
{
int count;
count = ism_countstr("Hello World","l");
printf ("count = %d\n", count);
}
This program would use the test example you provided, call
ism_countstr, and print out the returned value using the time-honored
"printf" function. Unfortunately, it would always return zero and
therefore usually be wrong.
To make it usually be right, use the "strstr" function that I
mentioned above. To find the prototype for "strstr", look up the
prototype for the function in the header file "string.h" if you are
using Microsoft Visual C++. Unfortunately, this is not the same
accross compilers. If you are using Borland's C++, the header file is
called "strings.h".
Strstr looks something like this:
int strstr(const char *sting, const char *substring);
The "const" prefix for "char" tells us that the content of the string
will not be modified durign procesing: strstr won't mess with the
strings we provide to it. That suggests that we need to use a
variable to contain the result of a call to "strstr" that can be
modified.
When we call strstr, the function returns a poiter to the first place
in the string that constains the substring. Using the data you
provided, it would return a pointer to the first "l". If you printed
this string, it would now say "llo, world".
To get to the next instance of "l", you have to coax the pointer to
the next location in the string and now use this new location as the
input to the next call to strstr. We also have to count the fact that
we found the substring.
The new code would look like this:
int ism_countstr(const char *buf, const char * str)
{
char *ptr = (char *) buf; // cast the buf pointer to a
// non-constant char pointer
int count = 0;
ptr = strstr(ptr, str);
if (ptr != NULL)
{
// ptr now points to a string: "llo, world"
count++; // add 1 to count since we found "l"
ptr++; // move returned pointer from "l"
// ptr now points to a string: "lo, world"
}
return (count); // will return 0 if no "l", 1 if "l" found
}
Now the function works a little better, but still is often wrong. To
make it more right, we have to put the "strstr" function in a loop
that will move through the string until it has found all of the
substrings. To do this, we have to check for a NULL return before
recalling "strstr".
The new code including a "while" loop, looks like this:
int ism_countstr(const char *buf, const char * str)
{
char *ptr = (char *) buf; // cast the buf pointer to a
// non-constant char pointer
int count = 0;
while ((ptr = strstr(ptr, str)) != NULL)
{
// ptr now points to a string: "llo, world"
count++; // add 1 to count since we found "l"
ptr++; // move returned pointer from "l"
// ptr now points to a string: "lo, world"
}
return (count); // will return 0 if no "l", 1 if "l" found
}
The "while" statement evaluates the value of "ptr" to determine
whether it contains a valid pointer or a NULL pounter. If "ptr" is
NULL, the substring was not present in the string.
This function can be called from C or C++ programs.
As to the comments from an earlier poster, the function will not find
overlapping instances of the substring within the string. This
function also handles an empty string. One additional test should be
done to handle pointers to bad strings.
Example: string "Hello, World".
The string takes up 12 locations to store the characters and an
additional location to store the terminator to the string. In C/C++,
this is a character with a value of zero. Therefore, the string looks
like this:
Hello, World*
1234567890123
The thirteenth character, that I chose to represent as a *, contains a
non-printable value of zero, not the number 0. The code above
successfully tests for this value by setting the value of "ptr" to
point to the string location occupied by the "zero" value. When we
test this value, it will work correctly.
But...
If we create a string pointer ("ptr") but do not set it to a valid
location, "ptr" itself is now a NULL string pointer. In other words,
it does not point to a location containing a zero but "ptr" itself
contains a zero. This will always lead to an error. To test for
this, modifiy the code to do the following:
#define ERR_BAD_STRING -1 // error return value for bad string
#define ERR_BAD_SUBSTRING -2 // error return value for bad substring
int ism_countstr(const char *buf, const char * str)
{
char *ptr;
int count = 0;
if (buf == NULL) // check to see whether buf is a NULL pointer
return (ERR_BADSTRING);
if (str == NULL) // check to see whether str is a NULL pointer
return (ERR_BADSUBSTRING);
ptr = (char *) buf; // cast the buf pointer to a
// non-constant char pointer
while ((ptr = strstr(ptr, str)) != NULL) // check to see whether
// "ptr" points to an empty string
{
// ptr now points to a string: "llo, world"
count++; // add 1 to count since we found "l"
ptr++; // move returned pointer from "l"
// ptr now points to a string: "lo, world"
}
return (count); // will return 0 if no "l", 1 if "l" found
}
The calling program has to know that a zero return value means that
there are zero substrings and that a negative return is now an error
code. In the old days, one never used the return of a function
without testing for errors. This could also be implemented in C++
using an exception hnadler to achieve a similar effect.
That's all she wrote. |