Related
I received an assignment to write a code that would erase the instances of a string in another string, and although my code does that successfully, the symbol ╠ appears many times at the end of the result string.
Example:
For input string 1 - A string is a string, and an input string 2 - str
The result should be A ing is a ing.
But I receive A ing is a ing╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
Hoped I could get some assistance regarding this issue, cause no matter what I've tried I wasn't able to
fix this.
#include <stdio.h>
#define STRING_SIZE 100
int StrippingFunc(char input_str1[STRING_SIZE], char input_str2[STRING_SIZE], char
result_string[STRING_SIZE])
{
if (input_str2[0] == '\n' || input_str2[0] == '\0')
{
return 0;
}
for (int k1 = 0; k1 < STRING_SIZE; k1++)
{
if (input_str1[k1] == '\n')
{
input_str1[k1] = '\0';
}
}
for (int k2 = 0; k2 < STRING_SIZE; k2++)
{
if (input_str2[k2] == '\n')
{
input_str2[k2] = '\0';
}
}
int Length;
int length2 = 0;
int index2 = 0;
while (input_str2[index2] != '\0') // Loop used to determine input_string2's length.
{
length2++;
index2++;
}
int InString = 0;
int i = 0;
int j;
int resultindex = 0;
while (input_str1[i] != '\0')
{
Length = length2;
int l = i;
j = 0;
int proceed = 1;
if (input_str1[l] == input_str2[j])
{
while ((input_str2[j] != '\0') && (proceed != 0))
{
while (Length >= 0)
{
if (Length == 0)
{
InString = 1;
i += (l-i-1);
proceed = 0;
Length = -1;
}
if (input_str1[l] == input_str2[j])
{
Length--;
j++;
l++;
}
else if ((input_str1[l-1] == input_str2[j-1]) && (input_str2[j] == '\0'))
{
proceed = 0;
Length = -1;
}
else
{
proceed = 0;
Length = -1;
result_string[resultindex] = input_str1[l - 1];
resultindex++;
}
}
}
}
else
{
result_string[resultindex] = input_str1[i];
resultindex++;
}
i++;
}
return InString;
}
int main()
{
char result_string[STRING_SIZE];
char input_string1[STRING_SIZE];
char input_string2[STRING_SIZE];
printf("Please enter the main string..\n");
// Your function call here..
fgets(input_string1, STRING_SIZE + 1, stdin);
printf("Please enter the pattern string to find..\n");
// Your function call here..
fgets(input_string2, STRING_SIZE + 1, stdin);
int is_stripped = StrippingFunc(input_string1, input_string2, result_string);; // Your function call here..
// Store the result in the result_string if it exists
printf("> ");
printf(is_stripped ? result_string : "Cannot find the pattern in the string!");
return 0;
}
But I receive A ing is a ing╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
In the code after you fill result_string but you missed to add the final null character, because of that the printf after reach non initialized characters with an undefined behavior producing your unexpected writting. After
while (input_str1[i] != '\0')
{
Length = length2;
...
}
add
result_string[resultindex] = 0;
note you have the place for because result_string and input_str1 have the same size
Having
char input_string1[STRING_SIZE];
char input_string2[STRING_SIZE];
these two lines can have an undefined behavior :
fgets(input_string1, STRING_SIZE + 1, stdin);
fgets(input_string2, STRING_SIZE + 1, stdin);
because fgets may write after the end of the arrays, you need to remove +1 or to size the arrays one more
In
for (int k1 = 0; k1 < STRING_SIZE; k1++)
{
if (input_str1[k1] == '\n')
{
input_str1[k1] = '\0';
}
}
for (int k2 = 0; k2 < STRING_SIZE; k2++)
{
if (input_str2[k2] == '\n')
{
input_str2[k2] = '\0';
}
}
except if fgets fill all the arrays you have an undefined behavior working on non initialized characters because you do not stop when you reach newline or the null character.
In
int length2 = 0;
int index2 = 0;
while (input_str2[index2] != '\0') // Loop used to determine input_string2's length.
{
length2++;
index2++;
}
length2 and length2 have exactly the same value, is it useless to have two variables, and in fact this lop is useless because the previous loop with the right termination already give you the expected length.
In
printf(is_stripped ? result_string : "Cannot find the pattern in the string!");
I encourage you to replace printf by a puts not only to add a final newline to flush the output and make it more clear in case you start your program in a shell, but also because in case the input string contains for instance %d and it is not removed and is_stripped is true then printf will try to get an argument whose do not exist, with an undefined behavior
If you do all the corrections with your inputs your code will print > A ing is a ing without undefined behavior
Write a program to "fold" long input lines into two or more shorter lines after the last non-blank character that occurs before the n-th column of input. Make sure your program does something intelligent with very long lines, and if there are no blanks or tabs before the specified column.
The algorithm I decided to follow for this was as follows:
If length of input line < maxcol (the column after which one would have to fold), then print the line as it is.
If not, from maxcol, I check towards it's left, and it's right to find the closest non-space character, and save them as 'first' and 'last'. I then print the character array from line[0] to line[first] and then the rest of the array, from line[last] to line[len] becomes the new line array.
Here's my code:
#include <stdio.h>
#define MAXCOL 5
int getline1(char line[]);
int main()
{
char line[1000];
int len, i, j, first, last;
len = getline1(line);
while (len > 0) {
if (len < MAXCOL) {
printf("%s\n", line);
break;
}
else {
for (i = MAXCOL - 1; i >= 0; i--) {
if (line[i] != ' ') {
first = i;
break;
}
}
for (j = MAXCOL - 1; j <= len; j++) {
if (line[j] != ' ') {
last = j;
break;
}
}
//printf("first %d last %d\n", first, last);
for (i = 0; i <= first; i++)
putchar(line[i]);
putchar('\n');
for (i = 0; i < len - last; i++) {
line[i] = line[last + i];
}
len -= last;
first = last = 0;
}
}
return 0;
}
int getline1(char line[])
{
int c, i = 0;
while ((c = getchar()) != EOF && c != '\n')
line[i++] = c;
if (c == '\n')
line[i++] = '\n';
line[i] = '\0';
return i;
}
Here are the problems:
It does not do something intelligent with very long lines (this is fine, as I can add it as an edge case).
It does not do anything for tabs.
I cannot understand a part of the output.
For example, with the input:
asd de def deffff
I get the output:
asd
de
def
defff //Expected until here
//Unexpected lines below
ff
fff
deffff
deffff
deffff
Question 1 - Why do the unexpected lines print? How do I make my program/algorithm better?
Eventually, after spending quite some time with this question, I gave up and decided to check the clc-wiki for solutions. Every program here did NOT work, save one (The others didn't work because they did not cover certain edge cases). The one that worked was the largest one, and it did not make any sense to me. It did not have any comments, and neither could I properly understand the variable names, and what they represented. But it was the ONLY program in the wiki that worked.
#include <stdio.h>
#define YES 1
#define NO 0
int main(void)
{
int TCOL = 8, ch, co[3], i, COL = 19, tabs[COL - 1];
char bls[COL - 1], bonly = YES;
co[0] = co[1] = co[2] = 0;
while ((ch = getchar()) != EOF)
{
if (ch != '\t') {
++co[0];
++co[2];
}
else {
co[0] = co[0] + (TCOL * (1 + (co[2] / TCOL)) - co[2]);
i = co[2];
co[2] = TCOL + (co[2] / TCOL) * TCOL;
}
if (ch != '\n' && ch != ' ' && ch != '\t')
{
if (co[0] >= COL) {
putchar('\n');
co[0] = 1;
co[1] = 0;
}
else
for (i = co[1]; co[1] > 0; --co[1])
{
if (bls[i - co[1]] == ' ')
putchar(bls[i - co[1]]);
else
for (; tabs[i - co[1]] != 0;)
if (tabs[i - co[1]] > 0) {
putchar(' ');
--tabs[i - co[1]];
}
else {
tabs[i - co[1]] = 0;
putchar(bls[i - co[1]]);
}
}
putchar(ch);
if (bonly == YES)
bonly = NO;
}
else if (ch != '\n')
{
if (co[0] >= COL)
{
if (bonly == NO) {
putchar('\n');
bonly = YES;
}
co[0] = co[1] = 0;
}
else if (bonly == NO) {
bls[co[1]] = ch;
if (ch == '\t') {
if (TCOL * (1 + ((co[0] - (co[2] - i)) / TCOL)) -
(co[0] - (co[2] - i)) == co[2] - i)
tabs[co[1]] = -1;
else
tabs[co[1]] = co[2] - i;
}
++co[1];
}
else
co[0] = co[1] = 0;
}
else {
putchar(ch);
if (bonly == NO)
bonly = YES;
co[0] = co[1] = co[2] = 0;
}
}
return 0;
}
Question 2 - Can you help me make sense of this code and how it works?
It fixes all the problems with my solution, and also works by reading character to character, and therefore seems more efficient.
Question 1 - Why do the unexpected lines print? How do I make my program/algorithm better?
You are getting the unexpected lines in the output because after printing the array, you are not terminating the new line array with null character \0 -
Here you are copying character from starting from last till len - last, creating a new line array:
for (i = 0; i < len - last; i++) {
line[i] = line[last + i];
}
You have copied the characters but the null terminating character is still at its original position. Assume the input string is:
asd de def deffff
So, initially the content of line array will be:
"asd de def deffff\n"
^
|
null character is here
Now after printing asd, you are copying characters from last index of line till len - last index to line array itself starting from 0 index. So, after copying the content of line array will be:
"de def deffff\n deffff\n"
|____ _____|
\/
This is causing the unexpected output
(null character is still at the previous location)
So, after for loop you should add the null character just after the last character copied, like this:
line [len - last] = '\0';
With this the content of line array that will be processed in the next iteration of while loop will be:
"de def deffff\n"
One more thing, in the line array you can see the \n (newline) character at the end. May you want to remove it before processing the input, you can do:
line[strcspn(line, "\n")] = 0;
Improvements that you can do in your program:
1. One very obvious improvement that you can do is to use pointer to the input string while processing it. With the help of pointer you don't need to copy the rest of the array, apart from processed part, again to the same array till the program process the whole input. Initialize the pointer to the start of the input string and in every iteration just move the pointer to appropriate location and start processing from that location where pointer is pointing to.
2. Since you are taking the whole input first in a buffer and then processing it. You may consider fgets() for taking input. It will give better control over the input from user.
3. Add a check for line array overflow, in case of very long input. With fgets() you can specify the maximum number of character to be copied to line array from input stream.
Question 2 - Can you help me make sense of this code and how it works?
The program is very simple, try to understand it at least once by yourself. Either use a debugger or take a pen and paper, dry run it once for small size input and check the output. Increase the input size and add some variations like multiple space characters and check the program code path and output. This way you can understand it very easily.
Here's another (and I think better) solution to this exercise :
#include <stdio.h>
#define MAXCOL 10
void my_flush(char buf[]);
int main()
{
int c, prev_char, i, j, ctr, spaceleft, first_non_space_buf;
char buf[MAXCOL+2];
prev_char = -1;
i = first_non_space_buf = ctr = 0;
spaceleft = MAXCOL;
printf("Just keep typing once the output has been printed");
while ((c = getchar()) != EOF) {
if (buf[0] == '\n') {
i = 0;
my_flush(buf);
}
//printf("Prev char = %c and Current char = %c and i = %d and fnsb = %d and spaceleft = %d and j = %d and buf = %s \n", prev_char, c, i, first_non_space_buf, spaceleft, j, buf);
if ((((prev_char != ' ') && (prev_char != '\t') && (prev_char != '\n')) &&
((c == ' ') || (c == '\t') || (c == '\n'))) ||
(i == MAXCOL)) {
if (i <= spaceleft) {
printf("%s", buf);
spaceleft -= i;
}
else {
putchar('\n');
spaceleft = MAXCOL;
for (j = first_non_space_buf; buf[j] != '\0'; ++j) {
putchar(buf[j]);
++ctr;
}
spaceleft -= ctr;
}
i = 0;
my_flush(buf);
buf[i++] = c;
first_non_space_buf = j = ctr = 0;
}
else {
if (((prev_char == ' ') || (prev_char == '\t') || (prev_char == '\n')) &&
((c != ' ') && (c != '\t') && (c != '\n'))) {
first_non_space_buf = i;
}
buf[i++] = c;
buf[i] = '\0';
}
prev_char = c;
}
printf("%s", buf);
return 0;
}
void my_flush(char buf[])
{
int i;
for (i = 0; i < MAXCOL; ++i)
buf[i] = '\0';
}
Below is my solution, I know the thread is no longer active but my code might help someone who's facing issues to grasp the already presented code snippets.
*EDIT
explaination
Keep reading input unless the input contains '\n', '\t' or there've been
atleast MAXCOl chars.
Incase of '\t', use expandTab to replace with required spaces and use printLine if it doesn't exceed MAXCOl.
Incase of '\n', directly use printLine and reset the index.
If index is 10:
find the last blank using findBlank ad get a new index.
use printLine to print the current line.
get new index as 0 or index of newly copied char array using the newIndex function.
code
/* fold long lines after last non-blank char */
#include <stdio.h>
#define MAXCOL 10 /* maximum column of input */
#define TABSIZE 8 /* tab size */
char line[MAXCOL]; /* input line */
int expandTab(int index);
int findBlank(int index);
int newIndex(int index);
void printLine(int index);
void main() {
int c, index;
index = 0;
while((c = getchar()) != EOF) {
line[index] = c; /* store current char */
if (c == '\t')
index = expandTab(index);
else if (c == '\n') {
printLine(index); /* print current input line */
index = 0;
} else if (++index == MAXCOL) {
index = findBlank(index);
printLine(index);
index = newIndex(index);
}
}
}
/* expand tab into blanks */
int expandTab(int index) {
line[index] = ' '; /* tab is atleast one blank */
for (++index; index < MAXCOL && index % TABSIZE != 0; ++index)
line[index] = ' ';
if (index > MAXCOL)
return index;
else {
printLine(index);
return 0;
}
}
/* find last blank position */
int findBlank(int index) {
while( index > 0 && line[index] != ' ')
--index;
if (index == 0)
return MAXCOL;
else
return index - 1;
}
/* re-arrange line with new position */
int newIndex(int index) {
int i, j;
if (index <= 0 || index >= MAXCOL)
return 0;
else {
i = 0;
for (j = index; j < MAXCOL; ++j) {
line[i] = line[j];
++i;
}
return i;
}
}
/* print line until passed index */
void printLine(int index) {
int i;
for(i = 0; i < index; ++i)
putchar(line[i]);
if (index > 0)
putchar('\n');
}
I basically have a sentence in a string and want to break it down word per word. Every word should go into an array of strings. I am not allowed to use strtok. I have this code but it doesn't work. Can someone help?
There is for sure something similar in the internet but I couldn't find anything...
int main(){
char s[10000]; // sentence
char array[100][100]; // array where I put every word
printf("Insert sentence: "); // receive the sentence
gets(s);
int i = 0;
int j = 0;
for(j = 0; s[j] != '\0'; j++){ // loop until I reach the end
for(i = 0; s[i] != ' '; i++){ // loop until the word is over
array[j][i] = s[i]; // put every char in the array
}
}
return 0;
}
Every word should go into an array of strings. I am not allowed to use
strtok.
Interesting problem which could be resolved in a compact algorithm.
It handles multiple spaces and punctuation marks specified in check(char c).
The most difficult part of the problem is to properly handle corner cases. We may have situation when words are longer more than WORD_LEN length or the number of words exceeds the capacity of the array.
Both cases are properly handled. The algorithm truncates the excessive words and parses only to the capacity of the array.
(BTW. Do not use gets: Why is the gets function so dangerous that it should not be used?)
Edit: The fully tested find_tokens function has been presented.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define WORD_LEN 3 // 100 // MAX WORD LEN
#define NR_OF_WORDS 3 // 100 // MAX NUMBER OF WORDS
#define INPUT_SIZE 10000
int is_delimiter(const char * delimiters, char c) // check for a delimiter
{
char *p = strchr (delimiters, c); // if not NULL c is separator
if (p) return 1; // delimeter
else return 0; // not a delimeter
}
int skip(int *i, char *str, int skip_delimiters, const char *delimiters)
{
while(1){
if(skip_delimiters) {
if( (str[(*i)+1] =='\0') || (!is_delimiter(delimiters, str[(*i)+1])) )
break; // break on nondelimeter or '\0'
else (*i)++; // advance to next character
}
else{ // skip excess characters in the token
if( is_delimiter(delimiters, str[(*i)]) )
{
if( (str[(*i)+1] =='\0') || !is_delimiter(delimiters, str[(*i)+1]) )
break; // break on non delimiter or '\0'
else (*i)++; // skip delimiters
}
else (*i)++; // skip non delimiters
}
}
if ( str[(*i)+1] =='\0') return 0;
else return 1;
}
int find_tokens(int max_tokens, int token_len, char *str, char array[][token_len+1], const char *delimiters, int *nr_of_tokens)
{
int i = 0;
int j = 0;
int l = 0;
*nr_of_tokens = 0;
int status = 0; // all OK!
int skip_leading_delimiters = 1;
int token = 0;
int more;
for(i = 0; str[i] != '\0'; i++){ // loop until I reach the end
// skip leading delimiters
if( skip_leading_delimiters )
{
if( is_delimiter( delimiters, str[i]) ) continue;
skip_leading_delimiters = 0;
}
if( !is_delimiter(delimiters,str[i]) && (j < token_len) )
{
array[l][j] = str[i]; // put char in the array
//printf("%c!\n", array[l][j] );
j++;
array[l][j] = 0;
token = 1;
}
else
{
//printf("%c?\n", str[i] );
array[l][j] = '\0'; // token terminations
if (j < token_len) {
more = skip(&i, str, 1, delimiters); // skip delimiters
}
else{
more = skip(&i, str, 0, delimiters); // skip excess of the characters in token
status = status | 0x01; // token has been truncated
}
j = 0;
//printf("more %d\n",more);
if(token){
if (more) l++;
}
if(l >= max_tokens){
status = status | 0x02; // more tokens than expected
break;
}
}
}
if(l>=max_tokens)
*nr_of_tokens = max_tokens;
else{
if(l<=0 && token)
*nr_of_tokens = 1;
else
{
if(token)
*nr_of_tokens = l+1;
else
*nr_of_tokens = l;
}
}
return status;
}
int main(void){
char input[INPUT_SIZE+1]; // sentence
char array[NR_OF_WORDS][WORD_LEN+1]; // array where I put every word, remeber to include null terminator!!!
int number_of_words;
const char * delimiters = " .,;:\t"; // word delimiters
char *p;
printf("Insert sentence: "); // receive the sentence
fgets(input, INPUT_SIZE, stdin);
if ( (p = strchr(input, '\n')) != NULL) *p = '\0'; // remove '\n'
int ret = find_tokens(NR_OF_WORDS, WORD_LEN, input, array, delimiters, &number_of_words);
printf("tokens= %d ret= %d\n", number_of_words, ret);
for (int i=0; i < number_of_words; i++)
printf("%d: %s\n", i, array[i]);
printf("End\n");
return 0;
}
Test:
Insert sentence: ..........1234567,,,,,,abcdefgh....123::::::::::::
tokens= 3 ret= 1
0: 123
1: abc
2: 123
End
You are not '\0'-terminating the strings and you are scanning the source from
the beginning every time you've found a empty character.
You only need one loop and, the inner loop and the condition must be s[i] != 0:
int j = 0; // index for array
int k = 0; // index for array[j]
for(i = 0; s[i] != '\0'; ++i)
{
if(k == 99)
{
// word longer than array[j] can hold, aborting
array[j][99] = 0; // 0-terminating string
break;
}
if(j == 99)
{
// more words than array can hold, aborting
break;
}
if(s[i] == ' ')
{
array[j][k] = 0; // 0-terminating string
j++; // for the next entry in array
k = 0;
} else
array[j][k++] = s[i];
}
Note that this algorithm doesn't handle multiple spaces and punctuation marks.
This can be solved by using a variable that stores the last state.
int j = 0; // index for array
int k = 0; // index for array[j]
int sep_state = 0; // 0 normal mode, 1 separation mode
for(i = 0; s[i] != '\0'; ++i)
{
if(k == 99)
{
// word longer than array[j] can hold, aborting
array[j][99] = 0; // 0-terminating string
break;
}
if(j == 99)
{
// more words than array can hold, aborting
break;
}
// check for usual word separators
if(s[i] == ' ' || s[i] == '.' || s[i] == ',' || s[i] == ';' || s[i] == ':')
{
if(sep_state == 1)
continue; // skip multiple separators
array[j][k] = 0; // 0-terminating string
j++; // for the next entry in array
k = 0;
sep_state = 1; // enter separation mode
} else {
array[j][k++] = s[i];
sep_state = 0; // leave separation mode
}
}
As you can see, using the sep_state variable I'm able to check if multiple
separators come one after the other and skips subsequent separators. I also
check for common punctuation marks.
#include <stdio.h>
int main()
{
char s[10000]; // sentence
char array[100][100]; // array where i put every word
printf("Insert sentence: "); // receive the sentece
gets(s);
printf("%s",s);
int i = 0;
int j = 0;
int k = 0;
for(j = 0; s[j] != '\0'; j++){ // loop until i reach the end
if ( s[j] != ' ' || s[j] == '\0' )
{
array[i][k] = s[j];
k++;
}
else {
i++;
k = 0;
}
}
return 0;
}
please note that the gets function is very unsafe and shouldn't in any case be used, use scanf or fgets instead
My program is designed to allow the user to input a string and my program will output the number of occurrences of each letters and words. My program also sorts the words alphabetically.
My issue is: I output the words seen (first unsorted) and their occurrences as a table, and in my table I don't want duplicates. SOLVED
For example, if the word "to" was seen twice I just want the word "to" to appear only once in my table outputting the number of occurrences.
How can I fix this? Also, why is it that i can't simply set string[i] == delim to apply to every delimiter rather than having to assign it manually for each delimiter?
Edit: Fixed my output error. But how can I set a condition for string[i] to equal any of the delimiters in my code rather than just work for the space bar? For example on my output, if i enter "you, you" it will out put "you, you" rather than just "you". How can I write it so it removes the comma and compares "you, you" to be as one word.
Any help is appreciated. My code is below:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
const char delim[] = ", . - !*()&^%$##<> ? []{}\\ / \"";
#define SIZE 1000
void occurrences(char s[], int count[]);
void lower(char s[]);
int main()
{
char string[SIZE], words[SIZE][SIZE], temp[SIZE];
int i = 0, j = 0, k = 0, n = 0, count;
int c = 0, cnt[26] = { 0 };
printf("Enter your input string:");
fgets(string, 256, stdin);
string[strlen(string) - 1] = '\0';
lower(string);
occurrences(string, cnt);
printf("Number of occurrences of each letter in the text: \n");
for (c = 0; c < 26; c++){
if (cnt[c] != 0){
printf("%c \t %d\n", c + 'a', cnt[c]);
}
}
/*extracting each and every string and copying to a different place */
while (string[i] != '\0')
{
if (string[i] == ' ')
{
words[j][k] = '\0';
k = 0;
j++;
}
else
{
words[j][k++] = string[i];
}
i++;
}
words[j][k] = '\0';
n = j;
printf("Unsorted Frequency:\n");
for (i = 0; i < n; i++)
{
strcpy(temp, words[i]);
for (j = i + 1; j <= n; j++)
{
if (strcmp(words[i], words[j]) == 0)
{
for (a = j; a <= n; a++)
strcpy(words[a], words[a + 1]);
n--;
}
} //inner for
}
i = 0;
/* find the frequency of each word */
while (i <= n) {
count = 1;
if (i != n) {
for (j = i + 1; j <= n; j++) {
if (strcmp(words[i], words[j]) == 0) {
count++;
}
}
}
/* count - indicates the frequecy of word[i] */
printf("%s\t%d\n", words[i], count);
/* skipping to the next word to process */
i = i + count;
}
printf("ALphabetical Order:\n");
for (i = 0; i < n; i++)
{
strcpy(temp, words[i]);
for (j = i + 1; j <= n; j++)
{
if (strcmp(words[i], words[j]) > 0)
{
strcpy(temp, words[j]);
strcpy(words[j], words[i]);
strcpy(words[i], temp);
}
}
}
i = 0;
while (i <= n) {
count = 1;
if (i != n) {
for (j = i + 1; j <= n; j++) {
if (strcmp(words[i], words[j]) == 0) {
count++;
}
}
}
printf("%s\n", words[i]);
i = i + count;
}
return 0;
}
void occurrences(char s[], int count[]){
int i = 0;
while (s[i] != '\0'){
if (s[i] >= 'a' && s[i] <= 'z')
count[s[i] - 'a']++;
i++;
}
}
void lower(char s[]){
int i = 0;
while (s[i] != '\0'){
if (s[i] >= 'A' && s[i] <= 'Z'){
s[i] = (s[i] - 'A') + 'a';
}
i++;
}
}
I have the solution to your problem and its name is called Wall. No, not the type to bang your head against when you encounter a problem that you can't seem to solve but for the Warnings that you want your compiler to emit: ALL OF THEM.
If you compile C code with out using -Wall then you can commit all the errors that people tell you is why C is so dangerous. But once you enable Warnings the compiler will tell you about them.
I have 4 for your program:
for (c; c< 26; c++) { That first c doesn't do anything, this could be written for (; c < 26; c++) { or perhaps beter as for (c = 0; c <26; c++) {
words[i] == NULL "Statement with no effect". Well that probably isn't what you wanted to do. The compiler tells you that that line doesn't do anything.
"Unused variable 'text'." That is pretty clear too: you have defined text as a variable but then never used it. Perhaps you meant to or perhaps it was a variable you thought you needed. Either way it can go now.
"Control reaches end of non-void function". In C main is usually defined as int main, i.e. main returns an int. Standard practice is to return 0 if the program successfully completed and some other value on error. Adding return 0; at the end of main will work.
You can simplify your delimiters. Anything that is not a-z (after lower casing it), is a delimiter. You don't [need to] care which one it is. It's the end of a word. Rather than specify delimiters, specify chars that are word chars (e.g. if words were C symbols, the word chars would be: A-Z, a-z, 0-9, and _). But, it looks like you only want a-z.
Here are some [untested] examples:
void
scanline(char *buf)
{
int chr;
char *lhs;
char *rhs;
char tmp[5000];
lhs = tmp;
for (rhs = buf; *rhs != 0; ++rhs) {
chr = *rhs;
if ((chr >= 'A') && (chr <= 'Z'))
chr = (chr - 'A') + 'a';
if ((chr >= 'a') && (chr <= 'z')) {
*lhs++ = chr;
char_histogram[chr] += 1;
continue;
}
*lhs = 0;
if (lhs > tmp)
count_string(tmp);
lhs = tmp;
}
if (lhs > tmp) {
*lhs = 0;
count_string(tmp);
}
}
void
count_string(char *str)
{
int idx;
int match;
match = -1;
for (idx = 0; idx < word_count; ++idx) {
if (strcmp(words[idx],str) == 0) {
match = idx;
break;
}
}
if (match < 0) {
match = word_count++;
strcpy(words[match],str);
}
word_histogram[match] += 1;
}
Using separate arrays is ugly. Using a struct might be better:
#define STRMAX 100 // max string length
#define WORDMAX 1000 // max number of strings
struct word {
int word_hist; // histogram value
char word_string[STRMAX]; // string value
};
int word_count; // number of elements in wordlist
struct word wordlist[WORDMAX]; // list of known words
This is a K&R exercise (1-13)...
"Write a program to print a histogram
of the length of words in its input.
It is easy to draw the histogram with
bars horizontal; a vertical
orientation is more challenging."
The section was about arrays, and to be honest, I'm not sure I fully understood it. Everything up to this point was fairly easy to grasp, this was not.
Anyway I'm trying to do a histogram with horizontal bars first. Once I got that down I'll try vertical, but right now I'm not even sure where to begin with the easy version. (I slept on it, woke up, and still couldn't get it.)
I drew an example of what the program would output:
----------------------------------------------------------------
001|XX
002|XXXX
003|X
004|XXXXXXXXXX
005|XXXXXXXXXXXXXXXXXXXXXXXXX
006|XXXX
007|X
008|
009|XXXXXXXXX
010|XXX
>10|XXXX
----------------------------------------------------------------
And tried to break it (the program) down in sections. This is what I came up with:
PRINT TOP BORDER
PRINT CATEGORY, PRINT X EACH TIME CONDITION IS TRUE, PRINT NEWLINE,
REPEAT.
PRINT BOTTOM BORDER
But the more I think about it the less I think that's how it would work (because getchar() goes through one character at a time, and it wouldn't be able to go back up to put a X in the right category.) Or...
... I'm just really confused as to how I would solve this problem. Here's as far as I've been able to get code wise:
#include <stdio.h>
#define MAXWORDLENGTH 10
// print a histogram of the length of words in input. horizontal bar version
int main(void)
{
int c;
while ((c = getchar()) != EOF) {
}
return 0;
}
Could someone help enlighten me? Not necessarily with the code, maybe just pseudo code, or with some "words from the wise" as to what I need to do, or think, or something. This has just been a really big stone in the road and I'd like to get past it :/.
(I'll check back in 30 minutes)
I loved the pseudo-code! Some good thinking there, but you're still not ordering your program right.
As you said yourself, you can't read the text, go back and print an X in a particular row. If we establish that it can't be done, then there's no choice but to know all the values of the histogram beforehand.
So you should think your program as having two parts (and you'll make this kind of division in practically every program you write): first, a part that will make calculations; and then a part that will output them in a certain format (the histogram).
This tip should get you started! If you need further help, comment below.
I suggest you simplify the problem by solving it for the case of one word per line, so you can use fgets. Here's how to "eat up" lines that are too long.
Then, as often, the central data structure is the key to solving the problem. The data structure you need is an array used as frequency table:
int freq[11];
In freq[1], store the number of words/lines of length 1, in freq[2] those of length 2, etc., and in freq[0] those of length >10. You don't need to store the words since the rest of the program only needs their length. Writing out the histogram should be easy now.
I hope this isn't too much of a spoiler.
The code below prints a horizontal histogram using only the basic toolkit provided by the book so far:
#include<stdio.h>
/* Prints a horizontal histogram of the lengths of words */
#define MAX_WORDS 100
#define IN 1
#define OUT 0
main()
{
int c, length, wordn, i, j, state, lengths[MAX_WORDS];
wordn = length = 0;
state = OUT;
for (i = 0; i < MAX_WORDS; ++i) lengths[i] = 0;
while ((c = getchar()) != EOF && wordn < MAX_WORDS)
{
if (c == ' ' || c == '\t' || c == '\n')
state = OUT;
else if (wordn == 0)
{
state = IN;
++wordn;
++length;
}
else if (state == IN)
++length;
else if (state == OUT)
{
lengths[wordn] = length;
++wordn;
length = 1;
state = IN;
}
}
lengths[wordn] = length;
for (i = 1; i <= wordn; ++i)
{
printf("%3d: ",i);
for (j = 0; j < lengths[i]; j++)
putchar('-');
putchar('\n');
}
}
#include<stdio.h>
#define RESET 0
#define ON 1
main()
{
int i,wnum=0,c,wc[50];
int count=0,state;
state=RESET;
for(i=0;i<50;++i)
wc[i]=0;
/*Populating the array with character counts of the typed words*/
while((c=getchar())!=EOF)
{
if(c=='\n'||c=='\t'||c==' '||c=='"')
{
if(state!=RESET)
state=RESET;
}
else if((c>=65&&c<=90)||(c>=97&&c<=122))
{
if(state==RESET)
{
count=RESET;
++wnum;
state=ON;
}
++count;
wc[wnum-1]=count;
}
}
c=RESET;
/*Finding the character count of the longest word*/
for(i=0;i<wnum;++i)
{
if(c<wc[i])
c=wc[i];
}
/*Printing the Histogram Finally*/
for(i=c;i>0;--i)
{
for(count=0;count<wnum;++count)
{
if(wc[count]-i<0)
printf(" ");
else printf("x ");
}
printf("\n");
}
}
VERTICAL ORIENTATION: Using only the tools we learned so far in the book. And you can change the array size, wc[50]. I kept the code valid for 50 words.
Horizontal orientation should be quite simpler. I didn't try it though.
To histogram the word lengths, you are going to need to know the word lengths.
How do you define a word?
How can you measure the length of a word? Can you do it one character at a time as you read the stream, or should you buffer the input an use strtok or something similar?
You will need to accumulate data on how many occurrences of each length occur.
How will you store this data?
You will need to output the results in a pleasing form. This is fiddly but not hard.
I will link the answer below but since you asked for details the key seems to be this
Use an ARRAY of lengths i.e have an array with each element initialised to zero assume MAX wordlength to be about 30...
*have a flag while in the word and increment a counter every time a whitespace is NOT encountered
*once out of the word flag is set to "out" and the corresponding word length index item in the array is incremented i.e if word length counter is w_ctr use
array[w_ctr]++
*use the array as a table of reference for each line in a loop to print each line in the histogram so you can use the array and will now be able to determine weather the 'X' in the histogram is to be inserted or not
EDIT: sorry i didn't read the question right but the idea is simpler for Vertical histograms and the same thing can be used.
after the last step just print the horizontal histogram until counter exceeds current wordlength being printed
for(ctr=0;ctr<array[current_wordlength];ctr++)
printf('X');
End
the original is here http://users.powernet.co.uk/eton/kandr2/krx113.html
CLC-wiki is also a place see the comments for details.
//This is for horizontal histogram.
//It works for any number of lines of words where total words <= MAX
#include <stdio.h>
#define MAX 100 //Change MAX to any value.But dont give words more than MAX.
void main()
{
int w, nwords[MAX] = {0}, i = 0; //nwords is an array for storing length of each word.Length of all words initialized to 0.
while ((w = getchar()) != EOF)
{
if (w == ' ' || w == '\t' || w == '\n')
++i; //if space or tab or newline is encountered, then index of array is advanced indicating new word
else
++nwords[i]; //increment the count of number of characters in each word
} //After this step,we will have array with each word length.
for (i = 0; i < MAX; i++) //iterating through array
{
printf("\n");
for (; nwords[i] > 0; nwords[i]--)
printf("$"); //if length of word > 0 , print $ and decrement the length.This is in loop.
if (nwords[i+1] == 0) //as MAX is 100, to avoid printing blank new lines in histogram,we check the length of next word.
break; //If it is 0, then break the loop
printf("\n"); //After each word bar in histogram, new line.
}
printf("\n");
} //main
I've been also studying K&R book. An good approach is to use a int array for storing word frequencies. The array index is the word length, and the array values are the frequencies.
For example:
int histogram[15]; // declares an int array of size 15
// it is very important to initialize the array
for (int i = 0; i <= 15; ++i) {
histogram[i] = 0;
}
histogram[4] = 7; // this means that you found 7 words of length 4
Given that now you have a data structure for storing your word length frequencies, you can use the same reasoning of Word Counting example found in the book to populate the histogram array. It is very important that you manage to find the right spot for word length tracking (and resetting it) and histogram update.
You can create a function display_histogram for displaying the histogram afterwards.
Here's a code example:
#include<stdio.h>
#define MAX_WORD_LENGTH 15
#define IS_WORD_SEPARATOR_CHAR(c) (c == '\n' || c == ' ' || c == '\t')
#define IN 1
#define OUT 0
/* WARNING: There is no check for MAX_WORD_LENGTH */
void display_horizontal_histogram(int[]);
void display_vertical_histogram(int[]);
void display_histogram(int[], char);
void display_histogram(int histogram[], char type) {
if (type == 'h') {
display_horizontal_histogram(histogram);
} else if (type = 'v') {
display_vertical_histogram(histogram);
}
}
void display_horizontal_histogram(int histogram[]) {
printf("\n");
//ignoring 0 length words (i = 1)
for (int i = 1; i <= MAX_WORD_LENGTH; ++i) {
printf("%2d: ", i);
for (int j = 0; j < histogram[i]; ++j) {
printf("*");
}
printf("\n");
}
printf("\n\n");
}
void display_vertical_histogram(int histogram[]) {
int i, j, max = 0;
// ignoring 0 length words (i = 1)
for (i = 1; i <= MAX_WORD_LENGTH; ++i) {
if (histogram[i] > max) {
max = histogram[i];
}
}
for (i = 1; i <= max; ++i) {
for (j = 1; j <= MAX_WORD_LENGTH; ++j) {
if (histogram[j] >= max - i + 1) {
printf(" * ");
} else {
printf(" ");
}
}
printf("\n");
}
for (i = 1; i <= MAX_WORD_LENGTH; ++i) {
printf(" %2d ", i);
}
printf("\n\n");
}
int main()
{
int c, state, word_length;
int histogram[MAX_WORD_LENGTH + 1];
for (int i = 0; i <= MAX_WORD_LENGTH; ++i) {
histogram[i] = 0;
}
word_length = 0;
state = OUT;
while ((c = getchar()) != EOF) {
if (IS_WORD_SEPARATOR_CHAR(c)) {
state = OUT;
if (word_length != 0) {
histogram[0]++;
}
histogram[word_length]++;
word_length = 0;
} else {
++word_length;
if (state == OUT) {
state = IN;
}
}
}
if (word_length > 0) {
histogram[word_length]++;
}
display_histogram(histogram, 'h');
display_histogram(histogram, 'v');
}
Here's an input/output sample:
kaldklasjdksla klsad lask dlsk aklsa lkas adç kdlaç kd dklask las kçlasd kas kla sd saçd sak dasças sad sajçldlsak dklaa slkdals kkçl askd lsak lçsakç lsak lsak laskjl sa jkskjd aslld jslkjsak dalk sdlk jsalk askl jdsj dslk salkoihdioa slk sahoi hdaklshd alsh lcklakldjsalkd salk j sdklald jskal dsakldaksl daslk
1: *
2: ***
3: ******
4: ***************
5: **********
6: ****
7: ****
8: ***
9:
10: *
11: **
12:
13:
14: **
15:
*
*
*
*
*
* *
* *
* *
* *
* * *
* * *
* * * * *
* * * * * * *
* * * * * * * * *
* * * * * * * * * * *
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
You should separate your 2 problems in functions, like:
void gethist(char *s, int *hist, int len)
{ /* words here breaks on spaces (' ') */
char *t;
for( t=strtok(s," ");t;t=strtok(0," ") )
if(*t)
hist[ strlen(t)>len-1?len-1:strlen(t)-1 ]++;
}
void outhist(int *hist, int len)
{
int i;
for( i=1; i<=len; ++i )
{
char *s = calloc(1,5+hist[i-1]);
sprintf(s,"%03d|", i);
memset( s+4, 'X', hist[i-1]);
puts(s);
free(s);
}
}
then its easy in your main:
int main(void)
{
int c, hist[11] = {};
char *s = calloc(1,1);
while ((c = getchar()) != EOF) {
s = realloc( s, 2+strlen(s) );
s[ strlen(s)+1 ] = 0;
s[ strlen(s) ] = c;
}
gethist(s,hist,11); free(s);
outhist(hist,11);
return 0;
}
The vertical histogram can be printed one line at a time, by going through the array of word lengths and decreasing the word length at each iteration. A # is printed if the word length is still above zero, and a space is printed when it reaches 0. The newline is printed after each iteration.
If lengths[i] holds the number of characters for word i, and wordn is the total number of words, then the following will print the vertical histogram:
#define YES 1
#define NO 0
more_lines = YES;
while (more_lines)
{
more_lines = NO;
for (i = 1; i <= wordn; ++i)
{
if (lengths[i] > 0 )
{
more_lines = YES;
printf("#\t");
--lengths[i];
}
else
printf(" \t");
}
putchar('\n');
}
The complete code is below:
#include<stdio.h>
/* Prints a histogram of the lenghts of words */
#define MAX_WORDS 100
#define IN 1
#define OUT 0
#define YES 1
#define NO 0
main()
{
int c, length, wordn, i, j, state, more_lines, lengths[MAX_WORDS];
wordn = length = 0;
state = OUT;
for (i = 0; i < MAX_WORDS; ++i) lengths[i] = 0;
while ((c = getchar()) != EOF && wordn < MAX_WORDS)
{
if (c == ' ' || c == '\t' || c == '\n')
state = OUT;
else if (wordn == 0)
{
state = IN;
++wordn;
++length;
}
else if (state == IN)
++length;
else if (state == OUT)
{
lengths[wordn] = length;
++wordn;
length = 1;
state = IN;
}
}
lengths[wordn] = length;
/* Print histogram header */
for (i = 1; i <= wordn; ++i)
printf ("%d\t", i);
putchar('\n');
more_lines = YES;
while (more_lines)
{
more_lines = NO;
for (i = 1; i <= wordn; ++i)
{
if (lengths[i] > 0 )
{
more_lines = YES;
printf("#\t");
--lengths[i];
}
else
printf(" \t");
}
putchar('\n');
}
}
Although the exercise is based on Arrays, I tried to write it using the basic while loop and an if statement. I am not really good with Arrays as of now, so thought of trying this out. I have not tested it for bugs though, but it seems to be working fine for most inputs.
#include<stdio.h>
main() {
long int c;
while((c=getchar())!=EOF) {
if(c!=' '&&c!='\n'&&c!='\t') {
putchar("*");
}
if(c==' '||c=='\n'||c=='\t') {
putchar('\n');
}
}
return 0;
}
Please note that this is a very basic piece of code to print it horizontally, just for the basic understanding of the structure.
// Histogram to print the length of words in its input
#include <stdio.h>
main()
{
int wordcount[10],c,token=0;
int word=0, count =0;
for (int i=0; i<10; i++)
{
wordcount[i]=0;
}
while((c=getchar())!=EOF)
{
if(c== ' ' || c == '\n' || c== '\t')
{
// add the length of word in the appropriate array number
switch(word)
{
case 1:
++wordcount[0];break;
case 2:
++wordcount[1];break;
case 3:
++wordcount[2];break;
case 4:
++wordcount[3];break;
case 5:
++wordcount[4];break;
case 6:
++wordcount[5];break;
case 7:
++wordcount[6];break;
case 8:
++wordcount[7];break;
case 9:
++wordcount[8];break;
case 10:
++wordcount[9];break;
}
word =0;
}
else if (c != ' ' || c != '\n' || c!= '\t')
{
word++;
}
}
for (int j=0; j<10; j++)
{
if(wordcount[j]==0)
{
printf("- ");
}
for (int k=0;k<wordcount[j];k++)
printf("X", wordcount[j]);
printf("\n");
}
}
Here is the example of simple vertical Histogram
#include <stdio.h>
int main()
{
int c, i, j, max;
int ndigit[10];
for (i = 0; i < 10; i++)
ndigit[i] = 0;
while ((c = getchar()) != EOF)
if (c >= '0' && c <= '9')
++ndigit[c-'0'];
max = ndigit[0];
for (i = 1; i < 10; ++i) /* for Y-axis */
if (max < ndigit[i])
max = ndigit[i];
printf("--------------------------------------------------\n");
for (i = max; i > 0; --i) {
printf("%.3d|", i);
for (j = 0; j < 10; ++j)
(ndigit[j] >= i) ? printf(" X ") : printf(" ");
printf("\n");
}
printf(" ");
for (i = 0; i < 10; ++i) /* for X-axis */
printf("%3d", i);
printf("\n--------------------------------------------------\n");
return 0;
}
#include <stdio.h>
#include <string.h>
int main()
{
//hold length of string
unsigned long length;
// Holds the name input by user upto 50 characters
char name[50];
//iterator for generating dash for bar chart
int i = 0;
//iterator for generating dash for bar chart
int j = 0;
//take user name input
printf("input your name [without spaces and < 50 characters] : ");
scanf("%s", &name[0]);
//find the length of string
length = strlen(name);
printf("length of your name is %lu \n", length);
//generate dashes for bar chart
while (i < length)
{
printf("--");
++i;
}
printf("| \n");
// fill the bar chart with []
while (j < length)
{
printf("[]");
++j;
}
printf("| \n");
//generate dashes for bar chart
while (length > 0)
{
printf("--");
--length;
}
printf("| \n");
}
input your name [without spaces and < 50 characters] : ThisIsAtestRun
length of your name is 14
----------------------------|
[][][][][][][][][][][][][][]|
----------------------------|
I've tried to implement the latter part of the question (i.e. Displaying the histogram in a vertical manner) and have managed to get most of it done. In the code below, The maximum number of words accepted are 20 and the maximum word length is 10. Also, apologies for not getting the best graphical representation of a typical histogram but the logic to display vertical bars is completely accurate!
Here's my code,
#define MAXWORDS 20
#define MAXLENGTH 10
int c, nlength = 0, i, nword = 0, j;
int length_words[20]= {0};
while((c = getchar()) != EOF && nword <= MAXWORDS)
{
if(c != ' ' && c != '\t' && c != '\n')
++nlength;
else
{
if(nlength != 0){
length_words[nword] = nlength;
++nword;
/* for(i = 0; i < nlength; i++)
printf("O");
printf("\n");*/
printf("Word number: %d has length: %d\n", nword - 1, nlength);
}
nlength = 0;
}
}
// Displaying the Histogram
for(i = MAXLENGTH; i > 0; i--)
{
for(j = 0; j < nword; j++)
{
if(i > length_words[j])
printf(" ");
else
printf(" O ");
}
printf("\n");
}
Feel free to run this and let me know in case of any discrepancy or loopholes!
#include <stdio.h>
int main(void)
{
int i, ii, state, c, largest, highest;
int A[100];
for(i = 0; i < 100; ++i)
A[i] = 0;
i = ii = state = c = largest = 0;
while((c = getchar()) != EOF)
{
if(c == ' ' || c == '\b' || c == '\n')
{
if(state)
{
++A[i];
if(largest <= i)
largest = i;
i = state = 0;
}
}
else
{
if(state)
++i;
else
state = 1;
}
}
for(i = 0; i < 100; ++i)
if(highest <= A[i])
highest = A[i];
for(i = 0; i <= highest; ++i)
{
for(ii = 0; ii < 100; ++ii)
if(A[ii])
{
if(A[ii] > (highest - i))
printf("*\t");
else
printf(" \t");
}
putchar('\n');
}
for(ii = 0; ii < 100; ++ii)
if(A[ii])
printf("-\t");
putchar('\n');
for(ii = 0; ii < 100; ++ii)
if(A[ii])
printf("%d\t", ii + 1);
putchar('\n');
return 0;
}
Print Histogram based on word Length. (C Program).
HORIZONTAL VERSION :
#include<stdio.h>
#define MAX 15
int main()
{
int c, wordLength, count;
int arr[MAX] = { 0 };
wordLength = count = 0;
while((c = getchar()) != EOF)
{
if(c != ' ' && c != '\t' && c != '\n')
++arr[count];
else
++count;
}
for(int i = 0; i <= count; i++)
{
printf("\n%2d |", i+1);
for(int k = arr[i]; k > 0; k--)
printf("— ");
}
return 0;
}
Input/Output :
man in black thor jsjsk ksskka
1 |— — —
2 |— —
3 |— — — — —
4 |— — — —
5 |— — — — —
VERTICAL VERSION :
#include<stdio.h>
#define MAX 15
int main()
{
int c, count, maxLength;
int arr[MAX] = { 0 };
maxLength = count = 0;
while((c = getchar()) != EOF)
{
if(c != ' ' && c != '\t' && c != '\n')
++arr[count];
else
++count;
if(arr[count] > maxLength)
maxLength = arr[count];
}
for(int i = 1; i <= maxLength + 2; i++)
{
printf("\n");
for(int k = 0; k <= count; k++)
{
if(i <= maxLength)
{
if(maxLength - i < arr[k])
printf("|");
else
printf(" ");
}
else
{
if(maxLength - i == -1)
printf("—");
else
printf("%d", k+1);
}
printf(" ");
}
}
return 0;
}
Input/Output :
jsjs sjsj sjsj sjskks sjs sjs
|
|
| | | |
| | | | | |
| | | | | |
| | | | | |
— — — — — —
1 2 3 4 5 6
here is the method that I used :
#include <stdio.h>
#define MAXWD 25 // maximum words
#define MAXLW 20 // maximum lenght of word or characters
int main(void) {
int word[MAXWD]; // an array consists of 25 words
int c, i, j, nc, nw; // declaring c for the input, i for the loop 'for', j is for printing left to right, nc and nw stands for new character and new word
for (i = 0; i < 25; ++i)
word[i] = 0;
nc = nw = 0; // set to count from 0
while ((c = getchar()) != EOF) {
++nc;
if ( c == ' ' || c == '\t' || c == '\n') {
word[nw] = nc -1; // for deleting the space to go for the new word
++nw;
nc = 0; // start counting from zero
}
}
for (i = MAXLW; i >= 1; --i) {
for(j = 0; j <= nw; ++j) { // to start from left to wright
if (i <= word[j]) // MAXLW is less or equal to the number of words
putchar('|');
else
putchar(' ');
}
putchar ('\n');
}
return 0;
}