Switching letters (no strings or strings functions) - c

I want to switch the word "cat" into "dog" each time it appears in the text.
I can't use string or strings functions.
my code:
#include <stdio.h>
int main()
{
int i; // loop counter
int size; // size of arry
int input[20];
printf("enter text here\n");
while((input[i] = getchar()) != '\n') // input text to the arry
{
if(input[i]=='c' && input[i+1]=='a' && input[i+2]=='t') // switching characters
{
input[i]='d'; input[i+1]='o'; input[i+2]='g';
}
i++;
size++;
}
i=0; // reset for next loop
while(i <= size) // printing the text out ofthe arry
{
putchar(input[i]);
i++;
}
printf("\n");
return 0;
}
output:
enter text here
cat
cat
ȵm�� $$ŵ��p��$���Zտ ��$��M��v��������������������� ������������!��d#8 $
�
�����������5_Segmentation fault

Few problems here.
Uninitialized local variables.
int i = 0; // loop counter
int size = 0; // size of array
You are checking a and t chars which are not read yet.
Hence check for the t in current inputted char if it is matching then check for a and c
in previous entered chars as below.
if(i>=2 && input[i-2]=='c' && input[i-1]=='a' && input[i]=='t') // switching characters
{
input[i]='g'; input[i-1]='o'; input[i-2]='d';
}

