Pure C string-replace for a whole word - c

I'm having trouble replacing a whole word in a sentence.
For example:
Replace:
the to a
hello to hi
house to tree
Input:
Hello there, this is the house.
Output:
Hi there, this is a tree.
Is it possible to do it only with the <string.h> library, with no Regex etc.?

May be this can help you
str-replace-c

It's entirely possible, but the standard library doesn't have any function to support it directly. Therefore, to do it, you'd typically use something like strstr to find the existing instance(s) of the string you want to replace, memmove to move the rest of the string, an overwrite the original with the desired replacement either manually, or perhaps with strncpy.
Note that in this case, your replacement strings are all shorter than the original they're replacing. This implies that the replacements can always be done safely. If the replacement is longer than the original, you'll also have to do something to track the maximum string length, and ensure you don't exceed it.
Oh, one other not-so-minor point, but if you're searching for something as a complete word, not just an occurrence of that string inside another word, you probably want to search for <space>word<space>, except at the beginning or end of the string, so (for example) you wouldn't replace the in there with a and turn the first word from there to are.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
typedef struct pair {
char *key;
char *value;
} Pair;
int cmp(const void *a, const void *b){
return strcmp(((const Pair*)a)->key, ((const Pair*)b)->key);
}
char *dictionary(const char *word){
static const Pair table[] =
{{"hello", "hi"}, {"house", "tree"},{"the", "a"}};
char *p, *wk = strdup(word);
Pair key, *pair;
for(p=wk;*p;++p)
*p = tolower(*p);
key.key = wk;
pair=bsearch(&key, table, sizeof(table)/sizeof(Pair), sizeof(Pair), cmp);
free(wk);
if(pair){
wk=strdup(pair->value);
if(isupper(*word))
*wk = toupper(*wk);//capitalize
return wk;
}
return NULL;
}
typedef char Type;
typedef struct vector {
size_t size;
size_t capacity;
Type *array;
} Vector;
Vector *vec_make(){
Vector *v;
v = (Vector*)malloc(sizeof(Vector));
if(v){
v->size = 0;
v->capacity=16;
v->array=(Type*)realloc(NULL, sizeof(Type)*(v->capacity += 16));
}
return v;
}
void vec_add(Vector *v, Type value){
v->array[v->size] = value;
if(++v->size == v->capacity){
v->array=(Type*)realloc(v->array, sizeof(Type)*(v->capacity += 16));
if(!v->array){
perror("memory not enough");
exit(-1);
}
}
}
void vec_adds(Vector *v, Type *values, size_t size){
while(size--)
vec_add(v, *values++);
}
char *convert(const char *str){
static const char *delimiters = " \t\n,.!?";
char *in = strdup(str);
char *out;
char *inp = in;
Vector *v = vec_make();
while(*inp){
size_t size;
if(size = strcspn(inp, delimiters)){
char *word, *cnv_word;
word = malloc(size+1);
memcpy(word, inp, size);
word[size]='\0';
if(NULL==(cnv_word = dictionary(word)))
vec_adds(v, word, size);
else
for(out = cnv_word; *out; ++out)
vec_add(v, *out);
free(word);
free(cnv_word);
inp += size;
}
if(size = strspn(inp, delimiters)){
vec_adds(v, inp, size);
inp += size;
}
}
vec_add(v, '\0');
out = v->array;
free(v);
free(in);
return out;
}
int main(void){
char input[] = "Hello there, this is the house.";
char *output = convert(input);
puts(input);
puts(output);
free(output);
return 0;
}
/* result
Hello there, this is the house.
Hi there, this is a tree.
*/

create a hash of before and after, ie what you are searching for and what you want to replace with, iterate through the string and search for the words with something like KMP or rabin karp and find what you need in your hash table.

Related

C: Take parts from a string without a delimiter (using strstr)

