C duplicate character,character by character - c

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;
}

Related

Im a beginner to C and getting an error with my code: assignment to char from char * makes integer from pointer without a cast. How could I fix this?

Im trying to write a code that gets vowels from a string and replaces them with a "*" before outputting the changed string using the functions of getchar() and putchar().
char input_char;
input_char = getchar();
while(input_char != EOF) {
if (input_char == 'a' || 'e' || 'i' || 'o' ||'u') {
input_char = "*";
}
putchar(input_char);
input_char = getchar();
}
There are two separate issues with your code.
The first is the error you are seeing in the title:
"*" implies a C-string, this is a char array [2] with the bytes '*' and '\0' respectively. Because you have accidentally specified a string instead of a character, your line input_char = "*"; is assigning input_char to be the pointer to the first character, instead of the character itself.
To see how this works, try substituting that line for the following.
char tmp[2] = "*";
input_char = tmp[0];
Which is equivalent to what you intended:
input_char = '*';
The second issue is that your if statement always returns true because the ascii characters are integers greater than zero. The logical OR denotes separate conditions which are independent of the first clause, you must specify that input_char is what's being compared for each condition. A lengthy but correct example is as below:
if (input_char == 'a' || input_char == 'e' || input_char == 'i' || input_char == 'o' || input_char == 'u') {
As an aside: If you want this to be more readable, iterating through an array is something to research.
Happy coding!

Switching letters (no strings or strings functions)

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;
}

How to format a user entered line?

