#include <stdio.h>
#include <string.h>
int main()
{
char str1[80] = "downtown", str2[20] = "town";
int len1 = 0, len2 = 0, i, j, count;
len1 = strlen(str1);
len2 = strlen(str2);
for (i = 0; i <= len1 - len2; i++) {
for (j = i; j < i + len2; j++) {
count = 1;
if (str1[j] != str2[j - i]) {
count = 0;
break;
}
}
if (count == 1) {
break;
}
}
if (count == 1) {
printf("True");
} else {
printf("False");
}
}
In the above code, I'm trying to solve this one without using string functions apart from strlen() which can be replaced with a simple while loop. Is there any other way of checking for consecutive characters like firstly checking if the character is in the string, and if the i index is in the next position and not randomly in the string.
Here is a very clean way to do it using a function. It assumes that both str and sub are proper C-strings and returns a pointer to first match and NULL if no match.
char *substr(const char *str, const char *sub) {
if (!*sub)
return str; // Empty string is substring of all strings
while (*str) {
const char *sub1 = sub;
const char *str1 = str;
while (*str1++ == *sub1++) {
if (!*sub1)
return (char *)str;
}
str++;
}
return NULL;
}
This function is identical to the standard function strstr(), present in the C Standard library and declared in <string.h>.
There are some problems in the posted code:
count is uninitialized and only set if the inner loop is reached, which will not be the case if len1 < len2. count should be intialized to 0 to handle this case properly. found would be a more informative name for this variable.
Furthermore, the statement count = 1; should be moved before the inner loop to handle the case of an empty substring.
Here is a modified version:
#include <stdio.h>
#include <string.h>
int main() {
char str1[] = "downtown", str2[] = "town";
int found = 0;
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
if (len1 >= len2) {
for (size_t i = 0; i <= len1 - len2; i++) {
found = 1;
for (size_t j = 0; j < len2; j++) {
if (str1[i + j] != str2[j]) {
found = 0;
break;
}
}
if (found) {
break;
}
}
}
if (found) {
printf("True\n");
} else {
printf("False\n");
}
return 0;
}
I did not readily see OP's error. See #chqrlie.
Minor: Use size_t i, not int to cope with long strings.
strlen(str1) runs down the entire string, even when not needed, so that is avoided.
Alternative:
Fixed a faulty case in #klutt otherwise good answer.
Added const for greater application.
Return beginning of match on success.
Simplified and test harness added.
I like the needle in a haystack identifiers
char* substr3(const char *haystack, const char *needle) {
while (*haystack) {
const char *htmp = haystack;
const char *ntmp = needle;
while (*htmp == *ntmp && *htmp) {
htmp++;
ntmp++;
}
if (!*ntmp) {
return (char*) haystack; // Beginning of match
}
haystack++;
}
return *needle ? NULL : (char *) haystack;
}
int main(void) {
printf("%s\n", substr3("ababc", "abc"));
printf("%s\n", substr3("abc", "abc"));
printf("%s\n", substr3("abd", "ab"));
printf("%s\n", substr3("abc", ""));
printf("%s\n", substr3("", ""));
printf("%p\n", substr3("a", "abc"));
printf("%p\n", substr3("aba", "abc"));
printf("%p\n", substr3("x", "ab"));
printf("%p\n", substr3("aaa", "ab"));
return 0;
}
Some tighter code
char* substr4(const char *haystack, const char *needle) {
do {
const char *htmp = haystack;
const char *ntmp = needle;
while (*htmp == *ntmp && *ntmp) {
htmp++;
ntmp++;
}
if (!*ntmp) {
return (char*) haystack; // Beginning of match
}
} while (*haystack++);
return NULL;
}
Simple && naive:
char *substr2(char *str, char *sub) {
if (!*sub) return str; // not needed: see the generated code.
for(; *str; str++) {
size_t pos;
for(pos=0; str[pos] ; pos++) {
if (str[pos] != sub[pos]) break;
}
if (!sub[pos]) return str;
}
return NULL;
}
Generally:
if you don't try to outsmart the compiler: you win.
fewer variables: you win
fewer conditions inside the loop: win
try to be smart: you'll lose
when all else fails: use KMP or BM search
Related
I have to write a function to check for palindromes in sentences and words.
For sentences it looks like this (and works):
int checkSentencepalindrome(char * str)
{
size_t len = strlen(str);
char *pntr1 = str;
char *pntr2 = str + len - 1;
while(pntr2 >= pntr1)
{
if (!isalpha(*pntr2))
{
pntr2--;
continue;
}
if (!isalpha(*pntr1))
{
pntr1++;
continue;
}
if(tolower(*pntr1) != tolower(*pntr2))
{
return 0;
}
pntr1++;
pntr2--;
}
return 1;
}
for word palindromes it looks like this:
int checkWordpalindrome(char * str)
{
size_t len = strlen(str);
char *pntr1 = str;
char *pntr2 = str + len - 1;
while(pntr2 >= pntr1)
{
if(tolower(*pntr1) != tolower(*pntr2))
{
return 0;
}
pntr1++;
pntr2--;
}
return 1;
}
However, the function for word palindromes returns me 0 all the time. (I expect a 1 for palindrome and 0 for non-palindrome words)
I thought the if statements I deleted are just there to skip spaces, exclamation marks, etc. (everything not included in the alphabet) Why are they so crucial for my function to work properly then?
How can I solve this while using pointers only?
EDIT: The issue only occurs when passing the string as following:
int checkWpalindrome(char * str)
{
printf("%s ", str);
size_t len = strlen(str);
if(len == 0)
{
return 0;
}
char *pntr1 = str;
char *pntr2 = str + len - 1;
while(pntr2 >= pntr1)
{
if(tolower(*pntr1) != tolower(*pntr2))
{
return 0;
}
pntr1++;
pntr2--;
}
return 1;
}
void input(char * str)
{
printf("Input: ");
fgets(str, 101, stdin);
fflush(stdin);
}
int main()
{
char arr[10];
input(arr);
printf("%d", checkWpalindrome(arr));
return 0;
}
fgets() includes reading and saving a '\n' and causes checkWordpalindrome() to return 0 (unless maybe only "\n" was read). #Adrian Mole.
To lop off the potential '\n', use str[strcspn(str, "\n")] = 0; right after fgets().
Both functions risk undefined behavior (UB) as they can attempt to generate a pointer to before str. Consider check...("?").
In checkSentencepalindrome() the UB is is more so as code then attempts *pntr2 of an invalid pointer.
isalpha(ch) and tolower(ch) have (UB) when ch < 0 (and not EOF)
Calling code buffer too small: char arr[10]; ... fgets(str, 101, stdin);. Make them the same size like char arr[101]; ... fgets(str, 101, stdin);
fflush(stdin); is UB and not needed. #Adrian Mole
Repaired code:
int checkSentencepalindrome(const char * str) {
size_t len = strlen(str);
const unsigned char *left = (const unsigned char *) str;
const unsigned char *right = left + len; // point one-past
while(left < right) {
if (!isalpha(right[-1])) {
right--;
continue;
}
if (!isalpha(left[0])) {
left++;
continue;
}
right--;
if (tolower(left[0]) != tolower(right[0])) {
return 0;
}
left++;
}
return 1;
}
A bit tidier with
while(left < right) {
if (!isalpha(*left)) {
left++;
continue;
}
right--;
if (!isalpha(*right)) {
continue;
}
if (tolower(*left) != tolower(*right)) {
return 0;
}
left++;
}
I have written a code to test a substring operation.
test.c
#include "compiler_expression.h"
#include <stdio.h>
int main()
{
char *s1 = "(Hello) World";
int l = 0;
printf("%s\n", substr_limits(s1, '(', ')', &l));
return 0;
}
compiler_expression.h
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
// strcpy(s1, s2) Copies s2 into s1
// strcat(s1, s2) Concatenates s2 onto the end of s1
// strlen(s1) Returns the length of s1
// strcmp(s1, s2) Returns 0 if s1 and s2 are the same; less than 0 if s1<s2; greater
// than 0 if s1>s2
// strchr(s1, ch) Returns a pointer to the first occurrence of ch in s1
// strstr(s1, s2) Returns a pointer to the first occurrence of s2 in s1
char * substr(char *str, int begin, int end)
{
if (begin<0 || end>strlen(str))
{
printf("\nError: arguments (begin or end) is out of index\n");
exit(1);
}
char *res = malloc(end-begin);
for (int i=begin; i<end; i++)
{
res[i-begin] = str[i];
}
return res;
}
char *substr_limits(char *str, char begin_char, char end_char, int *no_limit)
{
*no_limit = 1;
if (begin_char==end_char)
{
printf("%s\n", "::begin char is equal to end char");
int begin_limit = 0;
int end_limit = 0;
int limit_num = 0;
for (int i=0;i<strlen(str);i++)
{
printf("%s\n", "::begin first loop");
if (str[i]!=begin_char) continue;
printf("%s\n", "::limit char found");
if (limit_num==1)
{
printf("%s\n", "::limit_num==1");
end_limit = i;
char *res = malloc(end_limit-begin_limit-1);
for (int j=begin_limit+1;j<end_limit;j++)
{
printf("%s%c\n", "::char==", str[j]);
res[j-begin_limit] = str[j];
printf("%s%c\n", "::res_char==", res[j-begin_limit]);
}
printf("%s%s\n", "::result==", res);
return res;
}
*no_limit = 0;
limit_num = 1;
begin_limit = i;
}
if (limit_num!=0) {
printf("\nError: The limits are not justified\n");
exit(1);
}
if (*no_limit==1) {
return str;
}
}
else
{
int begin_limit = 0;
int end_limit = 0;
int limit_num = 0;
for (int i=0;i<strlen(str);i++)
{
if (str[i]!=begin_char&&str[i]!=end_char) continue;
*no_limit = 0;
if (str[i]==begin_char)
{
limit_num += 1;
if (limit_num==1) begin_limit = i;
}
else if (str[i]==end_char)
{
limit_num -= 1;
if (limit_num==0)
{
end_limit = i;
char *res = malloc(end_limit-begin_limit);
for (i=begin_limit+1;i<end_limit;i++)
{
res[i-begin_limit] = str[i];
}
return res;
}
}
}
if (limit_num!=0) {
printf("\nError: The limits are not justified\n");
exit(1);
}
if (*no_limit==1) {
return str;
}
}
}
at the function (substr_limits), there is a problem: when the condition (begin_char==end_char) is true and after executing all code, I noticed at last operations before executing (return res;) that the code is run in the correct way and get all specified characters from (str) into (res) variable but after ending block of (for loop), I have noticed that the value of (res) is blank string which makes also the returned value is blank string, I was trying to solve problem by writing this code:
char *res = malloc(end_limit-begin_limit-1);
for (int j=begin_limit+1;j<end_limit;j++)
{
printf("%s%c\n", "::char==", str[j]);
res[j-begin_limit] = str[j];
printf("%s%c\n", "::res_char==", res[j-begin_limit]);
if (j==end_limit-1) {
printf("%s%s\n", "::result==", res);
return res;
}
}
But the problem still exists!
Bug #1. Instead of this:
char *res = malloc(end-begin);
for (int i=begin; i<end; i++)
{
res[i-begin] = str[i];
}
This:
char *res = malloc(end-begin+1); //+1 for null char
for (int i=begin; i<end; i++)
{
res[i-begin] = str[i];
}
res[end-begin] = '\0'; // null terminate the string that gets returned
I don't see substr getting invoked, but I see other variations of this string copy pattern missing the null char in your limits function.
More to come as I keep looking at the code....
I am supposed to save every sequence of digits from a string in an array of chars , this is what i tried:
#include<stdio.h>
#include<ctype.h>
#include<string.h>
int check_number(char *s) {
for (; *s; ++s) {
if (!isdigit(*s))
return 0;
}
return 1;
}
void int_in_string(char *s, char **ar, int MaxCap) {
char temp[100];
int index = 0;
int i = 0;
for (; *s; s++) {
if (index == MaxCap) {
break;
}
if (isdigit(*s)) {
temp[i++] = *s;
}
if (*s == ' ' && check_number(temp)) {
ar[index++] = temp;
memset(temp, '\0', i);
i = 0;
}
}
if (index == 0) {
printf("no numbers in string");
}
for (int i = 0; i < index; i++)
printf(" %s \n", ar[i]);
}
but this code only prints several newlines , can someone explain me what i do wrong?
Some issues:
ar[index++]=temp;
This is just storing the same value (the address of temp) over and over. What you need to do is copy the string into the array.
Also, you need to terminate the string temp with '\0'. You handle this in all but the first string with memset(temp, '\0', i); However, since local variables are not initialized, you need to do it:
char temp[100] = {0}
Or, you can remove the initialization and the memset by just adding the EOS:
temp[i] = '\0';
Lastly, since you declare the original array as
char * ar[10];
You are not allocating any space for the strings. The simplest way to handle that is with strdup.
void int_in_string(char *s, char **ar, int MaxCap)
{
char temp[100];
int index = 0;
int i = 0;
for (; *s; s++) {
if (isdigit(*s)) {
temp[i++] = *s;
// Need to avoid buffer overflow
if (i == sizeof(temp)) {
i = 0;
}
}
if (isspace(*s)) {
temp[i] = '\0';
// strdup will allocate memory for the string, then copy it
ar[index++] = strdup(temp);
// if (NULL == ar[index-1]) TODO: Handle no memory error
i = 0;
if (index == MaxCap) {
break;
}
}
}
if (index == 0) {
printf("no numbers in string");
}
for (int i = 0; i < index; i++) {
printf(" %s \n", ar[i]);
// free the mem from strdup
free(ar[i]);
}
}
I believe some systems may not have strdup(). If not, it can be easily replicated:
char * my_strdup(const char *src)
{
if (src == NULL) return NULL;
char *dest = malloc(strlen(src) + 1);
if (dest == NULL) return NULL;
strcpy(dest, src);
return dest;
}
I have an array of strings and am trying to reverse each string in the array to see if that string is a palindrome. I am using a for loop to increment an int i (the index). However after the I call the reverse function, the value of i becomes some really large number and I cant figure out why this is happening.
#include <stdio.h>
#include <string.h>
void revString(char *dest, const char *source);
int main() {
const char *strs[] = {
"racecar",
"radar",
"hello",
"world"
};
int i;
char res[] = "";
for (i = 0; i < strlen(*strs); i++) {
printf("i is %d\n", i);
revString(&res[0], strs[i]); //reversing string
printf("i is now %d\n", i);
//comparing string and reversed string
if (strcmp(res, strs[i]) == 0) {
printf("Is a palindrome");
} else {
printf("Not a palindrome");
}
}
return 0;
}
void revString(char *dest, const char *source) {
printf("%s\n", source);
int len = strlen(source);
printf("%d\n", len);
const char *p;
char s;
for (p = (source + (len - 1)); p >= source; p--) {
s = *p;
*(dest) = s;
dest += 1;
}
*dest = '\0';
}
This is the output showing the value of i before and after the revString function is called.
i is 0
i is now 1667588961
Illegal instruction: 4
There are multiple problems in your code:
You pass a destination array char res[] = ""; that is much too small for the strings you want to reverse. It's size is 1. This causes buffer overflow, resulting in undefined behavior.
Use char res[20]; instead.
You enumerate the array of string with an incorrect upper bound. Use this instead:
for (i = 0; i < sizeof(strs) / sizeof(*strs); i++)
The termination test for the loop in revString() is incorrect too: decrementing p when is equal to source has undefined behavior, although it is unlikely to have an consequences. You can simplify this function this way:
void revString(char *dest, const char *source) {
size_t len = strlen(source);
for (size_t i = 0; i < len; i++) {
dest[i] = source[len - i - 1];
}
dest[len] = '\0';
}
Here is the resulting code:
#include <stdio.h>
#include <string.h>
void revString(char *dest, const char *source) {
size_t len = strlen(source);
for (size_t i = 0; i < len; i++) {
dest[i] = source[len - i - 1];
}
dest[len] = '\0';
}
int main(void) {
const char *strs[] = { "racecar", "radar", "hello", "world" };
char res[20];
for (size_t i = 0; i < sizeof(strs) / sizeof(*strs); i++) {
revString(res, strs[i]);
//comparing string and reversed string
if (strcmp(res, strs[i]) == 0) {
printf("Is a palindrome\n");
} else {
printf("Not a palindrome\n");
}
}
return 0;
}
Here is Final Code with some change
#include <stdio.h>
#include <string.h>
void revString(char* dest, const char* source);
int main(){
const char* strs[] = {
"racecar",
"radar",
"hello",
"world"
};
static int i;
char res[] = "";
int length = (int) sizeof(strs)/sizeof(char*);
for(i = 0; i < length; i++)
{
printf("i is %d\n", i);
revString(&res[0], strs[i]); //reversing string
printf("i is now %d\n", i);
//comparing string and reversed string
if(strcmp(res, strs[i]) == 0){
printf("Is a palindrome");
}else{
printf("Not a palindrome");
}
}
return 0;
}
void revString(char* dest, const char* source){
printf("%s\n", source);
int len = (int) strlen(source);
printf("%d\n", len);
const char* p;
char s;
for(p = (source + (len - 1)); p >= source; p--){
s = *p;
*(dest) = s;
dest += 1;
}
*dest = '\0';
}
Change 1 :-
int i; to static int i; (Reason:- i is local variable you are calling
function so when function call the value of i will remove and after
that it will assign garbage value.)
change 2 :-
strlen(*strs) to length of array (because strlen(*strs) will give the
length of first string)
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Right now I can replace the word indifferent in the given string with nonchalant but I need to make this function dynamic so indifferent can be replaced with any word. I know i need to use malloc to create a new array that will hold my original string with the new word but don't have a strong understanding of how to use malloc yet, Please explain how to use malloc properly in this situation. Thank you.
#include <stdio.h>
#include <stdlib.h>
int findPosition(char string[], char sub[]) {
int i = 0;
int j = 0;
int f = 0;
for (i = 0; string[i] != '\0'; i++) {
if (sub[j] == string[i]) {
if (sub[j + 1] == '\0') {
f = 1;
break;
}
j++;
} else
j = 0;
}
if (f == 1) {
return i - j;
}
return -1;
}
int findLength(char sub[]) {
int i = 0;
for (i = 0; sub[i] != '\0'; i++) {
}
return i;
};
void replaceWord(char string[], char sub[], char replace[]) {
int i = 0;
int j = 0;
int p = findPosition(string, sub);
int l = findLength(sub);
int k = p + l - 1;
for (i = p; i < k; i++) {
string[i] = replace[j];
j++;
}
while(string[k] != '\0') {
string[k] = string[k + 1];
k++;
}
}
int main(int argc, const char *argv[]) {
char stringArray[120] = "\"Mr.Fay, is this going to be a battle of wits? \""
"\t\"If it is,\" was the indifferent retort, \""
"you have come unarmed!\"";
replaceWord(stringArray, "indifferent", "nonchalant");
int i = 0;
while (stringArray[i] != '\0') {
printf("%c", stringArray[i]);
i++;
}
return 0;
};
You can use malloc to allocate memory for the new string where you replace each occurrence of one word with another word:
#include <stdio.h>
#include <stdlib.h>
/* use a local implementation of the string functions: */
size_t my_strlen(const char *s) {
size_t len;
for (len = 0; s[len] != '\0'; len++)
continue;
return len;
}
void *my_memcpy(void *dest, const void *src, size_t n) {
size_t i;
for (i = 0; i < n; i++) {
((unsigned char*)dest)[i] = ((unsigned char*)src)[i];
}
return dest;
}
char *my_strdup(const char *s) {
size_t n = my_strlen(s) + 1;
char *p = malloc(n);
if (p) my_memcpy(p, s, n);
return p;
}
char *my_strstr(const char *s1, const char *s2) {
for (;; s1++) {
for (size_t i = 0;; i++) {
if (s2[i] == '\0') return s1;
if (s1[i] != s2[i]) break;
}
if (*s1 == '\0') return NULL;
}
}
char *replaceWord(const char *str, const char *s1, const char *s2) {
char *res = my_strdup(str); /* return value is always allocated */
char *p, *q;
size_t offset = 0;
size_t len = my_strlen(str);
size_t len1 = my_strlen(s1);
size_t len2 = my_strlen(s2);
if (len1 == 0)
return res;
while ((p = my_strstr(res + offset, s1)) != NULL) {
offset = p - res;
if (len1 == len2) {
/* no need to reallocate, replace in place */
my_memcpy(res + offset, s2, len2);
} else {
/* allocate a new array with the adjusted length */
q = malloc(len + len2 - len1 + 1);
/* copy the beginning of the string */
my_memcpy(q, res, offset);
/* copy the replacement string */
my_memcpy(q + offset, s2, len2);
/* copy the remainder of the string, and the final '\0' */
my_memcpy(q + offset + len2, res + offset + len1, len - offset - len1 + 1);
/* free the previous string */
free(res);
res = q;
}
/* search for matches from the end of the replacement */
offset += len2;
}
return res;
}
int main(int argc, const char *argv[]) {
char stringArray[120] = "\"Mr.Fay, is this going to be a battle of wits? \""
"\t\"If it is,\" was the indifferent retort, \""
"you have come unarmed!\"";
char *p = replaceWord(stringArray, "indifferent", "nonchalant");
printf("%s", p);
free(p);
return 0;
}