This problem is perfect match for a finite-state machine.
Here's a flowchart of how a finite-state machine could do the job:
It might be a surprise that the non-matching cases go back to checking if the latest character matches 'C', rather than just printing it and getting a new one. The reason for this is that if we read say ccat or cacat, we want to check if we still match a (shorter) prefix, in this case c.
As a different example, consider if we were trying to match cocoa, and our input was cococoa. At the fifth character, we read c instead of a (already having matched coco, but not output anything yet), so we'd need to output co, and go back to matching for the second c.
We humans do not normally build such state machines by hand. We already have one for strings, either as a library or built-in to POSIX-compatible C libraries (Linux, Mac, BSDs): regular expression matching. If we use the basic POSIX ones, then regcomp() builds an efficient state machine based on our specifications, and regexec() processes input data on it.
This particular case is simple enough to implement by hand, though. What we'll want to do, is to put the first state ("Get next char") outside a loop, do a loop that continues as long as "char = EOF" is not true, and do the rest inside the loop. The final "Done" state is then after the loop. In pseudocode:
ch = Next char
While ch != EOF:
If ch != 'c':
Output ch
ch = Next char
Continue
End If
# The second "Get next char" in the flowchart:
ch = Next char
If ch == EOF:
Output 'c'
Break
Else
If ch != 'a':
Output 'c'
Continue
End If
# The third "Get next char" in the flowchart
ch = Next char
If ch == EOF:
Output 'c'
Output 'a'
Break
Else
If ch != 't':
Output 'c'
Output 'a'
Continue
End If
# 'c' 'a' 't' matched (with ch == 't').
Output 'd'
Output 'o'
Output 'g'
ch = Next char
End While
Done
A C program that reads standard input, converts each occurrence of cat to dog, case sensitive, can therefore be written as
#include <stdlib.h>
#include <stdio.h>
void cat_to_dog(FILE *in, FILE *out)
{
int ch;
ch = fgetc(in);
while (ch != EOF) {
if (ch != 'c') {
fputc(ch, out);
ch = fgetc(in);
continue;
}
ch = fgetc(in);
if (ch == EOF) {
fputc('c', out);
break;
} else
if (ch != 'a') {
fputc('c', out);
continue;
}
ch = fgetc(in);
if (ch == EOF) {
fputc('c', out);
fputc('a', out);
break;
} else
if (ch != 't') {
fputc('c', out);
fputc('a', out);
continue;
}
fputc('d', out);
fputc('o', out);
fputc('g', out);
ch = fgetc(in);
}
}
int main(void)
{
cat_to_dog(stdin, stdout);
return EXIT_SUCCESS;
}
The issue with finite state machines is that the code tends to be write-only. To understand the code, or to verify or maintain it in any time scales, you really need the definition of the finite-state machine, so one can compare the implemented code to the finite-state machine.
And here we finally get to the point of this "answer": Proper Documentation.
Solving a problem once using carefully-crafted, extremely tight and efficient code, is worth nothing if there is no way to modify or fix bugs in the code. (Even the very best programmer in the world makes mistakes and bugs at various levels of complexity. If someone claims they do not, they're lying.)
We could document the finite state machine by explaining it in comments interspersed with the above code. That would be fine; comments should always explain the intent of the programmer, the purpose or the task a piece of code is intended to accomplish. We often instead write comments that tells what the code does, which is less than useful, because we can easily read the code to see what it does. What we do not know, is whether the code matches the programmers intent, or whether the programmers intent is a valid solution to the underlying problem!
Another possibility would be to include the diagram, perhaps numbering the actions (ovals) and tests (parallelograms), and add comments in the code referring to the diagram. This is easier, but not as easy to follow (because you need to constantly cross-reference the code and the diagram).
It is, sadly, very common to omit the documentation part ("I'll do it later when I have more time"), and simply verify that the code works for correct input. It is often impossible to fully test the code for all possible inputs (although this one is so simple it can be), so a lot of bugs are left uncovered. Without documentation, so that a human can check the code and try and assess if it is logically correct (i.e., that the "flowchart", or finite state machine that it implements, is correct), and whether the code matches the working model or not, bugs are only found by being bitten by them in practice. Which is nasty.
Finite-state machines are a prime example of how important comments (and documentation) are, but it really applies to all code you write. Learn to try to describe your reasoning (model of the solution) and intent (what you want the code to accomplish) in your comments, and write a lot of comments, from the get go. It is very hard to get into the habit later on; I'm personally still struggling with this, after decades of programming. If a comment is later found to be unnecessary, it takes less than a fraction of a second to remove it; but if it explains a crucial quirk or complex detail that us humans don't normally notice, it may save hours, days, or even weeks of developer time later on.
This also means that the practice of commenting out unused code is not very useful, because the actual comments very quickly diverge from the code the compiler (or interpreter) sees. Instead, learn to use a version control system for your sources; I recommend git. It is available for just about all operating systems (see here), and you can use it both locally on your computer, as well as for distributed projects, in case you want to put your code on GitHub or similar services (or even set up your own git server). That way you can keep your code and its comments in sync; and when modifying your code, you can separately describe the reasons for those changes (changesets). After you get the hang of it, you'll find it is not a burden, but actually speeds up your code development!

Kiran mentions in his answer that "You are checking a and t chars which are not read yet". You can get around this by exploiting the use of argc and argv.
This is my version of your program using argc and argv. You'll notice that it also prevents you from limiting your input buffer (i.e. no input[20]).
#include <stdio.h>
int main(int argc, char **argv)
{
int i = 0, j = 0;
for(i=1; i<argc; i++)
{
while(argv[i][j] != '\0')
{
if(argv[i][j] == 'c')
{
if(((argv[i][j+1]) == 'a') && (argv[i][j+2] == 't'))
{
argv[i][j] = 'd';
argv[i][j+1] = 'o';
argv[i][j+2] = 'g';
}
}
printf("%c", argv[i][j]);
j++;
}
j=0;
printf(" ");
}
printf("\n");
return 0;
}
Sample input and output:
$ ./test my favourite cartoon when i was a kid was catdog
my favourite cartoon when i was a kid was dogdog
$ ./test i used to love cats but now i love dogs
i used to love dogs but now i love dogs
PS: Just in case you were born a little too late or didn't watch many cartoons on TV; here's the reference: https://en.wikipedia.org/wiki/CatDog.

You're trying to access input[i], input[i + 1] and input[i + 2].
Use :
while (input[i + 2] && input[i + 2] != '\n')
In your case :
#include <stdio.h>
int main()
{
int i = 0; // loop counter
int size = 0; // size of array
int input[20];
printf("enter text here\n");
while((input[i] = getchar()) != '\n' && i < 19) // input text to the array
{
/* if(input[i]=='c' && input[i+1]=='a' && input[i+2]=='t') // switching characters
{
input[i]='d'; input[i+1]='o'; input[i+2]='g';
}
*/
i++;
size++;
}
input[i] = 0;//ALWAYS null-terminate arrays.
if (i >= 2);
while (input[i + 2]) {
if (input[i] == 'c' && input[i + 1] == 'a' && input[i + 2] == 't') {
input[i] = 'd';
input[i + 1] = 'o';
input[i + 2] = 'g';
}
}
}
i=0; // reset for next loop
while(i < size) // printing the text out ofthe arry
{
putchar(input[i]);
i++;
}
printf("\n");
return 0;
}