I have a string, for example: "Error_*_code_break_*_505_*_7.8"
I need to split the string with a loop by the delimiter "_*_" using the strstr function and input all parts into a new array, let's call it -
char *elements[4] = {"Error", "code_break", "505", "7.8"}
but strstr only gives me a pointer to a char, any help?
Note: the second string "code_break" should still contain "_", or in any other case.
This will get you half-way there. This program prints the split pieces of the string to the standard output; it does not make an array, but maybe you can add that yourself.
#include <stdio.h>
#include <string.h>
#include <malloc.h>
void split(const char * str, const char * delimiter)
{
char * writable_str = strdup(str);
if (writable_str == NULL) { return; }
char * remaining = writable_str;
while (1)
{
char * ending = strstr(remaining, delimiter);
if (ending != NULL) { *ending = 0; }
printf("%s\n", remaining);
if (ending == NULL) { break; }
remaining = ending + strlen(delimiter);
}
free(writable_str);
}
int main(void) {
const char * str = "Error_*_code_break_*_505_*_7.8";
const char * delimiter = "_*_";
split(str, delimiter);
return 0;
}
Here is a function that splits a string into an array. You have to pass the size of the array so that the function won't overfill it. It returns the number of things it put into the array. What it puts into the array is a pointer into the string that was passed. It modifies the string by inserting null characters to end the pieces - just like strtok does.
#include<string.h>
#include<stdio.h>
int split(char *string, char *delimiter, char* array[], int size)
{
int count=0;
char *current=string;
char *next;
while(current && *current!='\0')
{
next=strstr(current,delimiter);
if(!next)break;
*next='\0';
if(count<size) array[count++]=current;
current=next+strlen(delimiter);
}
if(count<size) array[count++]=current;
return count;
}
int main()
{
char string[100]="Error_*_code_break_*_505_*_7.8";
char *array[10];
int size=split(string,"_*_",array,10);
for(int i=0;i<size;i++) puts(array[i]);
return size;
}

Passing multiple strings into a function

