Remember that strtok
modifies the buffer.
The caller to your functions would have to pass a temporary buffer that has been copied from the original before each call.
In other words, the call to ModifiedStringSize
trashes inputString
so that when you call ManipulateString
, the updated value for inputString
is [effectively] garbage.
The usual here is to parse a buffer only once and retain a char **
array that is built up from the strtok
loop.
UPDATE:
Sorry, I’m still learning C and I’m not really that good with pointers. How exactly do I implement this?
There are basically two ways to do this:
- Caller passes a pointer to a fixed size
char **
array that the subfunction fills in with tokens [for a given line] - The subfunction uses
malloc/realloc
to create and maintain a dynamically increasing list of tokens. It returns a pointer to that array.
Option (1) is easier/simpler and is suitable for line-at-a-time processing. That is, all necessary processing can be done without having to combine multiple lines of input (e.g. this is probably suitable for your use case).
Option (2) is used if we have to read an entire multiline file and store all lines before doing any processing. Or, we can not predict how many tokens we’ll need before attempting to parse. It is the caller’s responsibility to call free
on the array elements and the array pointer itself.
Note that I had to refactor your code a bit.
Here’s the first method:
// Inputs a string with a maximum of 100 characters
// tokenizes it and converts it to pig latin in a new string
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define SIZE 1000
#define DELIMS " \t\n"
void piglatin(char *pig,const char *inp);
void pigall(char **toklist);
// parse_string -- parse a line into tokens
// RETURNS: actual count
int
parse_string(char *inputString,char **toklist,int tokmax)
{
char *bp = inputString;
char *cp;
int tokcount = 0;
// allow space for NULL terminator
--tokmax;
while (1) {
cp = strtok(bp,DELIMS);
bp = NULL;
if (cp == NULL)
break;
if (tokcount >= tokmax)
break;
toklist[tokcount++] = cp;
}
// add end of list
toklist[tokcount] = NULL;
return tokcount;
}
int
main(void)
{
// inputString is where
// string entered by user
// is stored
char inputString[SIZE];
char pig[SIZE];
// some simple tests
piglatin(pig,"pig");
piglatin(pig,"smile");
piglatin(pig,"omelet");
// fgets reads up to 100
// characters into inputString
printf("Enter phrase: ");
fflush(stdout);
fgets(inputString, SIZE, stdin);
// define the worst case number of tokens that are possible (e.g. each
// token is one char)
char *toklist[((SIZE + 1) / 2) + 1];
// parse the string into tokens
int tokcount = parse_string(inputString,toklist,
sizeof(toklist) / sizeof(toklist[0]));
printf("tokcount is %d\n",tokcount);
// translate all tokens into pig latin
pigall(toklist);
return 0;
}
Here’s the second method:
// Inputs a string with a maximum of 100 characters
// tokenizes it and converts it to pig latin in a new string
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define SIZE 1000
#define DELIMS " \t\n"
void piglatin(char *pig,const char *inp);
void pigall(char **toklist);
// parse_file -- parse all tokens in a file
// RETURNS: token list
char **
parse_file(FILE *xfsrc,size_t *tokc)
{
char *bp;
char *cp;
char buf[SIZE];
size_t tokmax = 0;
size_t tokcount = 0;
char **toklist = NULL;
while (1) {
// get next line
cp = fgets(buf,sizeof(buf),xfsrc);
if (cp == NULL)
break;
bp = buf;
while (1) {
// get next token on line
cp = strtok(bp,DELIMS);
bp = NULL;
if (cp == NULL)
break;
printf("DEBUG: '%s'\n",cp);
// NOTE: using tokmax reduces the number of realloc calls
if (tokcount >= tokmax) {
tokmax += 100;
toklist = realloc(toklist,sizeof(*toklist) * (tokmax + 1));
}
// NOTE: we _must_ use strdup to prevent the first and subsequent
// lines from being jumbled together in the _same_ buffer
toklist[tokcount++] = strdup(cp);
// add null token to end of list (just like argv)
toklist[tokcount] = NULL;
}
}
// trim list to amount actually used
if (toklist != NULL)
toklist = realloc(toklist,sizeof(*toklist) * (tokcount + 1));
// return number of tokens to caller
*tokc = tokcount;
return toklist;
}
// tokfree -- free the token list
char **
tokfree(char **toklist)
{
for (char **tok = toklist; *tok != NULL; ++tok)
free(*tok);
free(toklist);
toklist = NULL;
return toklist;
}
int
main(void)
{
// some simple tests
char pig[SIZE];
piglatin(pig,"pig");
piglatin(pig,"smile");
piglatin(pig,"omelet");
// parse entire file
size_t tokcount;
char **toklist = parse_file(stdin,&tokcount);
// translate all tokens into pig latin
pigall(toklist);
// free the list
toklist = tokfree(toklist);
return 0;
}
Spoiler Alert: Here’s the pig latin code I used to test the above with:
// piglatin.c -- convert words to pit latin
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int
isvowel(int chr)
{
chr = (unsigned char) chr;
chr = tolower(chr);
return (strchr("aeiou",chr) != NULL);
}
void
piglatin(char *pig,const char *inp)
{
const char *rhs;
char *lhs;
char pre[100];
*pig = 0;
rhs = inp;
do {
if (isvowel(*rhs)) {
strcat(pig,rhs);
strcat(pig,"yay");
break;
}
lhs = pre;
for (; *rhs != 0; ++rhs) {
if (isvowel(*rhs))
break;
*lhs++ = *rhs;
}
*lhs = 0;
strcat(pig,rhs);
strcat(pig,pre);
strcat(pig,"ay");
} while (0);
printf("%s --> %s\n",inp,pig);
}
void
pigall(char **toklist)
{
char pig[1000];
for (char **tokp = toklist; *tokp != NULL; ++tokp)
piglatin(pig,*tokp);
}
1
solved Run-time error storing pointer character from strtok after multiple calls