Related

K&R c programming book 3.7 trim function example

I've been following K&R book 2nd version for c programming, but in 3.7 part, here is the screenshot of the function and my code part:
#include <stdio.h>
#include <string.h>
/* trim: removing the trailing blanks, tabs and newlines */
int trim(char s[]);
int main(){
char s[] = "hello,world \t\t\t\t\n\n\n\n";
printf("%s\n", s);
int length = trim(s);
printf("%d\n", length);
return 0;
}
int trim(char s[]){
int n;
for (n = strlen(s) -1 ; n >= 0 ; n--)
if (s[n] != ' ' && s[n] != '\n' && s[n] != '\t')
break;
s[n+1] = '\0';
return n;
}
The following is the output I get from running it:
Obviously the result length is 11, "hello,world", but the program outputs 10,
the reason is s[n+1] = '\n', instead of s[++n];I think it should be s[++n],
otherwise I will get the wrong output ==> 10, how to deal with it? could anyone plz take a look at it?
I would iterate over length and look ahead (behind?) one element to see if we need to trim it:
int trim(char s[]) {
int n;
for(n = strlen(s); n > 0; n--)
if(s[n-1] != ' ' && s[n-1] != '\t' && s[n-1] != '\n')
break;
s[n] = '\0';
return n;
}
In my opinion the book does not cleanly define the meaning of the return value.
But nearly:
looking for the first character that is not ...
I think that returning 10 matches that, if it is to be considered the definition.
So the 10 actually seems to be the correct value to me, it is the position of the "first" (or "last") non-white space character before/from the end of the string.
To answer the question "How to deal with it?":
You might not actually care, if you only use the modified string and not the return value.
If you do use the return value you need to use it according to the definition (if you consider the quoted text to be the definition), i.e. in order to get the length you have to increment by one in the using/calling function (not in the called function).
Or you modify the function as you described. That option only exists if you "own" the code (which in your own project and tutorials you do...). Keep in mind however, that this option will usually NOT be available in professional context of larger projects. There the APIs and behaviour is defined elsewhere and cannot be changed locally.
I do by the way agree and feel with you (your implicit rejection of the probable definition) and at least one commenter. The return value definition "position of last non-white character" is not one which jumps to mind....

Stdin + Dictionary Text Replacement Tool -- Debugging

