How to replace characters using 'getchar()' and if else? - c

My task is to check the user input and replace each period with exclamation mark, and each exclamation mark with 2 exclamation marks, then count the number of substitutions made.
This is my code:
int main(void)
{
int userInput, substitutionsNum = 0;
printf("please enter your input:\n");
while ((userInput = getchar()) != '#')
{
if (userInput == '.')
{
userInput = '!';
++substitutionsNum;
}
else if (userInput == '!')
{
userInput = '!!';
++substitutionsNum;
}
}
printf("%c, the number of substitutions are: %d", userInput, substitutionsNum);
return 0;
}
If I put in the input "nir." and then "#" to go out of the program, the output is "#, the number of substitutions are: 1"

You never print the input back out except once at the end, so the "replacement" won't work.
Also, you can't represent a pair of exclamation points as '!!', that's a multi-character literal which is not the same. At least, no I/O functions will do what you expect with it, if you try to print it for instance.

!!
is two characters. You assume it as a single character.
And you are overwriting the in the same variable userInput
You could use one more char buffer so that you can adjust your indices according to need. for example two increment to index when you want to store "!!".

You are doing it wrong. You need to store the accumulated changed input in a character array (i.e. char buffer[1024]) and place the substitutions there. With your algorithm, the only thing you are going to print is the last value of userInput variable.
Since this is probable homework I would suggest you to read more about string manipulation in C language.

Related

Displaying a string one word per line using IN/OUT flag

I have written a program that first stores an arbitrary number of lines of text from the user. After that, it checks when a new word has come and if it does, then it prints it in a new line.
Below is my code:
#include<stdio.h>
#define IN 1 //inside a word
#define OUT 0 //outside a word
int main()
{
char s[100]; //storing the string entered by user
int c; //getchar reading variable
int i=0; //iterating variable
while((c=getchar())!=EOF)
{
s[i++]=c;
}
s[i]='\0'; //end of string
i=0;
int p=0; //stores the start of the word
int current=OUT; //flag to indicate if program is inside or outside a word
while(s[i]!='\0')
{
if(current==OUT) //when program is outside a word
{
if(s[i]!=' ' || s[i]!='\n' || s[i]!='\t') //word found
{
p=i; //store starting position of word
current=IN;
}
}
else if(current==IN) //program is inside a word
{
if(s[i]==' ' || s[i]=='\n' || s[i]=='\t') //end of word found
{
current=OUT; //flag now outside the word
for(int j=p;j<i;j++) //print from starting position of word
{
printf("%c",s[j]);
}
printf("\n"); //go next line
}
}
++i; //incremnent the iterator variable
}
return 0;
}
My program works well if I just enter the string in a proper manner, i.e. without any extra spaces or new lines.
But if I enter a line as follows ( notice the extra spaces and new lines):
*I am a boy
I went to Japan */
Then it prints those extra newlines and spaces along with word too, which according to me should not happen because of the IN and OUT flags.
The output is like this:
enter image description here
I request you to please help me out.
I know I can do this easily with the putchar() method checking one character at one time, but I am just curious as to what I am doing wrong in this implementation.
First bug that jumps out at me:
if(s[i]!=' ' || s[i]!='\n' || s[i]!='\t')
will always return true. You want &&, or else use a !() around the whole condition that you use the other place, for symmetry.
Or better yet, factor that out into a function, or use isspace from <ctype.h>.
Your filtering condition for determining if a character is white space is not correct. The || operator means OR. Using chained OR will allow the expression to evaluate to true every time. You need the AND operator &&. The and operator fails as soon as one operand evaluates to false, or in the case of C, 0.
Besides that, there are better ways to check for white space. One idea is using the isspace function from <ctype.h>, which accepts a character as an int, which can also be an unsigned char, and determines if that character is any of ' ', '\t', '\v', '\n' or '\r'. You can also do character checking via switch statements
switch(ch) {
case ' ':
// do something
break;
case '\n':
//do something
break;
}

explain the logic behind the structure of code needed to account for extra spaces, in order to calculate the correct average word length

