Here is what I need to do: delete all occurrences of a number that appears most frequently in a given string
Here is what I've done: wrote two functions; the second one extracts all integers from a string into an array, finds the most frequently repeated one, calls the first function to find that number in a string, deletes all its occurrences in a given string
The problem is it works alright when I compile it, but doesn't pass the series of auto-generated tests and displays "access to an uninitialised value" and "memory error" in lines I marked with <------.
I know this is not exactly the "minimum reproducible code" but I'm hoping someone could point out what the problem is, as I run into a lot of similar errors when working with pointers.
char* find_number(char* string,int search)
{
int sign=1;
int number=0,temp=0;
char* p = string;
while(*string != '\0') {<----------
p=string;
if(*string=='-') sign=-1;
else if(*string==' ') {
string++;
continue;
} else if(*string>='0' && *string<='9') {
temp=0;
while(*string != '\0' && *string>='0' && *string<='9') {
temp=temp*10+*string-'0';
string++;
}
number=temp*sign;
if(number==search) {
return p;
}
} else {
sign=1,number=0;
}
string++;
}
return NULL;
}
char* delete_most_frequent(char* string)
{
//writing all integers in a string to an array
char* pointer=string;
char* s = string;
int temp=0, sign = 1,i=0,array[1000],number=0,counters[1001]= {0},n=0;
while (*s != '\0') {<------------
if (*s == '-') sign = -1;<----------
else if (*s >= '0' && *s <='9') {<----------
temp = 0;
while (*s != '\0' && *s >= '0' && *s <= '9') {
temp = temp * 10 + *s - '0';
s++;
}
number=sign*temp;
if(number>=0 && number<=1000) {
array[i]=number;
i++;
}
}
number=0;
sign=1;
s++;
}
n=i;//size of the array
//finding the number that occurs most frequently
int max=0;
for (i=0; i<n; i++) {
counters[array[i]]++;
if(counters[array[i]]>counters[max]) {
max=array[i];
}
}
char* p=find_number(string,max);//pointer to the first digit of wanted number
//deleting the integer
while (*string != '\0') {
if (p != NULL) {
char *beginning = p, *end = p;
while(*end>='0' && *end<='9')
end++;
//while (*beginning++ = *end++);
while(*end != '\0'){
*beginning = *end;
beginning++;
end++;
}
*beginning = '\0';
} else string++;
p=find_number(string,max);
}
return pointer;//pointer to the first character of a string
}
int main()
{
char s[] = "abc 14 0, 389aaa 14! 15 1, 153";
printf("'%s'", delete_most_frequent(s));
return 0;
}
chopping your code down to just what is likely causing the problem, you have pattern that looks like
while(*string != '\0') {
:
while(*string != '\0' ...) {
:
string++;
}
:
string++;
}
so you have two nested while loops, both of which are advancing the pointer looking for a NUL terminator to end the loop. The problem is that if the inner loop gets all the way to the NUL (it might stop earlier, but it might not), then the increment in the outer loop will increment the pointer past the NUL. It will then happily run through (probably invalid) memory looking for another NUL that might not exist. This is a hard one to catch as in most test cases you write, there are likely multiple NULs (soon) after the string, so it will appear to work fine -- you almost have to specifically write a test case to trigger this failure mode to catch this.
One fix would be to check you're not yet at the null before incrementing -- if (*string) string++; instead of just string++;
Related
I'm trying to find how may times two string matches in c, if we have two or more stars, multiple string combination can be suitable. e.g "abcdb" & "*b*" matches two times. my current code works but it returns four. I don't what I am missing here.
#include <stdio.h>
int nmatch(char *s1, char *s2) {
if (*s1 != '\0' && *s2 != '\0' && *s2 == '*' && (*s2 + 1) != *s1) {
return nmatch(s1 + 1, s2) + 1;
}
if (*s1 == *s2) {
return nmatch(s1 + 1, s2 + 1);
}
if (*s1 == *s2 && *s1 == '\0' && *s2 == '\0') {
return 0;
}
return (1);
}
int main() {
char ap[] = "abcd";
char ab[] = "*b*";
printf("%d", nmatch(ap, ab));
return 0;
}
Your code just does not count the number of different ways s1 matches pattern s2. It does not even return 1 for identical strings.
The first comparison (*s2 + 1) != *s1 is incorrect, you probably meant *(s2 + 1) != *s1 equivalent to s2[1] != *s1, but this fix is not enough to correct the algorithm.
Here is a naive implementation that does:
int nmatch(const char *s1, const char *s2) {
int count;
while (*s2 != '\0' && *s2 != '*' && *s1 == *s2) {
s1++;
s2++;
}
if (*s2 == '\0')
return *s1 == '\0';
if (*s2 != '*')
return 0;
while (*s2 == '*') /* skip all stars */
s2++;
if (*s2 == '\0')
return 1;
for (count = 0;; s1++) {
count += nmatch(s1, s2);
if (*s1 == '\0')
break;
}
return count;
}
I think your algorithm is just wrong. Together with the flawed implementation this will lead no where.
There's the input string and the pattern string. If the first character of the pattern is not a * and is not equal to the first character of the input string, then return 0 (it's a mismatch).
If they're equal, then remove the first character of both the input and the pattern and return the number of matches of the reduced input and pattern.
On the other hand, if the first character of the pattern is a *, then (in a loop) sum the number of matches of the remaining pattern with the complete input string, the input string without the first, then without the second character ... and so on until the input string is empty.
If both input and pattern are empty: return 1. If only one of the strings is empty: return 0.
I implemented it as direct recursive function (for production use that should be changed into an iterative version):
unsigned matches(char const * const input, char const * const pattern) {
if (! *pattern) {
// An empty pattern matches only the empty
// string, nothing else
return (*input) ? 0 : 1;
}
if (*pattern == '*') {
// a wildcard could be zero to any number
// of characters. Sum the number of matches
// for each possibility (excluding the empty
// rest of input for now)
char const * rest = input;
unsigned count = 0;
while (*rest) {
count += matches(rest, pattern + 1);
++rest;
}
// Add matches for the empty rest of input
count += matches(rest, pattern + 1);
return count;
}
if (! *input) {
// A non empty, non wildcard pattern cannot
// match the empty string
return 0;
}
if (*pattern == *input) {
// non wildcard match, count the ways the rest of
// the pattern matches the rest of the input.
return matches(input + 1, pattern + 1);
}
else {
// mismatch!
return 0;
}
}
(Live with tests)
To deal with the combinatory explosion of possible matches when there are multiple adjacent wildcards in the pattern one could remove these from the pattern first.
I need to develop a function that goes through a character string and detects letters (lower and upper cases), digits 0-9 and spaces ' '. If the functions finds only valid characters (the characters listed before) it returns 1 otherwise(if the string has characters like !,&,/,£, etc.) it returns 0. I am aware of a function that finds characters and digits which is isalnum().That is not helpful to find spaces. Does anyone can provide inbuilt or manual function which can detect characters,digits and spaces all together.
I've developed mine as under but function does not detect invalid character !,&,/,£ etc. in middle of the string and therefore it does not return the value I expect.
for (i=0; i<strlen(str); i++) {
if ((str[i]>='A' && str[i]<='Z') || str[i] == ' ' || (str[i]>='a' && str[i]<='z') || (str[i]>='0' && str[i]<='9'))
for (i=0; i<strlen(str); i++) {
char *p = str;
while (*p) {
if (isalnum((unsigned char) *p) || *p == ' ') {
res =1;
} else {
res = 0;
}
p++;
}
}
You can make the code more succinct:
int Validate_Alphanumeric(char *str)
{
unsigned char *ptr = (unsigned char *)str;
unsigned char uc;
while ((uc = *ptr++) != '\0')
{
if (!isalnum(uc) && uc != ' ')
return 0;
}
return 1;
}
Amongst other things, this avoids reevaluating strlen(str) on each iteration of the loop; that nominally makes the algorithm quadratic as strlen() is an O(N) operation and you would do it N times, for O(N2) in total. Either cache the result of strlen(str) in a variable or don't use it at all. Using strlen(str) requires the entire string to be scanned; the code above will stop at the first punctuation or other verboten character without scanning the whole string (but the worst case performance, for valid strings, is O(N)).
I came up with a function that goes through the string and that is able to return 0 if an invalid character (ex. $&$&&(%$(=()/)&)/) is found.
int Validate_Alphanumeric (char str[]) {
int i;
int res;
int valid=0;
int invalid=0;
const char *p = str;
while (*p) {
if (isalnum((unsigned char) *p) || *p == ' ') {
valid++;
} else {
invalid++;
}
p++;
}
if (invalid==0)
res=1;
else
res=0;
return res;
}
I forgot most of my C, so please forgive me if this is a stupid question. Because I need to separate a string of words into individual words.
#include "argsInfo.h"
#include <stdlib.h>
/* Parses string argument which contains words
* separated by whitespace. It returns an
* argsInfo data structure which contains an
* array of the parsed words and the number
* of words in the array.
*/
argsInfo getArgsInfo(char * string) {
argsInfo info;
char ** temp;
int nWords=1;
int i=0;
int j,k;
//Test if the the input string is empty
if (string[0] == '\0'){
nWords=0;
}else{
//First I need to check how long the input String is, as-well as cout how many words are in the string.
while (string[i] != '\0'){
if (string[i] == ' '){
nWords++;
}
i++;
}
}
//This allocates enough memory for each word.
temp = (char**) malloc(nWords*sizeof(char*));
for (j=0;j<nWords;j++){
temp[j] = (char*) malloc(i*sizeof(char));
}
j=0;
k=0;
// If I encounter a white space, it signifies a new word, and I need to move it to the next element
while (j < i){
if (string[j] == ' '){
k++;
}
temp[k][j] = string[j];
j++;
}
info.argc = nWords;
info.argv = temp;
return info;
}
That 3rd last LINE. THAT'S where I think the problem is. info.argv = temp;
This is what the struct looks like:
typedef struct {
int argc;
char ** argv;
} argsInfo;
Example Input and Output:
Input: "ax bcd efghij"
Output: ax
If I remove the k++ line, the output becomes: ax bcd efghij
Likewise, if I input a b c. Only 'a' will show up when I run through the array.
First, this part is inefficient but works:
for (j=0;j<nWords;j++){
temp[j] = (char*) malloc(i*sizeof(char));
}
You are using the value of i which will be equal to the total number of characters in your original input string. This means that for each separate word you are allocation enough room to store the original sentence which is a waste of space.
You could, for example, while you are counting words, also remember the longest word seen thus far and use that as your allocation factor which will probably be much less than the whole sentence. We start the length at 1 to include the terminating character '\0'
int longest = 1;
int tempLength = 1;
//Test if the the input string is empty
if (string[0] == '\0'){
nWords=0;
}else{
//First I need to check how long the input String is,
//as-well as count how many words are in the string.
while (string[i] != '\0'){
if (string[i] == ' '){
if(tempLength > longest) {
longest = tempLength;
}
nWords++;
} else {
tempLength++; // count characters of current word
}
i++;
}
}
for (j=0;j<nWords;j++){
temp[j] = (char*) malloc(longest*sizeof(char));
}
Finally, the last part of your code needs a fix. It doesn't work because you are using j as an index in the overall sentence and as an index in a single word. You never reset j.
Let's say the first word is
apple
Once you encounter a space, you will have:
j = 5
temp[0] = "apple"
Now you increment k to 1 but j stays the same so you will start storing characters of the next word from position 5 instead of 0:
temp[1][5] = string[5];
Instead of:
temp[1][0] = string[5];
Therefore, you have 3 indexes to worry about:
Index a that iterates over the input string.
Index b that iterates over a single word of the string.
Index c that iterates over the array of words.
The code:
int a, b, c;
for(a = 0, b = 0, c = 0; a < i; a++) { // index i holds the total number of chars in input string
if(string[a] != ' ') {
temp[c][b] = string[a];
b++;
} else {
temp[c][b] = '/0'; // add terminating character to current word
b = 0;
c++;
}
}
info.argc = nWords;
info.argv = temp;
return info;
Pretty sure this is what you were after. This should only require scanning the string once. Your index math has several issues:
Your calculation of i is inefficient.
The hoops nWords seems to go through questionable
You don't seem to be interested in terminating each word, which is very bad.
That said, walk through the following very carefully in a debugger to see how it works.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
argsInfo getArgsInfo(const char * s)
{
argsInfo info = {0,NULL};
while (*s)
{
// find start of next word
while (*s && isspace((unsigned char)*s))
++s;
// find end of next word
const char *beg = s;
while (*s && !isspace((unsigned char)*s))
++s;
if ((s - beg) > 0)
{
char **tmp = realloc(info.argv, (info.argc+1)*sizeof(*tmp));
if (tmp)
{
info.argv = tmp;
tmp[info.argc] = malloc((s - beg + 1) * sizeof(char));
if (tmp[info.argc] != NULL)
{
memcpy(tmp[info.argc], beg, s-beg);
tmp[info.argc++][s-beg] = 0; // <<= TERMINATE
}
else
{
perror("Failed to allocate string");
exit(EXIT_FAILURE);
}
}
else
{
perror("Failed to expand string pointer array");
exit(EXIT_FAILURE);
}
}
}
return info;
}
for example, given the string str1 = "120jdvj00ncdnv000ndnv0nvd0nvd0" and the character ch = '0', the output should be 12jdvj00ncdnv000ndnvnvdnvd. That is, the 0 is removed only wherever it occurs singly.
this code is not working
#include<stdio.h>
char remove1(char *,char);
int main()
{
char str[100]="1o00trsg50nf0bx0n0nso0000";
char ch='0';
remove1(str,ch);
printf("%s",str);
return 0;
}
char remove1(char* str,char ch)
{
int j,i;
for(i=0,j=0;i<=strlen(str)-1;i++)
{
if(str[i]!=ch)
{
if(str[i+1]==ch)
continue;
else
str[j++]=str[i];
}
}
str[j]='\0';
}
Your code looks for an occurrence of something other than the character to be removed with "if(str[i]!=ch)", then if the next character is the one to be removed it skips (i.e. does not keep the characters it has just seen), otherwise it copies the current character. So if it sees 'a0' and is looking for '0' it will ignore the 'a'.
What you could do is copy all characters other than the one of interest and set a counter to 0 each time you see one of them (for the number of contiguous character of interest you've seen at this point). When you find the one of interest increment that count. Now whenever you find one that is not of interest, you do nothing if the count is 1 (as this is the single character you want to remove), or put that many instances of the interesting character into str if count > 1.
Ensure you deal with the case of the string ending with a contiguous run of the character to be removed, and you should be fine.
char *remove1(char* str, char ch){
char *d, *s;
for(d = s = str;*s;++s){
if(*s == ch){
if(s[1] == ch)
while(*s == ch)
*d++=*s++;
else
++s;//skip a ch
if(!*s)break;
}
*d++ = *s;
}
*d = '\0';
return str;
}
Code to copy the basic
for(d = s = str;*s;++s){
*d++ = *s;
}
*d = '\0';
Special processing to be added.
for(d = s = str;*s;++s){
if(find a character that is specified){
Copy that in the case of continuously than one character
if one letter then skip
}
*d++ = *s;
}
*d = '\0';
Here is the working code
output is : "1o00trsg5nfbxnnso0000"
#include<stdio.h>
char remove1(char *,char);
int main()
{
char str[100]="1o00trsg50nf0bx0n0nso0000";
char ch='0';
remove1(str,ch);
printf("%s",str);
return 0;
}
char remove1(char* str,char ch)
{
int j,i;
int len = strlen(str);
for(i = 0;i < (len - 1);i++){
if(str[i] == ch){
/* if either of check prev and next character is same then contd. without removal */
if((str[i+1] == ch) || (str[i-1] == ch))
continue;
/* replacing the char and shifting next chars left*/
for(j = i;j < (len - 2);j++) {
str[j] = str[j + 1];
}
/* string length is decrementing due to removal of one char*/
len--;
}
}
str[len] = '\0';
}
So for a lab at uni... Ive been challenged to find all words in the usr/share/dict/linux.words
file using fopen, fgets etc with every vowel only once, in order.
i.e. facetious
So far I have the following code... but its flawed somewhere...
int all_vowels( char *s )
{
const unsigned char *p = (const unsigned char *)s;
char *v = malloc(sizeof(char *));
char *vowel = v;
if(*p == '\0') return -1;
while( *p != '\0' )
{
if( *p == 'a' || *p =='e' || *p =='i'|| *p =='o' || *p =='u' )
{
*v = *p;
v++;
}
p++;
}
if ( *vowel == 'a' && (*vowel + 1) == 'e' && (*vowel + 2) == 'i' && (*vowel + 3) == 'o' && (*vowel + 4) == 'u' )
{
return 1;
}
return -1;
}
int main (int argc, char *argv[])
{
FILE *file;
char line[BUFSIZ];
if (( file = fopen("/usr/share/dict/words", "r") ) == NULL)
{
fprintf(stderr, "cannot open %s\n", "/usr/share/dict/words");
exit(1);
}
while ( !feof(file) )
{
fgets(line, sizeof(line), file);
if ( all_vowels(line) == 1 )
{
printf("%s\n", line);
}
}
fclose(file);
return 0;
}
Any tips would be great!!!
Im really confused at the moment...
You are accessing v as if it were pointing to a location holding a number of characters, when indeed you only reserve space for one single char * (usually 4 byte on a 32 bit machine and 8 byte on a 64 bit machine):
char *v = malloc(sizeof(char *));
That might or might not be enough for what you are trying to store in it; in your case, the number of vowels, in any given word.
Whenever possible, you should avoid dynamic allocations; in your case, you don't need them, you can declare an array of fixed size instead of a char*:
char v[5];
In addition to that, you have to check if you have read 5 vowels already, so that you don't exceed the array size; if, after 5 vowels, you encounter another one, you can stop the check anyway; the currently encountered one has to be a duplicate vowel, the word will therefore not qualify.
The way you address characters is also a problem. Check again what * does: it dereferences the expression immediately to the right. In your case, it will always dereference v, then add something to it (which is also legal, since the result of dereferencing is a char). So if the first character where v points to is an a, the second an e, then *v will yield 'a', (*v + 1) will yield 'b', (*v +2) will yield 'c' and so on - you see, the result is an addition to the letter a by the given number; it doesn't matter at all what comes after the first character because you never access the value there. To achieve what you want with pointer arithmetic, you'd have to use parenthesis: *(v+1) - i.e., add 1 to the pointer v, then dereference it. This would yield the second character in the c string starting at v, i.e. 'e'.
Note that with v declared as above you can simply write v[0], v[1], v[2] and so on to address each character.
Aside from that, check the last comparison in your if condition, you had an 'e' instead of an 'u' there.
By the way, as a side note, and something to think about: There is a solution to your problem which does not require the v/vowel variables at all... only a single integer variable!
but its flawed somewhere...
Could there be an error here?
if ( *vowel == 'a' &&
(*vowel + 1) == 'e' &&
(*vowel + 2) == 'i' &&
(*vowel + 3) == 'o' &&
(*vowel + 4) == 'e' )
// ^^^ 'u'?
There may also be other errors. I haven't checked all your code.
Here is a big flaw:
char *v = malloc(sizeof(char *));
This only allocates four or eight bytes (depending on if you are on a 32 or 64 bit platform). I'm guessing you want a little more than that.
PS. In the future, you should probably try to be more specific instead of just saying that "it's flawed".
Why is all_vowels() allocating memory? And, even more interesting, why doesn't it free() it?
I'm pretty sure all_vowels() doesn't have to allocate any memory, and can be a bit simpler than what you have.
Also, you can´t use feof() before trying to read from the file. Remove that, just loop until fgets() returns NULL.
I would probably write a helper function int is_vowel(char c); to make the code clearer, and then attack the problem like so in all_vowels():
vowels = "aeiou"
for each character x in the string to check:
if is_vowel(x):
if vowels starts with x:
let vowels = vowels[1:]
else
return false
return true if vowels is empty
Okay.. So I finally got the right output... Any hints or tips for greater efficiency would be much appreciated.
int all_vowels( char *s )
{
const unsigned char *p = (const unsigned char *)s;
char v[5];
int i = 0;
if(*p == '\0') return -1;
while( *p != '\0' )
{
if( (*p == 'a' || *p =='e' || *p =='i'|| *p =='o' || *p =='u') && ( i < 5 ) )
{
v[i] = *p;
i++;
}
p++;
}
if ( ( v[0] == 'a' && v[1] == 'e' && v[2] == 'i' && v[3] == 'o' && v[4] == 'u' ) && (strlen(v) == 5 ))
{
return 1;
}
return -1;
}
int main (int argc, char *argv[])
{
FILE *file;
char line[30];
if (( file = fopen("/usr/share/dict/words", "r") ) == NULL)
{
fprintf(stderr, "cannot open %s\n", "/usr/share/dict/words");
exit(1);
}
while ( fgets(line, sizeof(line), file) )
{
if ( all_vowels(line) == 1 )
{
printf("%s\n", line);
}
}
fclose(file);
return 0;
}
little unsure regarding parseing your file yet i function(s) below check that a character is a vowel and tests whether the next vowel is greater then the current vowel.
#include <stdio.h>
// for readability not advocating the
// usage of #define booleans etc
#define TRUE 1
#define FALSE 0
int isVowel (char c)
{
switch (c)
{
case 'a': return TRUE;
case 'e': return TRUE;
case 'i': return TRUE;
case 'o': return TRUE;
case 'u': return TRUE;
case 'A': return TRUE;
case 'E': return TRUE;
case 'I': return TRUE;
case 'O': return TRUE;
case 'U': return TRUE;
}
return FALSE;
}
int hasOrderedVowels (char *str)
{
char c1, c2;
c1 = *str;
c2 = *(++str);
// ignore words beginning in vowels other then 'a' or 'A'
if (isVowel(c1) && !(c1 == 'a' || c1 == 'A')) return FALSE;
do {
// ignore case of `c1`
if (c1 >= 'a')
c1 -= 32;
// ignore case of `c2`
if (c2 >= 'a')
c2 -= 32;
// compare vowels and increment
// pointers as appropriate
if (isVowel(c1) && isVowel(c2))
{
// if we have found a vowel less then or equal to current
// then they are not in order/more then one, if we have found
// a 'U' and there are more vowels then this would be a duplicate
if (c2 <= c1 || c1 == 'U')
return FALSE;
c1 = c2;
}
else if (isVowel(c2)) // found first vowel so assign to c1
{
if (!(c1 == 'a' || c1 == 'A'))
{
return FALSE;
}
c1 = c2;
}
else if (!isVowel(c1))
{
c1 = *(str += 2); // skip over c2
}
c2 = *(++str);
}
while (c2 != '\0');
return (c1 == 'U');
}
int main ()
{
char *str[] = {"aeiou", "facecious", "chimpanze", "baboon"};
int i = 0;
for (; i<5; i++)
{
printf ("%s: %i\n", str[i], hasOrderedVowels(str[i]));
}
return 0;
}
demo