I'm working on a project in which I have two main files. Essentially, the program reads in a text file defining a dictionary with key-value mappings. Each key has a unique value and the file is formatted like this where each key-value pair is on its own line:
ipsum i%##!
fubar fubar
IpSum XXXXX24
Ipsum YYYYY211
Then the program reads in input from stdin, and if any of the "words" match the keys in the dictionary file, they get replaced with the value. There is a slight thing about upper and lower cases -- this is the order of "match priority"
The exact word is in the replacement set
The word with all but the first character converted to lower case is in the replacement set
The word converted completely to lower case is in the replacement set
Meaning if the exact word is in the dictionary, it gets replaced, but if not the next possibility (2) is checked and so on...
My program passes the basic cases we were provided but then the terminal shows
that the output vs reference binary files differ.
I went into both files (not c files, but binary files), and one was super long with tons of numbers and the other just had a line of random characters. So that didn't really help. I also reviewed my code and made some small tests but it seems okay? A friend recommended I make sure I'm accounting for the null operator in processInput() and I already was (or at least I think so, correct me if I'm wrong). I also converted getchar() to an int to properly check for EOF, and allocated extra space for the char array. I also tried vimdiff and got more confused. I would love some help debugging this, please! I've been at it all day and I'm very confused.
There are multiple issues in the processInput() function:
the loop should not stop when the byte read is 0, you should process the full input with:
while ((ch = getchar()) != EOF)
the test for EOF should actually be done differently so the last word of the file gets a chance to be handled if it occurs exactly at the end of the file.
the cast in isalnum((char)ch) is incorrect: you should pass ch directly to isalnum. Casting as char is actually counterproductive because it will turn byte values beyond CHAR_MAX to negative values for which isalnum() has undefined behavior.
the test if(ind >= cap) is too loose: if word contains cap characters, setting the null terminator at word[ind] will write beyond the end of the array. Change the test to if (cap - ind < 2) to allow for a byte and a null terminator at all times.
you should check that there is at least one character in the word to avoid calling checkData() with an empty string.
char key[ind + 1]; is useless: you can just pass word to checkData().
checkData(key, ind) is incorrect: you should pass the size of the buffer for the case conversions, which is at least ind + 1 to allow for the null terminator.
the cast in putchar((char)ch); is useless and confusing.
There are some small issues in the rest of the code, but none that should cause a problem.
Start by testing your tokeniser with:
$ ./a.out <badhash2.c >zooi
$ diff badhash2.c zooi
$
Does it work for binary files, too?:
$ ./a.out <./a.out > zooibin
$ diff ./a.out zooibin
$
Yes, it does!
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
void processInput(void);
int main(int argc, char **argv) {
processInput();
return 0;
}
void processInput() {
int ch;
char *word;
int len = 0;
int cap = 60;
word = malloc(cap);
while(1) {
ch = getchar(); // (1)
if( ch != EOF && isalnum(ch)) { // (2)
if(len+1 >= cap) { // (3)
cap += cap/2;
word = realloc(word, cap);
}
word[len++] = ch;
} else {
if (len) { // (4)
#if 0
char key[len + 1];
memcpy(key, word, len); key[len] = 0;
checkData(key, len);
#else
word[len] = 0;
fputs(word, stdout);
#endif
len = 0;
}
if (ch == EOF) break; // (5)
putchar(ch);
}
}
free(word);
}
I only repaired your tokeniser, leaving out the hash table and the search & replace stuff. It is now supposed to generate a verbatim copy of the input. (which is silly, but great for testing)
If you want to allow binary input, you cannot use while((ch = getchar()) ...) : a NUL in the input would cause the loop to end. You must pospone testing for EOF, because ther could still be a final word in your buffer ...&& ch != EOF)
treat EOF just like a space here: it could be the end of a word
you must reserve space for the NUL ('\0') , too.
if (len==0) there would be no word, so no need to look it up.
we treated EOF just like a space, but we don't want to write it to the output. Time to break out of the loop.

How can I make my program recognize tabs and turn them into normal spaces?

