Reputation: 327
I am building a program that takes user input, manipulates it, and outputs the changes (if any). The guidelines are:
I cannot use any data structure for this project, so I am using getchar() to read stdin char by char.
Is there any way to compare stdin to stdout in C? I am new to the language and do not know how to even look into this. My code with all guidelines satisfied sans 6 is below:
#include <stdio.h>
int newline(int iochar);
void testChars(int iochar);
void upperCase(int iochar);
void lowerCase(int iochar);
void space(int iochar);
void digit();
int main()
{
int iochar = 0;
iochar = getchar();
testChars(iochar);
return 0;
}
void testChars(int iochar)
{
while(iochar != EOF) {
if (iochar == 10)
iochar = newline(iochar);
else if(iochar == 32)
space(iochar);
else if (iochar > 64 && iochar < 91)
upperCase(iochar);
else if (iochar < 123 && iochar > 96)
lowerCase(iochar);
else if (iochar < 58 && iochar > 47)
digit();
else putchar(iochar);
iochar = getchar();
}
}
void space(int iochar)
{
putchar(iochar);
putchar(iochar);
}
int newline(int iochar)
{
int count = 0;
while (iochar == 10){
if(count < 2)
putchar(iochar);
count++;
iochar = getchar();
}
return iochar;
}
void digit()
{
return;
}
void upperCase(int iochar)
{
iochar += 32;
putchar(iochar);
}
void lowerCase(int iochar)
{
iochar -=32;
putchar(iochar);
}
Upvotes: 1
Views: 1280
Reputation: 84579
While you can break your tests up into multiple functions, since the number of cases you are handling is limited, simply including the tests directly in a single "state" loop that keeps track of the number of newlines and whether there have been any modifications to the input is an equally readable way to go. (good catch @iBug on the XY-Problem).
There are a number of ways to approach a character classification problem, which is essentially what you have. (meaning you read a character, classify whether if fits some predefined test, and then take some action depending on how it is classified). One efficient way to handle it is with a loop that keeps track of the state of things (e.g. "How many newlines have been sequentially read?", or whether I'm inside a word, reading whitespace, etc..) by setting, or resetting or incrementing a few variables that track whatever condition (or state) you are interested in that is based on something other than the current character.
You simply figure out what you need to follow (here, the number of sequential newlines, and whether you have modified the output in any way). Easy enough, just keep a count of the sequential newlines read in an integer like nl
and keep an integer flag you set if you make any changes, call it modified
or something logical. When you read anything other than a '\n'
reset your newline count, e.g. nl = 0;
.
Then it is just a matter of reading each character of input until you encounter EOF
and conducting a number of tests to determine what character you read (either using if, else if, else
or using a switch
statement). Then for each different test, take the appropriate action.
For example, you could satisfy your criteria with something simple like:
#include <stdio.h>
int main (void) {
int c, modified = 0, nl = 0;
while ((c = getchar()) != EOF) { /* loop over each char */
if (c == '\n') { /* am I a '\n'? */
if (nl < 2) /* have I ouput less than 2? */
putchar ('\n'); /* output the newline */
else
modified = 1; /* set modified flag */
nl++; /* update the number seen */
continue; /* get next char */
}
else if ('A' <= c && c <= 'Z') { /* am I uppercase? */
putchar (c + 'a' - 'A'); /* convert to lowercase */
modified = 1; /* set modified flag */
}
else if ('a' <= c && c <= 'z') { /* am I lowercase? */
putchar (c + 'A' - 'a'); /* convert to uppercase */
modified = 1; /* set modified flag */
}
else if (c < '0' || '9' < c) { /* am I not a digit? */
if (c == ' ') { /* am I a space ? */
putchar (c); /* output extra space */
modified = 1; /* set modified flag */
}
putchar (c); /* output unmodified char */
}
nl = 0; /* reset newlines seen to zero */
}
return modified ? 0 : 1; /* 0 - modified, 1 - stdout == stdin */
}
note: try and avoid using magic numbers in your code. If you need the ASCII value of 'A'
, then use 'A'
, not 65
. Reading thought code that has magic numbers scattered about (e.g. 65
, 32
, 10
is not nearly as informative as 'A'
, ' '
and '\n'
).
Note also, you can use the macros is ctype.h
(e.g. isupper()
, islower()
, isdigit()
, etc..) to easily test what the current character is in a very readable manner. There is also learning value in doing it manually to gain an understanding of what those convenient functions in ctype.h
are actually doing and practicing setting up your conditional tests properly.
Example Input File
Now just create a test case to exercise your character classifications and make sure you loop is working the way it should. There is nothing special required. Rather than trying to construct a myriad of assert()
statements, just create an input file that has a character that fits each case:
$ cat dat/stateloop.txt
This Is a Line followed by THREE-newlines
and with SEVEN single-spaced asterisk * * * * * * *
aND a lINE fOLLOWED bY five nEWLINES ('\N')
And A Few Numbers zERO-tO-nINE (123456789)
1
a b c d e f g - A B C D E F G
Done2Day
Example Use/Output
Then carefully check the output:
$ ./bin/stateloop <dat/stateloop.txt
tHIS iS A lINE FOLLOWED BY three-NEWLINES
AND WITH seven SINGLE-SPACED ASTERISK * * * * * * *
And A Line Followed By FIVE Newlines ('\n')
aND a fEW nUMBERS Zero-To-Nine ()
A B C D E F G - a b c d e f g
dONEdAY
Check Return Value
and validate the return
is correct.
$ echo $?
0
Try A (no-change) Case
Do the same thing for the non-modified case:
$ printf "~@##$@#$%%#*[]{};:^^&&*)\n" | ./bin/stateloop
~@###$%#*[]{};:^^&&*)
Check Appropriate Return
$ echo $?
1
As noted earlier, there are many ways to approach this type of problem. Just find something that is logical, robust, readable and understandable. Good luck with your coding.
Upvotes: 2
Reputation: 37287
It seems you're facing an XY problem.
From your description I read that the actual thing you want to do is to check whether the output is identical to the input. So the solution is simple: You just need to track if at least one character has been modified during the process, and if yes then you know the input and the output will be different.
int modified = 0;
void testChars(int iochar){
while(iochar != EOF){
if (iochar == 10){
iochar = newline(iochar);
} else if(iochar == 32) {
space(iochar);
modified++;
} else if (iochar > 64 && iochar < 91) {
upperCase(iochar);
modified++;
} else if (iochar < 123 && iochar > 96) {
lowerCase(iochar);
modified++;
} else if (iochar < 58 && iochar > 47) {
digit();
modified++;
} else {
putchar(iochar);
// Nothing modified
}
iochar = getchar();
}
}
int newline(int iochar){
int count = 0;
while (iochar == 10){
if (count < 2) {
putchar(iochar);
} else {
modified++;
}
count++;
iochar = getchar();
}
return iochar;
}
Then test if modified == 0
and you'll know it.
Upvotes: 0