This is the question on my assignment: Write a program that prompts the user to enter a sentence (assume that a sentence can have a maximum of 50 characters). It then counts the vowels and consonants in it. It also calculates the average word length of the input sentence. Word length is the total number of alphabetic characters in the sentence divided by the total number of words in it. Words are separated by one or more spaces. All the results are displayed at the end.
So far I have been able to complete all aspects of the question but I am running into a logical error on my part. When the user inputs more than a normal amount of spaces, it messes up the answer given for average word length.
Here is my code calculating average word length:
for(i = 1; sent[i] != '\0'; i++){
if( sent[i] == ' '){
++spaceCount;
}
else if((sent[i] != ' ') && (sent[i] != '\n')){
++charCount;
}
}
avgWordLength = (charCount / (spaceCount+1)) ;
Could someone help explain the logic behind the structure of code needed to account for extra spaces, in order to calculate the correct average word length
Here is a link to a previously already answered question:
Average word length for a sentence
But my school has not taught the "getchar" function yet and I would not like to use it unless I have too. To be more clear, is there away to complete the question without using the "getchar" function?
Here is an example of the problem when compiling and running
// Everything works good when
string: Thursday is ok
Average word length: 4.00 characters
// this is where my code fall apart
string: Thursday is ok
Average word length: 1.86 characters
Well, if you think about it, what you want to do is just treat any uninterrupted series of whitepace characters as one for the purpose of computing the word count. You can include ctype.h and use the isspace function to test all possible whitespace characters, or if you are supposed to do it manually, then at least check for space or tab characters (e.g. you could have a mixed sequence of spaces and tabs that should still be counted as a single (e.g. " \t \t ")
To handle multiple whitespace characters and count the sequence as one, just set a flag (e.g. ws for whitespace) and only increment spaceCount when you encounter the first whitespace, and reset the flag if another non-whitespace character is encountered.
Putting those pieces together, you could do something like the following:
int ws = 0; /* flag to treat multiple whitespace as 1 */
for(i = 0; sent[i]; i++){
if (sent[i] == ' ' || sent[i] == '\t') {
if (!ws) {
spaceCount++;
ws = 1;
}
}
else {
charCount++; /* non-whitespace character count */
ws = 0;
}
}
(note: begin your check at i = 0 to protect against Undefined Behavior in the event sent is the empty-string.)
(note2: you can check charCount before setting your first spaceCount and check ws after leaving the loop to handle leading and trailing whitespace -- and adjust spaceCount as necessary. That is left as an exercise)
Look things over and let me know if you have any further questions.
Could someone help explain the logic behind the structure of code needed to account for extra spaces, in order to calculate the correct average word length
You could use a state machine. You have two states:
1) Looking for the end of a word.
2) Looking for the end of a space sequence.
Look at the first character in the sentence. It is either the beginning of a word or a space. This tells you if you are in state 1 or 2.
If in state 1, then look for a space or the end of the sentence. If you find a space, set your state to 2.
If in state 2, then look for a non-space or the end of the sentence. If you find a non-space then set your state to 1.
counts the vowels and consonants in it. It also calculates the average word length of the input sentence.
Could someone help explain the logic behind the structure of code needed to account for extra spaces
There really is no need to count spaces. Instead all that is needed to to count the number of times a letter begins a word - it followed a non-letter - or was first character.
// pseudo code
sentence_stats(const char *s) {
vowels = 0;
consonants = 0;
word_count = 0;
previous = 0;
while (*s) {
if (isletter(*s)) { // OP to make isletter(), isvowel()
if (!isletter(previous)) {
word_count++; // start of word
}
if (isvowel(*s)) vowels++;
else consonants++;
} else if (*s == ' ') {
; // nothing to do
} else {
TBD_CODE_Handle_non_letter_non_space();
}
previous = *s;
s++;
}
average = (vowels + consonants)/word_count
}

while loop is not breaking