I am currently in the process of learning/ re-learning C. I had some trouble learning it last year in school. I am using a book and one of the exercises is asking to get excessive spaces and turn them into just one space. The problem is that I can't make it recognize tabs. I have looked for some on here already but they all deal with arrays. I can't use arrays. This is what I have so far.
int main()
{
int c;
c = getchar();
while (c != EOF)
{
if (c == ' ')
{
putchar(c);
for (c = getchar(); c == ' '; c = getchar())
;
}
else if (c == '\t')
putchar(' ');
putchar(c);
c = getchar();
}
}
So basically, the code starts off with getting a char value and putting it into "c". then while c is not EOF, if c equals the ASCII value of SPACE it is going to output it and enter a for loop. The for loop tests for additional spaces. c equals the following char and while c equals the ASCII value of SPACE, it the for loop will do nothing and it will iterate until all additional spaces are gone. The if statement works perfectly by the way.
Then I go into an else if which I am certain is wrong but it's my latest attempt. So for this I said else if c equals the ASCII value of tab (is that a thing? If not that might be what my error is) put down a space by using putchar(' '). I feel like that command might be wrong as well. After that statement it then exits the conditional, puts out the value then c now equals a new char and the loop continues.
Thanks!
EDIT: So right after posting this I realized, at least I think, my error is the putchar(c) at the bottom which is still printing out the tab regardless? Although I am still not sure how to approach the problem. One more thing is that these are the only commands I can use. The book still assumes I don't know how arrays and such work in C yet.
Although its a bit unclear on how subsequent spaces/tabs are to be handled, consider simply saving the previous character.
int previous = EOF;
int ch;
while ((ch = fgetchar()) != EOF) {
if (ch == '\t') { // or if (isblank(ch)) ...
ch = ' ';
}
if (previous != ' ') {
putchar(ch);
}
previous = ch;
}

C duplicate character,character by character