THIS IS A DIFFERENT QUESTION ENTIRELY. THE OTHER ONE IS URL SPECIFIC MINE IS NOT. THAT WAS JUST AN EXAMPLE.
So here's my code:
main()
{
char input[150]; //Create a variable of type char named input to store user input
printf(" Enter a standard line: "); //Ask the user for some standard input
if (fgets(input, 150, stdin) == NULL) //Initiate a series of checks to make sure there is valid input
{
puts(" End-of-File or Input Error Detected. "); //If the end of the file is reached or there is a problem with input output a message
}
else if (input[0] == '\n') //If the user neglected to enter anything output a message
{
puts(" Oops! Looks like you forgot to enter something! ");
}
else
{
printf(" Here's what you entered: %s ", input); //If there is valid user input echo it back to the user
int i = 0;
while ( input[i] != '\n' )
{
if (input[i] = '/')
putchar("%2F");
i++
}
}
}
I have to replace and adjust the input line accordingly by replacing certain characters with their ASCII code.
For example:
1. user inputs: google.COM/search?client
2. program changes and prints back to the user as: GOOGLE.com%2FSEARCH%3FCLIENT
But the system gives me this long error message when I try to compile my code.
/home/cs/carroll/cssc0154/One/p1.c: In function 'main':
/home/cs/carroll/cssc0154/One/p1.c:41:5: warning: passing argument 1 of 'putchar' makes integer from pointer without a cast [enabled by default]
putchar("%2F");
^
In file included from /home/cs/carroll/cssc0154/One/p1.c:15:0:
/usr/include/stdio.h:580:12: note: expected 'int' but argument is of type 'char *'
extern int putchar (int __c);
Where am I going wrong?
You're trying to pass 3 characters '%' '2' 'f' to a function putchar() that expects one character.
Consider using
printf("%%2f")
instead of
putchar("%2f"), you'll get the same output in stdout.
Note: you have to escape the percent character in printf with (%).
Since I don't have the points to comment on the above...
Background:
C doesn't really have 'strings' - it has pointers to characters which it handles very much like arrays.
standard library functions expect the last value in the array to be '\0', and they will keep processing characters until one of these 'null characters' is read.
This line:
putchar("%2F");
Passes a pointer (char *) to 'putchar()' instead of an integer.
The warning is simply pointing out the type mismatch, because it's unlikely you meant to do that. (it is correct, this is a serious mistake which can crash or cause vulnerabilities depending on the way the called function operates)
Most machines/compilers nowadays are LP64. That means 'putchar()' expects a 32bit integer, and you are providing a 64bit pointer.
The reason this code:
int i = 0;
while ( input[i] != '\n' )
{
if (input[i] == '/')
puts("%2F");
i++
}
prints out "%2F" (I assume given the input "google.COM/search?client") is because it finds '/' once, and prints the string you provided "%2F". No other character executes a print statement.
If you added:
int i = 0;
while ( input[i] != '\n' )
{
if (input[i] == '/')
puts("%2F");
else
putc(intput[i]);
i++
}
you would get
google.COM%2F
search?client
because puts() prints a newline ('\n') after printing its input.
To avoid that you'll need to choose a different output function, and while many options are available, printf() is likely the best and certainly the most recognizable.
printf() prints 'formatted' text which uses '%' to indicate formatting options, so printing a single '%' requires you to 'escape' it which looks like "%%"
Here is a working example of what you're trying to do.
I replaced, what must be a large block of
if (input[i] == 'CHAR') printf("%%XX");
else if (input[i] == ...
by putting all the special characters in a string.
I simply check whether that string contains each character I'm about to print.
If the character is found, I print a '%' followed by the character's hex value. The "%02X" tells printf() to write the hex value with at least 2 characters and to put 0 in front if it needs to pad the value. Basically it will never write '9' but instead write '09'.
You could also use the isalpha() ... isalphanum() ... ispunct() functions to identify which types of characters you should encode.
#include <stdio.h>
#include <string.h>
int main(int argc, char ** argv)
{
char *decodeStr = "/:\\+";
int decodeLen = strlen(decodeStr);
while(--argc >= 0 && ++argv && *argv){
char *str = *argv;
for(int i = 0; str[i] != '\0'; i++){
if (NULL != memchr(decodeStr, str[i], decodeLen))
printf("%%%02X", str[i]);
else
putchar(str[i]);
}
putchar('\n');
}
return 0;
}
example run:
./test http://google.com/atest+with+things
http%3A%2F%2Fgoogle.com%2Fatest%2Bwith%2Bthings

getop() function K&R book p 78

I'm studying K&R book. Currently i'm reading function getop() at p.78.
I do understand the code but i need clarifications about 2 things.
The code of getop() is as follows:
int getch(void);
void ungetch(int);
/* getop: get next character or numeric operand */
int getop(char s[])
{
int i, c;
while ((s[0] = c = getch()) == ' ' || c == '\t')
;
s[1] = '\0';
if (!isdigit(c) && c != '.')
return c; /* not a number */
i = 0;
if (isdigit(c)) /* collect integer part */
while (isdigit(s[++i] = c = getch()))
;
if (c == '.') /* collect fraction part */
while (isdigit(s[++i] = c = getch()))
;
s[i] = '\0';
if (c != EOF)
ungetch(c);
return NUMBER;
}
My question is about: s[0] in:
while ((s[0] = c = getch()) == ' ' || c == '\t')
The idea behind the while loop is to skip spaces and horizontal tab, so why are we saving 'c' in s[0]? Why the authors didn't simply write:
while (c= getch() == ' ' || c == '\t')
We are not going to use spaces and tabs later on, why do we need to save c in s[0] for? What is the need for s[0] here?
My second question is about:
s[1] = '\0';
Why are we assigning '\0' (end of string) to s[1] here?
I have read some of the previous answers posted on stackoverflow.com about it but i'm not totally convinced!
The accepted answer about the above question is: "Because the function might return before the remaining input is read, and then s needs to be a complete (and terminated) string."
Ok. But what if input has one white space at the beginning and followed by an operand or operator? In this case, s[1] = '\0' will close the string too early? isn't it?
In answer to your first question, the assignment to s[0] in this case is a convenient coding shortcut. The value of c is copied to s[0] for every character read by getch(), regardless of whether it will be used or discarded. If it is to be discarded, no big deal; it will be overwritten on the next iteration of the while() loop. If it is to be used, then it has already been copied into its necessary location in the destination array s[].
In answer to your second question,
But what if input has one white space at the beginning and followed by
an operand or operator?
Note that the previous while() loop prevents white space characters (spaces and tabs) from appearing in s[0] after exit from the loop. Therefore, after execution of
s[1] = '\0';
the s[] string will consist of a single character that is neither a space nor a tab, followed by a string terminator.
In the next statement
if (!isdigit(c) && c != '.')
return c; /* not a number */
the function will return if the character is anything but a digit or a decimal point. This is why it was necessary to terminate the string.
But what if input has one white space at the beginning and followed by an operand or operator? In this case, s[1] = '\0' will close the string too early? isn't it?
Nope,
i = 0;
if (isdigit(c)) /* collect integer part */
while (isdigit(s[++i] = c = getch()))
This makes sure, that if there is something to be read, it will get overwritten on \0, as i=0 and s[++i] would mean, storing in s[1], which contains the \0
for your first question about: s[0] in:
while ((s[0] = c = getch()) == ' ' || c == '\t')
because the saving 'c' in s[0] help to storing first number in advanced so that we can start our next code from simply i equal to 1.
i = 0;
if (isdigit(c)) /* collect integer part */
while (isdigit(s[++i] = c = getch()))
the above code is used for storing next string character which is start from index i = 1
About your second question :
we can not do
s[0] = '\0';
because at that time we already stored first number in string at s[0]
see
(s[0] = c = getch())
The answers given here are already good, though i would like to add one more point on the 2nd question.
"Ok. But what if input has one white space at the beginning and followed by an operand or operator? In this case, s[1] = '\0' will close the string too early? isn't it?"
In this case we do not care about the string at all(it would be overwritten anyway if a number is encountered) because the string is used only if a decimal number is encountered , rest of the characters such as '+' or '-' or '\n' are directly returned.

Whitespace in C strings?

I have ran into a problem while learning to program in C. I am creating a program to translate a text into "rövarspråket" (you don't need to understand what it is to understand my problem). The program works great if I only insert one word, but when I add a space it just stop producing the output. Here'sn example:
If I insert "hello world", I want the output to be "hohelollolo wowororloldod", but instead it outputs "hohelollolo♀". Here's the code:
int main(){
char inputText[100], outputText[300];
int inputLength, ipTextNum, opTextNum;
scanf("%s", &inputText);
inputLength = strlen(inputText);
for(ipTextNum = 0; ipTextNum < inputLength; ipTextNum++){
if(inputText[ipTextNum] == 'a' || inputText[ipTextNum] == 'e' || inputText[ipTextNum] == 'i' || inputText[ipTextNum] == 'o' || inputText[ipTextNum] == 'u' || inputText[ipTextNum] == 'y' || inputText[ipTextNum] == 'å' || inputText[ipTextNum] == 'ä' || inputText[ipTextNum] == 'ö'){
outputText[opTextNum] = inputText[ipTextNum];
opTextNum++;
}
else {
outputText[opTextNum] = inputText[ipTextNum];
outputText[opTextNum+1] = 'o';
outputText[opTextNum+2] = inputText[ipTextNum];
opTextNum += 3;
}
}
printf("%s", outputText);
return 0;
}
If I also add inputText[ipTextNum] == ' ' to the if statement, it outputs the same thing as before, but without the ♀. Does anyone have a clue what the problem might be? I've tried to print the input, but it seems like all the text after the whitespace is terminated there too. Is whitespace even allowed in strings and if not, how do I work around this?
issue is here:
scanf("%s", &inputText);
there are actually two issues.. You want it to read an entire line I assume, so we don't use %s, we use "all characters except \n", then read the \n. The other issue is the &inputText.
scanf("%s", inputText);
is how it should be.. but this adds the entire line.
scanf("%[^\n]\n", inputText);
Go ahead and do your parsing on that, as long as you dont overflow!
scanf really shouldn't be used for strings with spaces because it does not accept them. You should really use fgets instead. See this answer for more information:
https://stackoverflow.com/a/1248017/2990189
Here is an example (from the above answer) to show how fgets works:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Maximum name size + 1. */
#define MAX_NAME_SZ 256
int main(int argC, char *argV[]) {
/* Allocate memory and check if okay. */
char *name = malloc (MAX_NAME_SZ);
if (name == NULL) {
printf ("No memory\n");
return 1;
}
/* Ask user for name. */
printf("What is your name? ");
/* Get the name, with size limit. */
fgets (name, MAX_NAME_SZ, stdin);
/* Remove trailing newline, if there. */
if ((strlen(name)>0) && (name[strlen (name) - 1] == '\n'))
name[strlen (name) - 1] = '\0';
/* Say hello. */
printf("Hello %s. Nice to meet you.\n", name);
/* Free memory and exit. */
free (name);
return 0;
}
As well as ensuring that the input does not overflow the buffer as other posters pointed out; another major issue is that you never null-terminate your output.
The printf("%s" knows when to stop outputting characters because it encounters a null byte. But you never wrote a null byte on the end of your output, which explains why there is a garbage character after it (and then you happened to have a null byte by chance in your buffer after the garbage).
After your loop, add in outputText[opTextNum] = 0; before the printf.
Of course, you also need to make sure your loop always stops with opTextNum < sizeof outputText being true. Since input has max string length 99 , then the max output that can be written is 3*99+1, so you're safe as things stand. If you decide to change your algorithm later you'll need to think about this again though.
Superficially, your problem is the use of scanf - it stops at the first space it encounters. Using fgets is usually the answer - and that might have been that. There was another problem in that you did not terminate your string (which led to the interesting last character - quite by chance, I must add), and you did not check for overflow in either the input or output buffers. Most of these points were made in other answers.
However - the problem is quite a bit more interesting, and harder, because you are using "non ascii" characters in your input. I am guessing your robbers have Scandinavian roots. This means that you not only need to worry about whitespace - you need to be using "wide characters".
I took the liberty of rewriting your example in "wide" form - meaning that it uses
fgetws - the wide equivalent of fgets
wprintf - the wide equivalent of printf
wcschr - the wide equivalent of strchr
As well as "wide" strings like L"wide string", "wide" characters like L'å', and "wide" format specifier %ls.
A stylistic problem with your code is that really long if statement that consisted of a long series of OR-ed comparisons with individual characters. You can do one of three things:
format more clearly with carriage returns and line continuations:
if( inputText[ipTextNum] == L'a' || \
inputText[ipTextNum] == L'e' || \
inputText[ipTextNum] == L'i' || \
inputText[ipTextNum] == L'o' || \
inputText[ipTextNum] == L'u' || \
inputText[ipTextNum] == L'y' || \
inputText[ipTextNum] == L'å' || \
inputText[ipTextNum] == L'ä' || \
inputText[ipTextNum] == L'ö')
Note: you did not test for space, so space is turned into o which is not what you want according to your description.
2. Replace the whole thing with wcschr which looks for a character in a string; by searching for the character in the string of vowels (this is what I did in the code below - including test for space); or
3. You could also create a function for yourself like
int isVowel(wchar_t c) {
return wcschr("aeiouyåäö ", c)!=0;
}
To make it even more compact/readable.
Anyway - here is the complete, annotated code:
#include <stdio.h> // <<<< dont forget your header files
#include <wchar.h> // <<<< to be able to use 'wide' characters: ö etc
// define the max size of input and output string:
#define IP_MAX 100
#define OP_MAX 300
int main(void) { // <<<<< use the correct function signature
wchar_t inputText[IP_MAX], outputText[OP_MAX];
int inputLength, ipTextNum, opTextNum=0; // <<< initialize opTextNum to zero!!
// scanf("%s", &inputText); // <<<< triple NO:
// 1) scanf stops at the first space
// 2) if the input is very long you overwrite your buffer
// 3) you are passing the POINTER to the pointer to char;
// you should use inputText not &inputText
fgetws(inputText, IP_MAX, stdin); // read at most IP_MAX characters into inputText including '\0'
inputLength = wcslen(inputText); // length of wide C string
inputText[--inputLength]='\0'; // strip carriage return
// printf("The length of the string entered is %d\n", inputLength);
wprintf(L"you entered \n'%ls'\n", inputText);
for(ipTextNum = 0; ipTextNum < inputLength && opTextNum < OP_MAX-3; ipTextNum++) {
if(wcschr(L"aeiouyåäö ", inputText[ipTextNum])) { // <<< include test for space too
outputText[opTextNum] = inputText[ipTextNum];
opTextNum++;
}
else {
outputText[opTextNum] = inputText[ipTextNum];
outputText[opTextNum+1] = 'o';
outputText[opTextNum+2] = inputText[ipTextNum];
opTextNum += 3;
}
}
outputText[opTextNum]=L'\0'; // nul terminate the string
wprintf(L"The robber said:\n'%ls'\n\n", outputText);
return 0;
}
Output:
hello world
you entered
'hello world'
The robber said:
'hohelollolo wowororloldod'

Resources