i am trying to read several strings into a function for processing. The instructions are to pass each string into the function (not create a 2d array of strings). The parameters must stay the same. Here is what i tried
#include <stdio.h>
#include <math.h>
void convert(char s[]), int counts[]);
int main(void)
{
int i = 0;
int d[2] = {};
char text0[] = "this IS a String 4 you.";
char text1[] = "This sample has less than 987654321 leTTers.";
while(i<2)
{
convert (text[i],d); """ this is wrong but i dont know how to correctly do this
i = i +1;
}
}
void convert(char s[]), int counts[])
{
printf("%s this should print text1 and text2", s );
}
So i have a couple of questions. Is there some sort of special character/operator similiar to the glob module in python that can correctly do the convert (text[i],d) part for me where i try to read in each string. Also the int counts[] purpose is to be filled in with the word and character count in the function. So if i fill in this array in function convertwill main also recognize it since i need to print the word/character count in main without returning the actual counts in convert
You could use temporary string pointer array to pass all strings:
char text1[] = "This sample has less than 987654321 leTTers.";
char const * texts[] = { text0, text1 };
convert (texts, 2, d);
}
void convert(char const * s[], size_t n, int counts[])
{
while(n--) {
*counts++ = strlen(*s);
printf("%s\n", *s++);
}
}
Some notes:
I added char const to function argument type. You should always do that when function does not change the string. If you need to change the string in function, just remove the const.
There is extra argument size_t n to pass array array element count to function. size_t can be found in stddef.h.
i think u lost a "(" in "void convert(char s[]), int counts[]);".
it should be void convert((char s[]), int counts[]);
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void convert(char s[], int counts[]);
int main(void){
int i = 0;
int d[2] = {0};
char text0[] = "this IS a String 4 you.";
char text1[] = "This sample has less than 987654321 leTTers.";
char *text[] = { text0, text1 };
for(i=0; i<2; ++i){
convert (text[i], d);
printf("%d, %d\n", d[0], d[1]);
}
}
void convert(char s[], int counts[]){
printf("%s\n", s );
{
char *temp = strdup(s);
char *word, *delimiter = " \t\n";//Word that are separated by space character.
int count_w=0, max_len=0;
for(word = strtok(temp, delimiter); word ; word = strtok(NULL, delimiter)){
int len = strlen(word);
if(max_len < len)
max_len = len;
++count_w;
}
counts[0] = count_w;
counts[1] = max_len;
free(temp);
}
}

Making an array of tokens and comparing it with another string

I have an *input string from a console. That string might look like: show name year xxx.. and I need an output to look like this:
name: Adi
year: 1994 (for example)..
I have been trying to achieve this by using strtok() function, but I also need to compare every tokon with allowed keyywords(name, year...) if that word is not allowed, than the token needs to be skiped(deleted).. for example in this case it would skip show, and xxx.
Another problem is that I need those tokens in a form of an array in order to work with them and with a structs..
There should be no limit to number of words that could be entered in an input..
I hope you understood what I asked.. so, how to make tokens from a string using strtok or something else and make them be arrays or pointers, and how to compare those tokens with another string ( for example constant: #define NAME "name") and of there are some other inputs to skip(delete) them..
I would really appreciate it if you could help me with this.. Thanks..
I would avoid the array. It provides unnecessary overhead. What you're asking for could be accomplished with something like this:
void parseString(char * string) {
char * name = NULL;
char * year = NULL:
char * ptr = strtok(string, " ");
while (ptr != NULL) {
if (stricmp(ptr, "name") == 0) {
ptr = strtok(ptr, " ");
name = ptr;
/* do whatever with name */
} else if (stricmp(ptr, "year") == 0) {
ptr = strtok(ptr, " ");
/* do whatever with year */
year = ptr;
} /* else if ... */
ptr = strtok(ptr, " ");
}
This gives you a fair amount of flexibility. You check all the terms you need, you don't need to worry about how to allocate the array, and you can access values for settings if necessary.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
char *tolowerstr(char *str){
char *p = str;
while((*p++ = tolower(*p)));
return str;
}
int cmp(const void *a, const void *b){
return strcmp(*(const char **)a, *(const char **)b);
}
bool isBanWord(const char *word){
static const char *table[] =
{ "fuck", "show", "xxx" };//sorted
char **ret, *key;
key = tolowerstr(strdup(word));
ret=bsearch(&key, table, sizeof(table)/sizeof(*table), sizeof(*table), cmp);
free(key);
return !!ret;//ret != NULL ? true : false;
}
//Create and return as a dynamic array of pointer to the copy of the word from a string.
//String passed is destroyed.
char **strToWords(char *str, size_t *size){
const char *delimiters = " .";
size_t count=0;
char **array = malloc(strlen(str)*sizeof(char*));//number of words < string length
if(array){
char *token=strtok(str, delimiters);
for(; token ;token=strtok(NULL, delimiters)){
if(!isBanWord(token))//skip ban word
array[count++] = strdup(token);
}
array[count] = NULL;//End mark
array=realloc(array, (count + 1)*sizeof(*array));//include NULL
}
*size = count;
return array;
}
typedef struct words {
char **words;
size_t n; //number of words
} Words;
void clearWords(Words *w){
size_t i;
for(i=0;i < w->n;++i)
free(w->words[i]);
free(w->words);
w->words = NULL;
w->n = 0;
}
void printWords(Words *w){
size_t i=0;
while(i < w->n){
printf("%s", w->words[i++]);
if(w->words[i])
putchar(' ');
}
putchar('\n');
}
int main(){//DEMO
char sentence[] = "show name year xxx.";//input string. Will be destroyed.
Words w;
w.words = strToWords(sentence, &w.n);
printWords(&w);//name year
clearWords(&w);
return 0;
}

qsort() sorts one array of strings but segfaults on another one

I'm trying to read a bunch of names from a .txt file and copying them to an array as I go. I then want to sort the array using qsort(). Also, the file I'm reading is names.txt from Project Euler #22. Here is the code:
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
/* create a pointer to point to s */
char *strdup(char *s)
{
char *p;
p = (char *) malloc(strlen(s)+1);
if (p != NULL)
strcpy(p, s);
return p;
}
int compare(const void *a, const void *b)
{
const char *ap = *(const char **) a;
const char *bp = *(const char **) b;
return strcmp(ap, bp);
}
int main(void)
{
FILE *fp;
int c, i, j=0;
char name[100], *names[10000];
fp = fopen("names.txt", "r");
if (fp == NULL) {
printf("can't open file\n");
exit(0);
}
c = fgetc(fp); /* initialize c and skip first quotation mark */
while (c != EOF) { /* loop until no names are left */
i = 0;
while ((c=fgetc(fp)) != '"') /* copy chars to name until " is reached */
name[i++] = c;
name[i] = '\0';
names[j++] = strdup(name);
fgetc(fp); /* skip comma */
c = fgetc(fp);
}
size_t size = sizeof(names[0]);
size_t count = sizeof(names)/size;
qsort((void **) names, count, size, &compare);
return 0;
}
Trying to sort the names array causes a segfault. However, if I instead try to sort an array of strings that is explicitly declared it works:
char *test[] = { "FOO", "BAR", "TEST" };
size_t size = sizeof(test[0]);
size_t count = sizeof(test)/size;
qsort((void **) test, count, size, &compare);
for (i = 0; i < 3; ++i)
printf("%s\n", test[i]);
return 0;
I suspect that the segfault is due to an error in my array "names", but if I loop through and print each element of "names" before trying to sort it does so without a problem.
Any help is much appreciated!
This line:
size_t count = sizeof(names)/size;
Will yield the entire length of your names array, not just the values you have initialized. If you entered fewer than 10000 names, you're going to have some invalid pointers in there, and when you try to sort them - KABOOM!
You can just use j instead of count, since you're using that to keep track of how many names have been input.
You are missing to initialise names.
The easiest way to do so is like this:
names[10000] = {NULL};
Also the compare function is not prepared to handle the unused entries, you could modify it like this, treating unused entries like emtpy entries.
int compare(const void *a, const void *b)
{
const char *ap = a ?*(const char **) a :"";
const char *bp = b ?*(const char **) b :"";
return strcmp(ap, bp);
}
Alternativly you could sort all unused entries to the end:
int compare(const void *a, const void *b)
{
if (*a && *b)
{
const char *ap = a ?*(const char **) a :"";
const char *bp = b ?*(const char **) b :"";
return strcmp(ap, bp);
}
else
{
if (*a)
return -1;
else (*b)
return 1;
return 0;
}
}
Also you are telling qsort() to always inspect all of names's entries. Which is is unnecessary.

how to create a substring without using malloc()

How can I implement a substring function such as the following that returns the substring but without using malloc() in the process so I don't have to worry about freeing the associated memory elsewhere in my code using the free() function. Is this even possible?
const char *substring(const char *string, int position, int length)
{
char *pointer;
int c;
pointer = malloc(length+1);
if (pointer == NULL)
{
printf("Unable to allocate memory.\n");
exit(EXIT_FAILURE);
}
for (c = 0 ; c < position -1 ; c++)
string++;
for (c = 0 ; c < length ; c++)
{
*(pointer+c) = *string;
string++;
}
*(pointer+c) = '\0';
return substr;
}
UPDATE: 30 DEC 2012
Having considered all the answers and comments it's clear that essentially what I'm trying to do is create a dynamically sized array (i.e. the substring) and that is not possible in C without somewhere along the way having to use some kind of malloc() function and a subsequent free() call on the substring pointer or without the aid of a garbage collector. I attempted to integrate the libgc garbage collector as kindly suggested by #elhadi but so far have not been able to get this to work in my Xcode project. So I have opted to stick with using the following code with malloc() and free().
char * subStr(const char* srcString, const int offset, const int len)
{
char * sub = (char*)malloc(len+1);
memcpy(sub, srcString + offset, len);
sub[len] = 0;
return sub;
}
int main()
{
const char * message = "hello universe";
char * sub = subStr( message, 6, 8 );
printf( "substring: [%s]", sub );
free(sub);
}
I see two options:
If you can destroy the source string (usually a bad thing):
{
string[ position + length] = 0;
return & string[ position ];
}
Note: (see Cole Johnsons note: free no longer works on the returned pointer!)
If you can't modify the source string:
Modify your methods signature so that the caller has to worry about it:
const char *substring(const char *source, char* destination, int position, int length)
And put the modified string into destination (and return it).
And do not even think about this:
const char *substring(const char *string, int position, int length)
{
char *pointer;
int c;
static char modifiedString[256];
...
return modifiedString;
}
Using a static variable inside the function for the modified results...
(This is not thread-safe (not re-entrant!) )
Use a local buffer (an auto array) and a function like this:
void substr(char *dst, const char *src, size_t loc, size_t len)
{
memcpy(dst, src + loc, len);
dst[len] = 0;
}
Call it like this:
const size_t size = 3;
char buf[size + 1]; // this is an auto array, it will be "freed" at the end of the scope
substr(buf, "abcdFOObar", 4, size);
Always ensure the buffer is at least len + 1 bytes long to avoid buffer overflow errors.
const char *substring(const char *string, char *substr, int position, int length)
{
int c;
for (c = 0 ; c < position -1 ; c++)
string++;
for (c = 0 ; c < length ; c++)
{
*(substr+c) = *string;
string++;
}
*(substr+c) = '\0';
return substr;
}
calling function...
int main(int argc, char * argv[]) {
char substr[10];
substring("hello! World", &substr[0], 2, 4);
}
The best way to do it is:
typedef struct vstr_t {
char *s;
int len;
} vstr_t;
#define vstr_set(d, l) \
({ \
vstr_t vs = {.s = d, .len = l}; \
\
vs; \
})
#define vstr_fmt_arg(vs) (vs).len, (vs).s
int main()
{
const char *message = "hello universe";
printf( "substring: [%.*s]\n", vstr_fmt_arg(vstr_set(smpl + 6, 8)));
return 0;
}
You can use a garbage collector, you allocate the memory the first time, the garbage collector will free the memory when no needed.
you should include
#include "gc.h"
in the main you should make something like
GC_INIT(); /* Optional on Linux/X86;*/
and your substr function is:
char *substr(const char* buffer, const int offset, int len)
{
char sub = (char*)GC_MALLOC(len+1);
memcpy(sub, buffer + offset, len);
sub[len] = 0;
return sub;
}
you should link with libgc.a

Resources