I am trying to find if two characters following by one another are the same character. ie if i have this input "The oldd woman" I want to print next to second D "duplicate". Here is my code but I can't find out how can I do this coding. Here is my code:
void main()
{
char ch,ch2;
ch = getchar(); // getting the line of text
int i = 1;
ch2 = ch;
while (ch != '\n') // the loop will close if nothing entered
{
if (ch == ch2[&i]) {
printf("%c-duplicate", ch);
}
i++;
if (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U') { // checking for uppaercase vowel
printf("%c-upper case vowel", ch);
}
else if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u') { // checking for lowecase vowel
printf("%c-lower case vowel", ch);
}
else if (ispunct(ch)) { // checking for punctuation
printf("%c-punctuation", ch);
}
else
putchar(ch);
printf("\n");
ch = getchar();
}
printf("\n");
}
}
I am setting the character to another variable and then checking them with the first if statement. The program should run character by character.
Below is an example that I believe is what you intended. c is the current character being read (note: it is type int) and c1 is the previous character read (initialized to -1 to insure it does not match the first test).
While you can compare A and E..., the string library provides strchr that easily allows testing if a single character is included within a larger string.
Rather than call printf for each duplicate or vowel, etc.., why not use sprintf to build a string containing all the criteria applicable to any one character. That way you only call printf once at the end of each iteration (depending on whether you are printing all or just those that match criteria). s is used as the buffer that holds the match information for each character, offset is simply the number of characters previously written to s. (you should check to insure you don't exceed the number of characters available in s (but that was unneeded here)
It is unclear whether you want to print each character in the input string, or just those you have matched so far. The code below with only output those characters that match one of the criteria (if no argument is given). If you would like to see all characters, then pass any value as the first argument to the program.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXL 32
int main (int argc, char **argv) {
char *uvowel = "AEIOU", *lvowel = "aeiou";
int c, c1 = -1;
while ((c = getchar()) != '\n' && c != EOF) {
char s[MAXL] = ""; /* description buffer */
int offset = 0; /* offset */
if (c == c1) /* new == old */
offset = sprintf (s, " duplicate");
if (strchr (uvowel, c)) /* upper-case? */
sprintf (s + offset, " upper-case vowel");
else if (strchr (lvowel, c)) /* lower-case? */
sprintf (s + offset, " lower-case vowel");
else if (ispunct (c)) /* punctuation? */
sprintf (s + offset, " punctuation");
else if (argc > 1) /* if printing all */
sprintf (s + offset, " other");
if (*s) /* print c and s */
printf (" %c - %s\n", c, s);
c1 = c; /* save last char read */
}
return 0;
}
Example Use/Output
$ echo "The oldd woman" | ./bin/classdup
e - lower-case vowel
o - lower-case vowel
d - duplicate
o - lower-case vowel
a - lower-case vowel
Pass any value as the first argument to print all characters:
$ echo "The oldd woman" | ./bin/classdup 1
T - other
h - other
e - lower-case vowel
- other
o - lower-case vowel
l - other
d - other
d - duplicate other
- other
w - other
o - lower-case vowel
m - other
a - lower-case vowel
n - other
Duplicate vowels
$ echo "womaan" | ./bin/classdup
o - lower-case vowel
a - lower-case vowel
a - duplicate lower-case vowel
Look things over and let me know if you have any questions. There are many ways to do this, this is just one that seemed close to your intent.
(note: you will want to pass the -Wno-unused-parameter compiler option to eliminate the warning about argv being unused, or just do a stub test somewhere in the code, e.g. if (argv[1]) {})
Worth answering to try and help understand variables and pointers I think.
To try and answer . . . as simply as possible . . . NOTE #1: the main problem/issue is that ch and ch2 are declared as single char variables. They can be 'a' or 'b' or '\n' or 0x20 or any other single char. They are NOT char arrays or pointers. You have a comment where you read one char 'ch = getchar() // getting the line of text', that comment is incorrect (although you do have good comments in showing what you are thinking in your example), anyway, this 'ch = getchar()' just gets a single char. Later you treat ch2 as an array.
char ch,ch2;
. . . then later:
if (ch == ch2[&i]) { // ouch, does that even compile? yes. oh dear. how do we explain this one?!
ouch! This is wrong because it treats ch2 as an array/pointer.
The way your loop is working now is ch2 is set once to the very first char read. And it never changes.
It may compile okay BUT does it give a warning? Actually, in fairness to you. I do not get a warning. gcc 4.8.2 is perfectly happy with ch2 being a char and doing (ch == ch2[&i]). Now, ch2[&i] may be syntactically valid code, it will compile ok. It will even run ok. BUT what does it mean? Is it semantically valid? Let's forget about this weird thing until later.
Note that you can have c compiling fine BUT it can be quite full of pointer errors and can crash/hang. So . . . be careful :-).
Try making a change like this:
ch = getchar(); // does not get the line of text, just gets one char
. . .
ch2 = 0; // for the first iteration of loop
if (ch == ch2) { // change from ch2[&i] - wow :-) very confused!
. . .
ch2 = ch; // set ch2 to the last char read before reading new
ch = getchar(); // read one new char
This makes the code work just using 2 chars. ch and ch2. You do not use i. You do not use an array or string or char pointer.
NOTE #1.1: ch2[&i] compiles and runs. BUT IT IS WRONG, OHHHH SOOOOOO WRONG. And weird. How does array access work in c? The syntax of c[&i] is "correct" (maybe depends on compiler). BUT please do not use this syntax! What does it mean? It is semantically dubious. Looks like perhaps intent was to use array of chars together with i. Quick example showing assigning and reading from array of chars correctly:
char s[100]; // string of 100 chars, accessing index below 0 and above 99 is bad
i=0;
s[i]='H'; // assign char 'H' to first letter of string (s[0])
i++; // increment i, i is now 2.
s[i]='i';
i++;
s[i]=0; // end string
printf("s[0]=%c s[1]=%c s[2]=%02x string:%s",s[0],s[1],s[2],s);
NOTE #1.2: ch2[&i] compiles and runs. How and why does it compile?
&i means the pointer to the variable i in memory
%p in printf will show pointer value
So try adding this to code sample:
printf("%p %p %p\n", &ch, &ch2, &i);
// ch2[i] will not compile for me, as ch2 is not an array. syntax error
// ch2[&i] DOES compile for me. But . . what is the value ?
// What does it mean. I do not know! Uncharted territory.
printf("ch2[&i]:%p:%02x\n",&i,ch2[&i]);
printf("ch2[&ch]:%p:%02x\n",&ch,ch2[&ch]);
printf("ch2[&ch2]:%p:%02x\n",&ch2,ch2[&ch2]);
I'm getting something like this each run the pointers change:
ch2[&i]:0xbfa0c54c:bfa0de71
ch2[&ch]:0xbfa0c54a:08
ch2[&ch2]:0xbfa0c54b:00
The discovered explaination:
Usually we declare an array e.g. 'int array[100];' where 100 is the size of array. array[0] is first element and array[99] is the last. We index the array using integer. Now, all arrays are pointers. SO *array is the same as array[0]. *(array+1) is the same as array[1].
So far so good.
Now *(1+array) is also the same as array[1].
We can say int i=7; And use array[i] or *(array+7) or *(7+array) OR i[array] to show the 7th element of the array. i[array] to any programmer should look very VERY WROOOONG (not syntactically wrong BUT philosophically/semantically/morally wrong!)
Okay. Fine. Calm down. Jeez. Now with 'char ch2;' ch2 is a single char. It is NOT an array. ch2[&i] works(works as in compiles and sometimes/mostly runs ok!!!) because the last(WROOOONG) i[array] notation is valid. Looking at TYPES is interesting:
i[array] === <type int>[<type int *>].
ch2[&i] === <type char>[<type int *>].
C happily and merrily casts char to int and int can be added to pointer or used as pointer. SO FINALLY IN CONCLUSION: the syntax ch2[&i] evaluates to an integer at offset of: &i(pointer to integer i) PLUS value of char ch2. There is no good reason to use this syntax! It's DANGEROUS. You end up accessing a memory location which may or may not be valid and as your pointer is pointer to single variable the location in not valid in reference to any other values.
See here: Why 'c[&i]' compiles while 'c[i]' doesn't?
NOTE #2: watch the bracketing {}. while and main closing } and indentation do not match in example. The program functions okay with this error. The putchar(ch) runs as part of the last else. The commands after that run at end of while loop.
NOTE #3 main should return int not void main optionally takes nothing '()' or '(int argc, char**argv)'.
Per other suggestions: Change ch == ch2[&i] This makes no sense here
Since you set ch = ch2 before the loop then the line
if(ch == ch2 )
(After you fix it) will always evaluate to true the first time around
Your else, is very confusing, if you have more than one line of code there you have to put in brackets
Keep in mind, when you enter your input you are actually submitting two characters eg ("e" AND "\n") because you press enter after you type the character and that counts
Try a little harder, meaning put an error message, put down the results of your attempts at a solution. It helps both us and you. It seems a little like you wrote this and just immediately want a fix. Programming gets harder, if you can't work through problems (with suggestions) then it's going to hurt a lot more, but it doesn't have to.
For a quick an dirty proof of concept, add another ch=getchar(); immediately after the one under your else. Note that the code below should run but doesn't do exactly what you want yet, you'll need to do some further debugging.
Edit 10/19/2016
Fixed the char2 issue you guys pointed out
Moved ch2 = ch to above the lines which get the new character
#include <stdio.h>
#include <ctype.h>
int main(){
char ch,ch2;
ch = getchar(); // getting the line of text
int i = 1;
ch2 = 0;
while (ch != '\n') // the loop will close if nothing entered
{
if (ch == ch2) {
printf("%c-duplicate", ch);
}
i++;
if (ch == 'A' || ch == 'E' || ch == 'I' || ch == 'O' || ch == 'U') { // checking for uppaercase vowel
printf("%c-upper case vowel", ch);
}
else if (ch == 'a' || ch == 'e' || ch == 'i' || ch == 'o' || ch == 'u') { // checking for lowecase vowel
printf("%c-lower case vowel", ch);
}
else if (ispunct(ch)) { // checking for punctuation
printf("%c-punctuation", ch);
}
printf("\n");
ch2 = ch;
ch = getchar();
ch = getchar();
}
printf("\n");
return 0;
}

How to check and validate user input is one of two valid choices

I have the following code asking for user input either A or P. I have the same sort of setup for hour and minutes, where hour would be between 1 and 12 and minutes would be between 0 and 59. That part of my code is thoroughly working.
My issue is that I don't know how to check what the timePeriod variable is and ensure that it is either A or P and to print an error message and prompt again if it is anything else including lowercase a and p. User input has to be in uppercase and ONLY A or P.
I've only put the function code here. I added the clean_stdin code as well so the while statement inside getTimePeriod might be easier to understand. As I said before, I'm using a similar set up for both the hour and minutes and that's working.
char getTimePeriod(void)
{
char timePeriod, term;
while ( (((scanf("%c%c",&timePeriod,&term)!=2 || term !='\n') && clean_stdin()) || timePeriod != "A" || timePeriod != "P") && printf("Invalid value entered. Must be A or P. Please re-enter: ") );
return timePeriod;
}
int clean_stdin()
{
while (getchar()!='\n');
return 1;
}
Edit: For those getting their panties in a twist about this being bad code, it works for me based on my assignment requirements for an Intro to C course. Hope that clarifies the noob-ness of this question as well.
Also note that
timePeriod != 'A'
does not work. I don't know why but it doesn't work.
Recommend separating user input from validation.
scanf() tries to do both at once. It is easier to handle potential wrong user input if simply a line of input is read (fgets() - standard or getline() common #Jonathan Leffler) and then parsed by various means.
// return choice or EOF
int GetChoice(const char *prompt, const char *reprompt, const char *choices) {
char buf[10];
puts(prompt);
while (fgets(buf, sizeof buf, stdin)) {
buf[strcspn(buf, "\n")] = 0; // drop potential trailing \n
char *p = strchr(choices, buf[0]);
if (p && buf[1] == '\0') {
// Could fold upper/lower case here if desired.
return *p;
}
puts(reprompt);
}
return EOF;
}
int timePeriod = GetChoice("TimePeriod A or P", "Try Again", "AP");
switch (timePeriod) {
case 'A' : ...
case 'P' : ...
default: ...
Additional checks could be added. That is the best part about rolling this off to a helper function, it can be used is multiple places in code and be improved as needed in a localized manner.
OP code comments:
It user input is not as expected, it is unclear that OP's complex while() condition will properly empty user's line of input. It certainly has trouble if EOF is encountered or if first char is a '\n'.
timePeriod != "A" as commented by #Alan Au is not the needed code. That compares timePeriod to the address of the string "A". Use timePeriod != 'A'.
clean_stdin() should be clean_stdin(void). It is an infinite loop on EOF. Consider:
int ch;
while ((ch = getchar()) != '\n' && ch != EOF);
The problem you're having is here:
timePeriod != "A" || timePeriod != "P"
First, as was mentioned before, you can't compare a character to a string. You need to use single quotes instead of double quotes. Assuming that's been corrected, this conditional will always be true since timePeriod will always either not be 'A' or not be 'P'. This needs to be a logical AND:
(timePeriod != 'A' && timePeriod != 'P')
Note also that an extra set of parenthesis were added to make sure that the order of operation in your while expression is preserved.
Regarding the "cute" comment, what that means is that cramming a bunch of statements in the while expression and leaving the body blank makes your code difficult to read and therefore more prone to bugs. Had you broken that up into multiple statements each doing one logical thing you would have been more likely to find this bug yourself. Olaf made the comment he did primarily to warn other users who come across your code about just that.

Resources