I wrote the following code
#include<stdio.h>
int main(void)
{
int i, max = 0, count = 0, j;
char str[] = "I'm a programmer";
for(i = 0; i < str[i]; i++)
{
if (str[i] != ' ')
count++;
else
{
if (max < count)
{
j = i - count;
max = count;
}
count = 0;
}
}
for(i = j; i < j + max; i++)
printf("%c", str[i]);
return 0;
}
With the intention to find and print the longest word, but does not work when the longest word this in the last as I'm a programmer I printed I'm instead of programmer
How to solve this problem, someone gives me a hand
The terminating condition of your for loop is wrong. It should be:
for(i = 0; i < strlen(str) + 1; i++)
and also, since at the end of string you don't have a ' ', but you have a '\0', you should change:
if (str[i] != ' ')
to:
if (str[i] != ' ' && str[i] != '\0')
The issue should be rather obvious. You only update your longest found word when the character you are inspecting is a space. But there is no space after the longest word in your test string, thus updating code is never exeuted for it.
You can just plop this code after the loop and that should do the trick.
Note however you could have trivially found this by adding mere printfs showing progress of this function.
Related
This question already has answers here:
How to check duplicate words in a string in C?
(2 answers)
Closed 1 year ago.
I have written a code in c to search for duplicate words in a string, It just appends every word in a string to a 2d string array, but it is returning 0 for the numbers of rows and duplicate strings, what is the problem with the code?
int main() {
char str[50] = "C code find duplicate string";
char str2d[10][50];
int count = 0;
int row = 0, column = 0;
for (int i = 0; str[i] != '\0'; i++) {
if (str[i] != '\0' || str[i] != ' ') {
str2d[row][column] = str[i];
column += 1;
} else {
str2d[row][column] = '\0';
row += 1;
column = 0;
}
}
for (int x = 0; x <= row; x++) {
for (int y = x + 1; y <= row; y++) {
if (strcmp(str2d[x], str2d[y]) == 0 && (strcmp(str2d[y], "0") != 0)) {
count += 1;
}
}
}
printf("%i %i", row, count);
return 0;
}
There are multiple problems in your code:
the 2D array might be too small: there could be as many as 25 words in a 50 byte string, and even more if you consider sequences of spaces to embed empty words.
the test if (str[i] != '\0' || str[i] != ' ') is always true.
the last word in the string is not null terminated in the 2D array.
the word at str2d[row] is uninitialized if the string ends with a space
sequences of spaces cause empty words to be stored into the 2D array.
there is no point in testing strcmp(str2d[y], "0"). This might be a failed attempt at ignoring empty words, which could be tested with strcmp(str2d[y], "").
Here is a modified version:
#include <stdio.h>
#include <string.h>
int main() {
char str[50] = "C code find duplicate string";
char str2d[25][50];
int count = 0, row = 0, column = 0;
for (int i = 0;;) {
// skip initial spaces
while (str[i] == ' ')
i++;
if (str[i] == '\0')
break;
// copy characters up to the next space or the end of the string
while (str[i] != ' ' && str[i] != '\0')
str2d[row][column++] = str[i++];
str2d[row][column] = '\0';
row++;
}
for (int x = 0; x < row; x++) {
for (int y = x + 1; y < row; y++) {
if (strcmp(str2d[x], str2d[y]) == 0)
count += 1;
}
}
printf("%i %i\n", row, count);
return 0;
}
The problems are:
if (str[i] != '\0' || str[i] != ' ') should be if (str[i] != '\0' && str[i] != ' '). If I recall right, using the logical or will prevent reaching the else case.
if (strcmp(str2d[x], str2d[y]) == 0 && (strcmp(str2d[y], "0") != 0)) should be if (strcmp(str2d[x], str2d[y]) == 0). Otherwise, your code will not count duplicates when the word is "0".
a. To avoid confusion, use something like printf("Number of rows = %d, Number of duplicates = %d\n", row+1, count);. Since C arrays start at index 0, that's what row in your code contains. But the number of rows is 1.
b. If you haven't realised by now, there are no duplicates in your str variable: char str[50] = "C code find duplicate string";. So your code returns a correct value of 0. Change it to char str[50] = "C code find duplicate duplicate"; (for example), your code will correctly return 1.
I'm trying to find the shortest word in a given C string, but somehow it fails if there is only one word or if it's the last word.
I tried start at the null character and count backwards until I hit " " and than count the steps I took but it did not work properly.
Also is there a better or faster way to iterate through a string while finding the shortest or longest word?
#include <sys/types.h>
#include <string.h>
#include <stdio.h>
ssize_t find_short(const char *s) {
int min = 100;
int count = 0;
for (int i = 0 ; i < strlen(s); i++) {
if (s[i] != ' ') {
count++;
} else {
if (count < min) {
min = count;
}
count = 0;
}
if (s[i] == '\0') {
count = 0;
while (s[i] != ' ') {
--i;
count++;
if (s[i] == ' ' && count < min) {
min = count;
}
}
}
}
return min;
}
Your idea was correct you just complicated a little. Let us break down your code:
int min = 100;
First you should initialized min to INT_MAX which you can get it from #include <limits.h>. Maybe all words are bigger than 100.
for (int i = 0 ; i < strlen(s); i++)
you can use the C-String terminal character '\0':
for(int i = 0; s[i] != '\0'; i++)
The if and else part:
if (s[i] != ' ')
{
count++;
}
else
{
if (count < min)
{
min = count;
}
count = 0;
}
almost correct, but you need to append count != 0 to the condition count < min to ensure that if the string starts with spaces, you do not want to count them as the smallest word.
This part can be removed :
if (s[i] == '\0')
{
count = 0;
while(s[i] != ' ')
{
--i;
count++;
if(s[i] == ' ' && count < min)
{
min = count;
}
}
}
check the last word outside the loop. Hence, your code would look like the following:
ssize_t find_short(const char *s)
{
int min = INT_MAX;
int count = 0;
// Iterate over the string
for(int i = 0; s[i] != '\0'; i++){
if(s[i] != ' '){ // Increment the size of the word find so far
count++;
}
else{ // There is a space meaning new word
if(count != 0 && count < min){ // Check if current word is the smallest
min = count; // Update the counter
}
count = 0; // Set the counter
}
}
return (count < min) ? count : min // Check the size of the last word
}
There are multiple issues in your code:
mixing int, size_t and ssize_t for the index and string lengths and return value is confusing and incorrect as these types have a different range.
int min = 100; produces an incorrect return value if the shortest word is longer than that.
for (int i = 0 ; i < strlen(s); i++) is potentially very inefficient as the string length may be recomputed at every iteration. for (size_t i = 0 ; s[i] != '\0'; i++) is a better alternative.
find_short returns 100 for the empty string instead of 0.
Scanning backwards from the end is tricky and not necessary: to avoid this special case, omit the test in the for loop and detect the end of word by comparing the character with space or the null byte, breaking from the loop in the latter case after potentially updating the minimum length.
The initial value for min should be 0 to account for the case where the string is empty or contains only whitespace. Whenever a word has been found, min should be updated if it is 0 or if the word length is non zero and less than min.
Here are an implementation using <ctype.h> to test for whitespace:
#include <stddef.h>
#include <ctype.h>
size_t find_short(const char *s) {
size_t min = 0, len = 0;
for (;;) {
unsigned char c = *s++;
if (isspace(c) || c == '\0') {
if (min == 0 || (len > 0 && len < min))
min = len;
if (c == '\0') // test for loop termination
break;
len = 0;
} else {
len++;
}
}
return min;
}
Here is a more general alternative using the functions strcspn() and strspn() from <string.h> where you can define the set of word separators:
#include <string.h>
size_t find_short(const char *s) {
size_t min = 0;
const char *seps = " \t\r\n"; // you could add dashes and punctuation
while (*s) {
s += strspn(s, seps);
if (*s) {
size_t len = strcspn(s, seps);
if (min == 0 || (len > 0 && len < min))
min = len;
s += len;
}
}
return min;
}
As you said your program fails if the shortest word is the last one. That's because the last i for which the loop runs is i == len-1 that is the last letter of the string. So in this last lap, count will be incremented, but you will never check if this count of the last word was smaller than the min you had so far.
Assuming that you receive a null-terminated string, you could extend the loop till i <= len (where len = strlen(s)) and adjust the if condition to
if( s[i] != ' ' && s[i] )
That means: if s[i] is not a space nor the terminating null character
Also you can remove the condition if (s[i] == '\0').
About faster algorithms, I don't think it's possible to do better.
If you want you can automate the count increment using an inner empty for loop running till it finds a space and then in the outer for check for how long the innermost have been running.
I once wrote a program for the same problem which uses an inner for, I show you just for the algorithm, but don't take example from the "style" of the code, I was trying to make it as few lines as possible and that's not a good practice.
ssize_t find_short(const char *s)
{
ssize_t min = 99, i = 0;
for( --s; !i || (min > 1 && *s); s += i) {
for(i = 1; *(s+i) != ' ' && *(s+i); i++);
if( min > i-1 ) min = i-1;
}
return min;
}
Oh, one improvement I just noticed in my code could be to return the min when it reaches 1 because you know you are not going to find shorter words.
I'd suggest that you use strtok to split your string into an array of strings using space character as the token. You can then process each string in the resulting array to determine which is the "word" that you want.
See Split strings into tokens and save them in an array
Here's the overview: I have to make a cipher that can decrypt and encrypt messages(I have completed that), but the reason we have these ciphers are to encrypt/decrypt messages that are sentences which have whitespaces. Basically, I just need to know when there is whitespace, and when there is, to add that whitespace to the final output.
#TL;DR:# How to add whitespace to a String - char output[] ~~~~~~~~~~ that's the String
for(int j = 0; input[j] != '\0'; j++){
int at_index = 0;
int i = 0;
//need to check at this point for whitespace
if(input[j] == ' '){
output[j] = ' ';
}
//gives garbage value
for(i; alphabet[i] != input[j]; i++){
++at_index;
}
output[j] = alphabet[at_index + shift];
}
The error you made is that you don't stop the handling of the character after recognizing the whitespace.
This is one possible solution, derived from your source:
for (int j = 0; input[j] != '\0'; j++) {
if (isspace(input[j])) {
output[j] = input[j];
} else {
for (int i = 0; alphabet[i] != input[j]; i++) {
}
output[j] = alphabet[i + shift];
}
}
Some notes:
at_index is always the same value as i so it's not needed.
You need to add some logic if the input character is not in the alphabet. Since I don't know how your alphabet is defined, I can't show it here.
The for-loop can be replaced by some appropriate string function.
You need some logic to prevent an out-of-bounds access to alphabet when reading the shifted character.
I would call a helper function in order to achieve this,
white_spaces(char *dest, int size, int num_of_spaces) {
int len = strlen(dest);
// for the check i still assume dest tto contain a valid '\0' terminated string, so len will be smaller than size
if( len + num_of_spaces >= size ) {
num_of_spaces = size - len - 1;
}
memset( dest+len, ' ', num_of_spaces );
dest[len + num_of_spaces] = '\0';
}
My assignment is to allow the user to enter any input and print the occurrences of letters and words, we also have to print out how many one letter, two, three, etc.. letter words are in the string. I have gotten the letter part of my code to work and have revised my word function several times, but still can't get the word finding function to even begin to work. The compiler says the char pointer word is undeclared when it clearly is. Do I have to allocate memory to it and the array of characters?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void findLetters(char *ptr);
void findWords(char *point);
int main()
{
char textStream[100]; //up to 98 characters and '\n\ and '\0'
printf("enter some text\n");
if (fgets(textStream, sizeof (textStream), stdin)) //input up to 99 characters
{
findLetters(textStream);
findWords(textStream);
}
else
{
printf("fgets failed\n");
}
return 0;
}
void findLetters(char *ptr) //find occurences of all letters
{
int upLetters[26];
int loLetters[26];
int i;
int index;
for (i = 0; i < 26; i++) // set array to all zero
{
upLetters[i] = 0;
loLetters[i] = 0;
}
i = 0;
while (ptr[i] != '\0') // loop until prt[i] is '\0'
{
if (ptr[i] >= 'A' && ptr[i] <= 'Z') //stores occurrences of uppercase letters
{
index = ptr[i] - 'A';// subtract 'A' to get index 0-25
upLetters[index]++;//add one
}
if (ptr[i] >= 'a' && ptr[i] <= 'z') //stores occurrences of lowercase letters
{
index = ptr[i] - 'a';//subtract 'a' to get index 0-25
loLetters[index]++;//add one
}
i++;//next character in ptr
}
printf("Number of Occurrences of Uppercase letters\n\n");
for (i = 0; i < 26; i++)//loop through 0 to 25
{
if (upLetters[i] > 0)
{
printf("%c : \t%d\n", (char)(i + 'A'), upLetters[i]);
// add 'A' to go from an index back to a character
}
}
printf("\n");
printf("Number of Occurrences of Lowercase letters\n\n");
for (i = 0; i < 26; i++)
{
if (loLetters[i] > 0)
{
printf("%c : \t%d\n", (char)(i + 'a'), loLetters[i]);
// add 'a' to go back from an index to a character
}
}
printf("\n");
}
void findWords(char *point)
{
int i = 0;
int k = 0;
int count = 0;
int j = 0;
int space = 0;
int c = 0;
char *word[50];
char word1[50][100];
char* delim = "{ } . , ( ) ";
for (i = 0; i< sizeof(point); i++) //counts # of spaces between words
{
if ((point[i] == ' ') || (point[i] == ',') || (point[i] == '.'))
{
space++;
}
}
char *words = strtok(point, delim);
for(;k <= space; k++)
{
word[k] = malloc((words+1) * sizeof(*words));
}
while (words != NULL)
{
printf("%s\n",words);
strcpy(words, word[j++]);
words = strtok(NULL, delim);
}
free(words);
}
This is because you are trying to multiply the pointer position+1 by the size of pointer. Change line 100 to:
word[k] = malloc(strlen(words)+1);
This will solve your compilation problem, but you still have other problems.
You've got a couple of problems in function findWords:
Here,
for (i = 0; i< sizeof(point); i++)
sizeof(point) is the same as sizeof(char*) as point in a char* in the function fincdWords. This is not what you want. Use
for (i = 0; i < strlen(point); i++)
instead. But this might be slow as strlen will be called in every iteration. So I suggest
int len = strlen(point);
for (i = 0; i < len; i++)
The same problem lies here too:
word[k] = malloc((words+1) * sizeof(*words));
It doesn't makes sense what you are trying with (words+1). I think you want
word[k] = malloc( strlen(words) + 1 ); //+1 for the NUL-terminator
You got the arguments all mixed up:
strcpy(words, word[j++]);
You actually wanted
strcpy(word[j++], words);
which copies the contents of words to word[j++].
Here:
free(words);
words was never allocated memory. Since you free a pointer that has not been returned by malloc/calloc/realloc, the code exhibits Undefined Behavior. So, remove that.
You allocated memory for each element of word. So free it using
for(k = 0; k <= space; k++)
{
free(word[k]);
}
Your calculation of the pointer position+1 is wrong. If you want the compilation problem will go away change line 100 to:
word[k] = malloc( 1 + strlen(words));
I wrote a code that counts how many words are there in a sentence, but it does not work in cases like this for example:
"hello world."
It needs to return that there are 2 words, but it returns 4 because of the spaces. It's only good for the case of one space between each word. This is my code:
int counthowmanywordsinasentence(char sentence[])// help forfunc7
{
int count = 0, i;
for (i = 0;sentence[i] != '\0';i++)
{
if (sentence[i] == ' ')
count++;
}
return (count+1);
}
Use a flag. If you encounter a space & flag is not set, set the flag and increment count. If space is encountered & flag is set , just ignore that case. And if flag is set & char(i.e. sentence[i]) is not space, reset flag.
This is the simplest of all answers, just add 2 lines
#include <stdio.h>
int counthowmanywordsinasentence(char sentence[])// help forfunc7
{
int count = 0, i;
for (i = 0;sentence[i] != '\0';i++)
{
if (sentence[i] == ' ')
count++;
while (sentence[i] == ' ')
i++;
}
return (count+1);
}
You can safely replace your if by this new version:
if (sentence[i] == ' ' && sentence[i+1] != ' ')
Which means you will be only counting the last space in each space sequence. So in your case of 4 contiguous spaces, you will count only the last one.
You will still need to decide what to do in these two cases:
" hello world."
"hello world "
As you need to know if these should count as 2 or 3 words in both cases.
So sscanf already does what you need it will eat any number of whitespaces before a string including tabs. This algorithm is safe with leading or trailing spaces.
int countHowManyWordsInASentence(char* sentence){
int result = 0;
int i = 0;
while(sscanf(sentence, "%*s%n", &i) != EOF){
sentence += i;
result++;
}
return result;
}
sscanf is extremely versatile you can easily read out each word as follows:
int countHowManyWordsInASentence(char* sentence){
int result = 0;
int size = strlen(sentence);
if(size > 0){
char* word = (char*)malloc((size + 1) * sizeof(char));
for(int i = 0; sscanf(sentence, "%s%n", word, &i) > 0; sentence += i){
result++;
}
free(word);
}
return result;
}
int counthowmanywordsinasentence(char sentence[])
{
int count = 0, i;
char ch, pre = ' ';
for (i = 0; (ch=sentence[i]) != '\0'; i++, pre = ch)
{
if (pre == ' ' && ch != ' ')//reckon the rise
count++;
}
return count;
}
You'll have to decide what is a word first :) let's assume a word is any sequence of characters with at least one alphabetic character (A-Za-z). then you can follow #Abhilash's suggestion to complete your code.
int wordcount(char *sentence) {
int count = 0;
int is_word = 0;
int i;
for(i=0; sentence[i]!='\0'; i++) {
if(isalpha(sentence[i])) {
is_word = 1;
}
if(sentence[i] == ' ' && is_word) {
count++;
is_word = 0;
}
}
return count + is_word;
}