I am not able to find out the reason for the misbehavior of the below code. This is a simple code to accept characters until either * is entered or until array size is reached and then print the characters read from the keyboard. It looks like the reading part is fine. Also if I enter * before array size is reached everything is OK. But if I do not enter * and wait until array size is reached in reading portion, I have the trouble. While printing it prints the characters read, but after that some garbage is printed. Ran through debugger, but while loop is not breaking when index is 3 or more.
int main()
{
char myStr [3];
unsigned int index=0;
printf("Enter Single characters. Enter * to stop\n");
do
{
scanf(" %c",&myStr[index]);
index++;
} while ((myStr[index-1]!='*')&&((index)<(sizeof(myStr)/sizeof(myStr[0]))));
index=0;
while ((myStr[index]!='*')&&(index<(sizeof(myStr)/sizeof(myStr[0]))))
{
printf("%c",myStr[index]);
index++;
}
printf("\n");
return(0);
}
The code runs into undefined behaviour on the printf loop's last iteration here
while ((myStr[index]!='*')&&(index<(sizeof(myStr)/sizeof(myStr[0]))))
{
...
as it in fact is doing
while ((myStr[3] ....
with myStr[3] accessing myStr out-of-bounds.
To fix this do:
while ((index < (sizeof(myStr)/sizeof(myStr[0]))) && (myStr[index] != '*'))
Boolean short-circuiting will take care of myStr[3] not being executed.
You are manipulating strings, witch need to be null-terminated.
You should use fgets(3) to get your string and then strlen(3) to get the length.
Then you can move in your string by
while (str[i] != '*' && i < strlen(str))
good luck

How to get 3 chars entered with spaces in scanf into a char array?

I am previously a java programmer, but I'm now doing a C course at university (computer science major).
I need the user to be able to enter 3 chars,the first 2 being numbers, and the last 1 being either 'v' or 'h'.
For example "1 2 v".
I need the user to be able to enter it with the spaces in between each character.
This is my current code:
void manageInput(char box[][width]){
char move[4];
char input[16];
while(1){
scanf("%s", input);
int i = 0;
while(input[i] != 0){
if(input[i] != ' ' && input[i] != "\n"){
move[i] = input[i];
}
i++;
}
printf("%s\n", move);
makeMove(box, move);
printBox(box, height, width);
// TODO
if(move[0] == 'x'){
exit(0);
}
}
}
However if I run it, it works fine when I enter the chars with out spaces like "12v", but If I enter "1 2 v", it will print out "1", call printBox, then print out "2", then print out box again, and so on.
If someone could explain what I'm doing wrong here, I would appreciate it.
If someone could explain what I'm doing wrong here, I would appreciate it.
The short story is: Your code doesn't fulfill your requirements. It simply doesn't do what you want it to do.
Your requirements are:
All fields must be one character. This requirement isn't fulfilled by your code. Your code will mistakenly accept multiple characters per field.
There must be one space (exactly one space?) between the fields. This requirement isn't fulfilled by your code. There might be multiple spaces between the fields, and your code will mistakenly accept that.
In fact, your code invokes undefined behaviour by accessing the move array out of bounds. Consider that as a consequence of one of the above scenarios i might become some value higher than 3. What might happen in this code: move[i] = input[i];?
Your code is also way too complex. All of your functionality can be performed by scanf alone. It's a very powerful function, when you know how to use it correctly... I suggest reading and understanding the manual multiple times, when you have an opportunity. You'll learn a lot!
I notice something you neglected to mention from within the logic you have presented: It's expected that the first field might also be 'x', which corresponds to an exit usecase. This is a bad design; the caller has no opportunity to clean up... but I'll run with it. You really should use return (and return an int value or something, corresponding to error/success) instead.
Let us caste that last paragraph aside, because we can simply consider 'x' to be invalid input (and exit as a result), and I don't want to change the contracts of your functions; I'll leave that to you. The expression described so far appears to be int x = scanf("%1[0123456789]%*1[ ]%1[0123456789]%*1[ ]%1[vh]", a, b, c);.
Note that it is expected that a, b and c will have enough space to store a string of one byte in length. That is, their declaration should look like: char a[2], b[2], c[2];.
Make sure you check the return value (x, in the example)! If x is 3, it's safe to assume that the three variables a, b and c are safe to use. If x is 2, it's safe to assume that a and b are safe to use, and so on... If x is EOF or 0, none of them are safe to use.
By checking the return value, you can reject input that doesn't match that precise pattern, that is:
Fields that aren't exactly one byte in width will be rejected.
Too many or too few spaces will be rejected.
Something else popped up that you have neglected to mention, and it's also present within your code: Chux mentioned that you'll likely be expecting the input to be terminated with a '\n' (newline) character. This can also be implemented in a number of ways using scanf:
scanf("%1*[\n]"); will attempt to read and discard precisely one '\n' character, but there's no way to ensure that was successful. getchar would be more appropriate for that purpose; something along the lines of if (getchar() != '\n') { exit(EXIT_FAILURE); } might make sense, if you wish to ensure that the lines of input are perfectly formed and bomb out when they aren't... #define BOMB_OUT?
scanf("%*[^\n]"); scanf("%*c"); makes more sense; If you're interested in reading one item per line, then it makes sense to discard everything remaining on the line, and then the newline character itself. Note that your program should always tell the user when it's discarding or truncating input. You could also use getchar for this.
void manageInput(char box[][width]){
for (;;) {
char a[2], b[2], c[2];
int x = scanf("%1[0123456789]%*1[ ]%1[0123456789]%*1[ ]%1[vh]", a, b, c);
if (x != 3) {
/* INVALID INPUT should cause an error value to be returned!
* However, this function has no return value (which makes it
* poorly designed)... Calling `exit` gives no opportunity for
* calling code to clean up :(
*/
exit(EXIT_FAILURE);
}
if (getchar() != '\n') {
# ifdef BOMB_OUT
exit(EXIT_FAILURE);
# else
scanf("%*[^\n]");
getchar();
puts("NOTE: Excess input has been discarded.");
# endif
}
char move[4] = { a[0], b[0], c[0] };
printf("%s\n", move);
makeMove(box, move);
printBox(box, height, width);
// TODO
if(move[0] == 'x'){
exit(0);
}
}
}
%s reads a whitespace-delimited string with scanf, so if that's not what you want, it's not the thing to use. %c reads a single character, but does not skip whitespce, so you probably also want a (space) in your format to skip whitespace:
char input[3];
scanf(" %c %c %c", intput, input+1, input+2);
will read 3 non-whitespace characters and skip any whitespace before or between them. You should also check the return value of scanf to make sure that it is 3 -- if not, there was less than 3 characters in your input before an end-of-file was reached.
It's usuall a bad idea to read string via scanf because of potential buffer overflow. Consider using fscanf or better fgets as in
fgets(input, 15, stdin);
Note the extra byte for '\0'.
Also, you're comparing char to string here: input[i] != "\n". It should be input[i] != '\n' instead.
And btw you can just use something like
int x, y;
char d;
scanf("%d%d%c", &x, &y, &d);
This looks like two simple bugs.
You need to use separate indexes for move[] and input[]
int i = 0;
while(input[i] != 0){
if(input[i] != ' ' && input[i] != "\n"){
move[i] = input[i];
}
i++;
}
Imagine input of 1 2 v
input[0] != 0, so we enter the loop
it's not ' ' or '\n' either, so we copy input[0] to move[0]
so far so good
You increment i, and discover that input[1] == ' '
But then you increment i again
You discover that you are interested in input[2] (2) - so you copy it to move[2], rather than move[1]. Oops!
Then to make things worse, you never put an end-of-string character after the last valid character of move[].

Converting Character Array to Integer Array in C for ISBN Validation

I really hope someone can give a well explained example. I've been searching everywhere but can't find a proper solution.
I am taking an introduction to C Programming class, and our last assignment is to write a program which validates a 10 digit ISBN with dashes... The ISBN is inputted as a string in a CHAR array. From there I need to separate each digit and convert them into an integer, so I can calculated the validity of the ISBN. On top of that, the dashes need to be ignored..
My thought process was to create an INT array and then use a loop to store each character into the array, and pass it through the atoi() function. I also tried using an IF statement to check each part of the CHAR array to see if it found a dash. If it did find one, it would skip to the next spot in the array. It looked something like this:
int num[12], i = 0, j = 0, count = 0;
char isbn[12];
printf ("Enter an ISBN to validate: ");
scanf ("%13[0-9Xx-]%*c", &isbn);
do {
if (isbn[i] == '-') {
i++;
j++;
}
else {
num[i]= atoi(isbn[j]);
i++;
j++;
}
count++;
} while (count != 10);
But that creates a segmentation fault, so I can't even tell if my IF statement has actually filtered the dashes....
If someone could try and solve this I'd really appreciate that. The Assignment was due Dec 4th, however I got an extension until Dec 7th, so I'm pressed for time.
Please write out the code in your explanation. I'm a visual learner, and need to see step by step.
There's obviously a lot more that needs to be coded, but I can't move ahead until I get over this obstacle.
Thanks in advance!
First of all, your definition of isbn is not sufficient to hold 13 characters; it should therefore be 14 chars long (to also store the terminating '\0').
Second, your loop is overly complicated; three loop variables that maintain the same value is redundant.
Third, the loop is not safe, because a string might be as short as one character, but your code happily loops 10 times.
Lastly, converting a char that holds the ascii value of a digit can be converted by simply subtracting '0' from it.
This is the code after above improvements have been made.
#include <stdio.h>
int main(void)
{
int num[14], i;
char isbn[14], *p;
printf("Enter an ISBN to validate: ");
scanf("%13[0-9Xx-]%*c", &isbn);
// p iterates over each character of isbn
// *p evaluates the value of each character
// the loop stops when the end-of-string is reached, i.e. '\0'
for (p = isbn, i = 0; *p; ++p) {
if (*p == '-' || *p == 'X' || *p == 'x') {
continue;
}
// it's definitely a digit now
num[i++] = *p - '0';
}
// post: i holds number of digits in num
// post: num[x] is the digit value, for 0 <= x < i
return 0;
}

Resources