I wrote a code that takes a sentence and outputs every each word in a line. But I also want to write the size of each word next to it.
Input:
Hi my name is
Current output:
Hi
my
name
is
Desired output:
Hi(2)
my(2)
name(4)
is(2)
My current Code:
#include <stdio.h>
#define MAX 100
int main(void) {
int c = 0;
size_t n = 0;
printf("\n Enter a sentence.\n\n input: ");
/* read up to 100 characters from stdin, print each word on a line */
while (n < MAX && (c = getchar()) != EOF && c != '\n')
{
if (c == ' ')
printf("\n");
else
printf("%c", c);
n++;
}
printf("\n");
if (n == MAX) /* read and discard remaining chars in stdin */
while ((c = getchar()) != '\n' && c != EOF);
return 0;
}
How can I do that?
For completeness a different approach reading the whole input in one call and then tokenising it:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAX (100)
int main(void)
{
int result = EXIT_SUCCESS; /* Be optimistic. */
char s[MAX +1];
printf("\n Enter a sentence.\n\n input: ");
/* read up to 100 characters from stdin, print each word on a line */
if (NULL == fgets(s, sizeof s, stdin))
{
if (ferror(stdin))
{
perror("fgets() failed");
result = EXIT_FAILURE;
}
}
else
{
s[strcspn(s, "\r\n")] = '\0'; /* chop off carriage return, line feed, if any */
for (char * pc = strtok(s, " "); NULL != pc; pc = strtok(NULL, " "))
{
printf("%s (%zu)\n", pc, strlen(pc));
}
}
return result;
}
As the read buffer is never explicitly used the following variation is possible as well:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define MAX (100)
int main(void)
{
int result = EXIT_SUCCESS; /* Be optimistic. */
printf("\n Enter a sentence.\n\n input: ");
{
/* read up to 100 characters from stdin, print each word on a line */
char * pc = fgets((char[MAX+1]), MAX+1, stdin);
if (NULL == pc)
{
if (ferror(stdin))
{
perror("fgets() failed");
result = EXIT_FAILURE;
}
}
else
{
pc[strcspn(pc, "\r\n")] = '\0'; /* chop off carriage return, line feed, if any */
for (pc = strtok(pc, " "); NULL != pc; pc = strtok(NULL, " "))
{
printf("%s (%zu)\n", pc, strlen(pc));
}
}
}
return result;
}
Have one more variable and print out when you hit space.
size_t len = 0;
/* read up to 100 characters from stdin, print each word on a line */
while (n < MAX && (c = getchar()) != EOF && c != '\n')
{
if (c == ' ') {
printf("(%u)\n", len);
len = 0;
}
else {
len++;
printf("%c", c);
}
n++;
}
In addition to the good answer by #kiranBiradar, you may want to add an additional variable that allows you to track whether you are in-a-word reading characters or outside-a-word reading whitespace. (using a simple int value as a flag set to 1 (true) for in-word or 0 (false) not-in-word is all you need) This will allow you to ignore leading whitespace, multiple included whitespace, or trailing whitespace in your input, e.g., if your input was similar to:
" my dog has fleas and my cat has none "
Unless you are keeping track of the state of your read, whether you are in-word / not-in-word, you will be outputting multiple occurrences of "(0)\n" each time a whitespace character is read. By keeping a flag of whether you are in / not-in a word and setting it zero when you encounter your first whitespace after being in-word reading non-whitespace characters allows you to only output the length once on the first whitespace encountered.
Additionally, conditioning your read on c != '\n' will skip outputting the length of the final word, unless you include additional code after you exit your read loop.
Also by including <ctype.h> you have the isspace() macro available to check for all whitespace (e.g, space, tab, newline, backspace, vertical-tab, etc...) It greatly simplifies your conditional checks.
Putting it altogether you could do:
#include <stdio.h>
#include <ctype.h> /* for isspace() */
int main (void) {
int c = 0, in = 0, len = 0; /* char, in/out flag, length */
fputs ("enter text: ", stdout); /* prompt for text */
fflush (stdout); /* (optional), but recommended */
while ((c = getchar()) != EOF) { /* loop reading chars until EOF */
if (isspace (c)) { /* if input is space */
if (in) { /* check if in-word */
printf ("(%d)\n", len); /* output (len) */
len = 0; /* reset len zero */
in = 0; /* set in flag zero (false) */
}
if (c == '\n') /* if space is \n */
break; /* break read loop */
}
else { /* if not whitespace */
putchar (c); /* output char */
len++; /* increment length */
in = 1; /* set in flag 1 (true) */
}
}
}
(note: there is no reason to limit your read to n < MAX unless you simply want to arbitrarily limit your read of characters to the first 100-characters. There is no array being filled or other storage being occupied by the character c (aside from it's one byte). You could read billions if your input contained them)
Example Use/Output
$ ./bin/getchar_word_len
enter text: my dog has fleas and my cat has none
my(2)
dog(3)
has(3)
fleas(5)
and(3)
my(2)
cat(3)
has(3)
none(4)
Look over both answers and let either of us know if you have further questions. If you are confused by the logic, take out an 8.5x11 sheet of paper and work through the logic of the loop for each character starting at the beginning of your input. It will make sense by the time you work through the first word.
Related
Problem: take a string and move every character in the alphabet 13 times forward; for example 'hello' would be 'uryyb', but the trick here is that if there is a vowel in the element before then its 14 spaces so it would be 'urzyb'. I got the 14 space but then nothing else happens to the other letters so I keep getting 'hezlo', but if remove the // and use this line of code
message[i] = message[i] + key;`
then it doesn't do the 14 and only does 13 times. Can someone explain what I'm doing wrong and how to fix it?
#include<stdio.h>
int main()
{
char message[100], ch;
int i, key;
printf("Enter a message to encrypt: ");
gets(message);
printf("Enter key: ");
scanf("%d", &key);
for(i = 0; message[i] != '\0'; ++i){
if(message[i] >= 'a' && message[i-1] <= 'z'){
if(message[i-1] == 'e'){
message[i]=message[i] + 14;
}
//message[i] = message[i] + key;
if(message[i] > 'z'){
message[i] = message[i] - 'z' + 'a'-1 ;
}
message[i] = message[i];
}
}
printf("Encrypted message: %s", message);
return 0;
}
Output is hezlo
should be urzyb
I have three advises for you.
Don't use gets, it is deprecated and for good reason, use fgets instead.
Since you are modifying the message character by character. You cannot look back at the previous character using message[i-1] to see if that was a wovel, because it was already shifted in the previous iteration of the loop. store the previous character in a separate variable instead.
Since you are wrapping back to 'a' when you reach 'z', consider using the modulus arithmetic, which is used to cycle the numbers in a given range.
see the code below with these ideas applied to your code.
int main()
{
// ...
printf("Enter a message to encrypt: ");
fgets(message,100,stdin);
printf("Enter key: ");
scanf("%d", &key);
char p = 1; // some initial value that is not a vowel.
for(i = 0; message[i] != '\0'; ++i){
if(message[i] >= 'a' && message[i] <= 'z'){
char ch = (message[i]-'a' + key) % 26 + 'a'; // using modular math
if (strchr("aeiou",p)){
ch++; // increment by 1 if previous character was a vowel
}
p = message[i]; // save previous character
message[i]=ch; // update message.
}
}
printf("Encrypted message: %s", message);
return 0;
}
First of all, pay attention to things like this one:
if(message[i-1] == ...
because on the first iteration the index i is 0: so, i-1 is negative, and does not address a valid character of the string. This is an error. The way to solve this is that if the index (i) is 0, then there is no previous character, so you know it can not be a vowel.
Second, you state that you want to "slide" the characters: then you must include a statement to do that, like this:
message[i] = message[i] + key;
Try to describe the algorithm in plain english, then translate it in C code. Something like this:
Scan all characters, and add 13 to each; but if the preceding char is a vowel, than add 14 instead of 13.
The direct outcome of the previous sentence goes like this:
for(i = 0; message[i] != '\0'; i++) {
if (i > 0) {
// we are past the first letter
if (message[i-1]=='a' || message[i-1]=='e' || ...)
message[i] += 14;
else message[i] += 13;
} else {
// it's the first letter, there can not be a preceding vowel
message[i] += 13;
}
}
The above algorithm has some problem - mainly, it does tests for vowels on the already modified message. And it is slightly too verbose. Better to do something like this (warning, untested):
int wasvowel = 0;
char previous;
for(i = 0; message[i] != '\0'; i++) {
previous = message[i]; // save for later
if (wasvowel)
message[i] += key+1;
else message[i] += key;
wasvowel = previous=='a' || previous=='e' ...
}
The code above misses some checks; it is not clear what to do if the translated char becomes a not-letter, for example. But the general idea is there.
A note about the variable "previous" (perhaps the name is not very correct). The algorithm must consider each char in order to determine whether it is a vowel or not. But this check must be made before changing the string. Imagine what happens with words having two vowels in a row, like "aerial". The letter 'e' must be slided 14 times, ok, but the letter 'r' too. So we must remember that the letter before the 'r' was a vowel, and to do that we must preserve the 'e'. There are other methods to do that, but this one is simple.
Another note about the variable wasvowel. Prior to the cycle its value is set to 0: of course there is no vowel before the start of the message. Then in the cycle wasvowel is set, ready for the next iteration. In the last iteration, wasvowel is set again, but the value will be never used. This is, I think, acceptable in this context - it is possible that, in another context, it would not.
Because you are overwriting the main string, the e becomes r and then you can't read e to compare to anymore. That's why you are getting uryyb instead of urzyb. This is a modified code with an alternative vowel check method and another buffer for the modified string, keeping the original intact:
#include <stdio.h>
#include <string.h>
int main()
{
unsigned char message[100], original[100], ch;
int i, key, len;
memset(message, 0, 100);
memset(original, 0, 100);
printf("Enter a message to encrypt: ");
scanf("%s", original);
printf("Enter key: ");
scanf("%d", &key);
for(i = 0, len = strlen(original); i < len; i++){
if(original[i] >= 'a' && original[i] <= 'z'){
if(i > 0 && memchr("aeiou", original[i-1], 5)){
message[i] = original[i] + 14;
}else{
message[i] = original[i] + 13;
}
}
}
for(i = 0, len = strlen(message); i < len; i++){
if(message[i] > 'z'){
message[i] = message[i] - 'z' + 'a' - 1;
}
}
printf("Encrypted message: %s", message);
return 0;
}
Edit: moved the overflow fix outside another loop.
Edit: forgot an crucial part, chars need to be unsigned.
https://onlinegdb.com/ryo2dKR5H
First, don't use gets() it is so vulnerable to exploit by buffer overrun is has been completely removed from the current C standard library, see Why gets() is so dangerous it should never be used!
While there is nothing wrong with reading and buffering your input (using fgets() or POSIX getline()), there is no real need to do so if you simply want to output the encrypted input. You can use getchar() to read each character of input and simply convert each character as they are read.
Regardless whether you buffer the input of just convert it on the fly, organizing your +13 (or if previous char was a vowel +14) logic into a simple function that takes the current character c and the previous character prev can help keep your logic straight and code clean, e.g.
/* simple function to encrypt lowercase chars,
* prev == vowel, c + 14, else c + 13
*/
char encrypt (const char c, const char prev)
{
char enc;
if (!islower (c)) /* validate c is lowercase */
return c;
if (prev == 'a' || prev == 'e' || prev == 'i' || /* if prev is vowel */
prev == 'o' || prev == 'u')
enc = 'a' + (c - 'a' + 14) % 26; /* add 14 to c */
else
enc = 'a' + (c - 'a' + 13) % 26; /* add 13 to c */
return enc; /* return encrypted char */
}
(note: the validation of the input with islower() from the ctype.h header ensures your conversion is applied to only lowercase characters. You can expand it to handle both cases -- that is left to you)
Also note the logic of the addition wraps back to the beginning in case adding +13 (or +14) would result in a character beyond 'z'. (for example 'z' + 13 == 'm'). You can adjust as required.
Then your code becomes simply:
int main (void) {
int c, prev = 0;
fputs ("Enter a message to encrypt: ", stdout); /* prompt */
while ((c = getchar()) != '\n' && c != EOF) { /* read each char */
putchar (encrypt (c, prev)); /* output encrypted */
prev = c; /* save current as prev */
}
putchar ('\n'); /* tidy up with newline */
}
Putting it altogether in a short example and adding the two required header files, you could do:
#include <stdio.h>
#include <ctype.h>
/* simple function to encrypt lowercase chars,
* prev == vowel, c + 14, else c + 13
*/
char encrypt (const char c, const char prev)
{
char enc;
if (!islower (c)) /* validate c is lowercase */
return c;
if (prev == 'a' || prev == 'e' || prev == 'i' || /* if prev is vowel */
prev == 'o' || prev == 'u')
enc = 'a' + (c - 'a' + 14) % 26; /* add 14 to c */
else
enc = 'a' + (c - 'a' + 13) % 26; /* add 13 to c */
return enc; /* return encrypted char */
}
int main (void) {
int c, prev = 0;
fputs ("Enter a message to encrypt: ", stdout); /* prompt */
while ((c = getchar()) != '\n' && c != EOF) { /* read each char */
putchar (encrypt (c, prev)); /* output encrypted */
prev = c; /* save current as prev */
}
putchar ('\n'); /* tidy up with newline */
}
Example Use/Output
$ ./bin/charencrypt
Enter a message to encrypt: hello
urzyb
Look things over and let me know if you have further questions.
Edit - Converting an Array of Chars
To read into a buffer (array) of char and then convert each character in the buffer based on the same logic, requires little change. The only change required is instead of converting each character on-the-fly, you read your input into a buffer, then loop over each character in the buffer making the conversions. (only caveat, you must save the prev/last char before making the conversion so you know how to correctly apply the next conversion)
The changes needed to the above are minimal. The follow uses the exact same encrypt function and a single character array to store what is read from the user and then the conversions are made in-place updating the characters in the same array, e.g.
#include <stdio.h>
#include <ctype.h>
#define MAXC 2048 /* don't skimp on buffer size (except for embedded dev.) */
/* simple function to encrypt lowercase chars,
* prev == vowel, c + 14, else c + 13
*/
char encrypt (const char c, const char prev)
{
char enc;
if (!islower (c)) /* validate c is lowercase */
return c;
if (prev == 'a' || prev == 'e' || prev == 'i' || /* if prev is vowel */
prev == 'o' || prev == 'u')
enc = 'a' + (c - 'a' + 14) % 26; /* add 14 to c */
else
enc = 'a' + (c - 'a' + 13) % 26; /* add 13 to c */
return enc; /* return encrypted char */
}
int main (void) {
char buf[MAXC], *p = buf; /* buffer and pointer to buffer */
int current, prev = 0;
fputs ("Enter a message to encrypt: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read into buffer */
fputs ("(user canceled input)\n", stdout);
return 1;
}
while (*p && *p != '\n') { /* read each char */
current = *p; /* save current */
*p = (encrypt (*p, prev)); /* encrypt char */
prev = current; /* set prev to current */
p++; /* advance to next char */
}
fputs (buf, stdout); /* output converted buffer */
}
How you loop over the characters in the array is up to you. You can use a for loop with array indexes, or simply use a pointer and advance the pointer to the next character on each iteration until you reach the nul-terminating character or '\n' character, each of which would signify the end of the characters you are converting.
If you want to use a second array so that you preserve both the original and have the new encrypted array, just declare another array and fill it in the same manner, except instead of writing the converted values back to the original array, write it to your second array (don't forget to nul-terminate the second array)
Example Use/Output
The out is exactly the same:
$ ./bin/charencryptbuf
Enter a message to encrypt: hello
urzyb
Let me know if you have further questions.
Edit Based On Comment To Encrypt Multiple-Words
If you simply want to prompt the user for the number of words to encrypt, that as in my comment, you just need to wrap what you are doing in a loop. Prompt the user for the number of words to encrypt, then loop that number of times encrypting each word. The changes to the code above are minimal, e.g.
int main (void) {
char buf[MAXC]; /* buffer to hold input */
int nwords; /* no. of words to encrypt */
fputs ("How may words to encrypt?: ", stdout); /* prompt no. of words */
if (!fgets (buf, MAXC, stdin) || sscanf (buf, "%d", &nwords) != 1 ||
nwords < 1) {
fputs ("error: invalid integer input or nothing to do.\n", stderr);
return 1;
}
for (int i = 0; i < nwords; i++) {
char *p = buf; /* pointer to buf */
int current, prev = 0; /* current and prev chars */
printf ("\nenter word[%d]: ", i + 1); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read into buffer */
fputs ("(user canceled input)\n", stdout);
return 1;
}
while (*p && *p != '\n') { /* read each char */
current = *p; /* save current */
*p = (encrypt (*p, prev)); /* encrypt char */
prev = current; /* set prev to current */
p++; /* advance to next char */
}
fputs (buf, stdout); /* output converted buffer */
}
}
Example Use/Output
$ ./bin/charencryptbufmulti
How may words to encrypt?: 6
enter word[1]: hello
urzyb
enter word[2]: there
gurfr
enter word[3]: how
ubk
enter word[4]: are
nfr
enter word[5]: you
lbi
enter word[6]: hello
urzyb
Separating And Encrypting Any Number of Words Entered by the User Individually
To encrypt multiple words separately, you just need to do what you are doing for the whole string but separating the input into tokens (individual words). C provides the strtok() function to do just that, but you will want to make a copy of the entire input string if you need to preserve it as strtok() modifies the original. You can also simply make a copy of each token to preserve the original word and encrypted word separately.
An easy way to implement the addition is just to write a small wrapper-function that takes whole words as an input parameter and then passes the word to the existing encrypt() function. So for the encrypt-word (or encrypt-wrapper), you could do:
/** Simple wrapper function that takes a word and passes it to encrypt */
char *encryptw (char *buf)
{
char *p = buf; /* pointer to buffer holding word */
int current, prev = 0; /* current and previous characters */
while (*p) { /* read each char */
current = *p; /* save current */
*p = (encrypt (*p, prev)); /* encrypt char */
prev = current; /* set prev to current */
p++; /* advance to next char */
}
return buf;
}
(note: the char* type and return buf; is just a convenience to allow you make immediate use of the encrypted word, e.g. char word[] = "hello"; puts (encryptw (word)); Also note, encryptw() modifies the input, so you could not pass a string-literal, e.g. encryptw("hello");)
Having moved the code that encrypts a word into the encryptw() function, all you need to do in main() is separate the words into tokens and pass each token to encryptw() to encrypt and then output the results. You must include string.h for strtok() as well as for strcpy(), e.g.
#include <string.h>
...
#define MAXW 128 /* max individual word size to encrypt */
#define DELIM " \t\n" /* strtok delimiters */
...
int main (void) {
...
/* loop separating buf into individual words to encrypt */
p = strtok (buf, DELIM); /* 1st call - pass buf */
while (p) { /* validate return not NULL */
strcpy (word, p); /* make copy, to preserve original */
encryptw (word); /* pass word to encryptw to encrypt word */
/* output word, original and encrypted */
printf ("word[%2zu] : %-12s : (%s)\n", ++n, p, word);
p = strtok (NULL, DELIM); /* all subsequent calls - pass NULL */
}
}
(note: above the output is now the word number encrypted, e.g. word[1].. followed by the original word and then the encrypted word in parenthesis)
The full code containing all changes would be:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXC 2048 /* don't skimp on buffer size (except for embedded dev.) */
#define MAXW 128 /* max individual word size to encrypt */
#define DELIM " \t\n" /* strtok delimiters */
/* simple function to encrypt lowercase chars,
* prev == vowel, c + 14, else c + 13
*/
char encrypt (const char c, const char prev)
{
char enc;
if (!islower (c)) /* validate c is lowercase */
return c;
if (prev == 'a' || prev == 'e' || prev == 'i' || /* if prev is vowel */
prev == 'o' || prev == 'u')
enc = 'a' + (c - 'a' + 14) % 26; /* add 14 to c */
else
enc = 'a' + (c - 'a' + 13) % 26; /* add 13 to c */
return enc; /* return encrypted char */
}
/** Simple wrapper function that takes a word and passes it to encrypt */
char *encryptw (char *buf)
{
char *p = buf; /* pointer to buffer holding word */
int current, prev = 0; /* current and previous characters */
while (*p) { /* read each char */
current = *p; /* save current */
*p = (encrypt (*p, prev)); /* encrypt char */
prev = current; /* set prev to current */
p++; /* advance to next char */
}
return buf;
}
int main (void) {
char buf[MAXC], *p = buf, /* buffer and pointer to buffer */
word[MAXW]; /* array for word to encrypt */
size_t n = 0;
fputs ("Enter a message to encrypt: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read into buffer */
fputs ("(user canceled input)\n", stdout);
return 1;
}
putchar ('\n');
/* loop separating buf into individual words to encrypt */
p = strtok (buf, DELIM); /* 1st call - pass buf */
while (p) { /* validate return not NULL */
strcpy (word, p); /* make copy, to preserve original */
encryptw (word); /* pass word to encryptw to encrypt word */
/* output word, original and encrypted */
printf ("word[%2zu] : %-12s : (%s)\n", ++n, p, word);
p = strtok (NULL, DELIM); /* all subsequent calls - pass NULL */
}
}
Example Use/Output
Combining your original string from your question "hello" with the phrase from your last comment "how many words you want to encrypt", running the program and passing the combined string would result in:
$ ./bin/charencryptbufmulti
Enter a message to encrypt: hello how many words you want to encrypt
word[ 1] : hello : (urzyb)
word[ 2] : how : (ubk)
word[ 3] : many : (znbl)
word[ 4] : words : (jbfqf)
word[ 5] : you : (lbi)
word[ 6] : want : (jnbg)
word[ 7] : to : (gb)
word[ 8] : encrypt : (rbpelcg)
Let me know if that is what you described in your last comment and let me know if you have further questions.
I am required to write a code where I constantly read a string input from the user and store it in the same variable. Each time an input is received the string will be concatenated onto a dynamic array (so the dynamic array grows bigger and bigger). It will stop reading the input from the user when the input contains '#'.
Expected inputs and outputs should be:
inputs output
here I am #abc hereIam
there you are #12 thereyouare
Here's the code I've done:
#include<stdio.h>
#include<stdlib.h> // for malloc
#include<string.h> // for string funs
int main(void){
char input1[256];
char *combined = malloc(sizeof(char));
int i = 0;
while (input1[i]!= '#'){
// read in the arrays
printf("Enter a string (max 256 char) ");
scanf("%256s",input1);
// find out string lengths
int len1;
len1=strlen(input1);
// allocate an array big enough for both
combined=realloc(combined, sizeof(char)*(len1));
//concatenate
strcat(combined,input1);
}
// print
printf("%s\n",combined);
return 0;
}
This code I have here have several problems:
I have no idea how to check if elements other than the first element of the user's input is '#' or not.
Even if the input contains '#', the output will still contain that input where '#' is in it.
Can anyone give me hints on how to solve this? Thank you!
You may be making this harder on yourself that need be. While your heading a 2-column output adds a bit of formatting challenge, the easiest way to handle the input and classification (store-it/ignore-it) is with a character-oriented approach using getchar() or fgetc().
That way you simply continually read from the input and check if the character is a '#' or '\n', if so, stop storing characters in your buffer and just read and output the rest. After your loop completes, you simply need to nul-terminate your final buffer, compute the whitespace needed between the end of the original and the output of your buffer content, write the spaces and the final buffer and you are done. A short example would be:
#include <stdio.h>
#include <ctype.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char buf[MAXC];
int c, idx = 0, nc = 0, ws = 0;
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
puts ("inputs output"); /* output headings */
while ((c = fgetc(fp)) != EOF) { /* read each char until EOF */
if (c == '#' || c == '\n') { /* if # or \n, end of storage */
buf[idx] = 0; /* nul-terminate buffer at idx */
putchar (c); /* output delim in orig string */
nc++; /* increment no. of char */
while ((c = fgetc(fp)) != '\n' && c != EOF) { /* print rest */
putchar (c);
nc++;
}
ws = 20 - nc; /* compute amount of whitespace to col */
while (ws--) /* output that many spaces */
putchar (' ');
printf ("%s\n", buf); /* print the stored buffer */
idx = 0, nc = 0; /* reset index and counter */
continue; /* go get next char */
}
else if (isalnum (c)) /* if alnum char add to buffer */
buf[idx++] = c;
putchar (c); /* output all chars until # */
nc++; /* increment no. of chars */
}
buf[idx] = 0; /* nul-terminate final line after loop */
ws = 20 - nc; /* set number of whitespace needed to 2nd col */
while (ws--) /* write that number of spaces */
putchar (' ');
printf ("%s\n", buf); /* output string without whitespace in buf */
if (fp != stdin) /* close file if not reading stdin */
fclose (fp);
return 0;
}
Example Input File
$ cat dat/pounddelim.txt
here I am #abc
there you are #12
Example Use/Output
Running the program on your input produces the "Expected inputs and outputs":
$ ./bin/pounddelim <dat/pounddelim.txt
inputs output
here I am #abc hereIam
there you are #12 thereyouare
Look things over and let me know if you have further questions.
I want read data from console and output to Text file with reserve of N character per variable of structure type.
The Text file is similar to:
1 111 1 Peter
22 22 2 John Lays
3 3 3 Anne Belgs
I do not know if I'm using the most correct functions.
Also I can not read ("carro.name") more than 1 word (example: John Lays)
struct estruturaCarro {
int id, potencia, avariado;
char name[11];
} carro;
...
//Read data to Text File:
...
printf("\n ID......:"); scanf("%d", &carro.id);
printf("\n Potencia:"); scanf("%d", &carro.potencia);
printf("\n Avariado:"); scanf("%d", &carro.avariado);
printf("\n NAME:"); scanf("%10[0-9a-zA-Z ]", carro.name); // or scanf("%[^\n]s",...)
fprintf(fp, "%-2d %-3d %-1d %-10s \n\n", carro.id, carro.potencia, carro.avariado, carro.name);
...
//Show File Text data:
...
int registos=0;
while(1)
{
fscanf(fp, "%d %d %d %-10s", &carro.id, &carro.potencia, &carro.avariado, carro.name);
if(feof(fp)){ break; }
printf("%-2d %-3d %-1d %-10s\n", carro.id, carro.potencia, carro.avariado, carro.name);
registos++;
}
printf("\nCarros=%d", registos);
As you say in your question you cannot use scanf to read a complex name including spaces.
But before to search how to do it is needed to decide what to do.
Probably you do not want to memorize the extra spaces at the beginning and at the end (including the newline), and probably a name must not be empty.
But what about inside a complex name ? If the user enter John Lays do you save the name with the two spaces or you want to simplify to have only one ? Do you have to manage other special character like '-' (are John - Lays / John- Lays / John -Lays read as John-Lays ?).
What to do if the input string is longer than 10 characters ? Just to stop to read letting the rest for the next read or to bypass up to a newline ? Because you print a message before each input you clearly want an input per line and the rest of the line must be bypassed.
If you do not want to read the string as it is enter the best way is probably to write your own read string function.
You also have to decide what to do if the user do not enter a number for ID or Potencia or Avariado, currently you do not even detect the error, this is not a good way. So in that case do you abort all (exit program), or you redo the read ? Probably you prefer to read again, for that you need to bypass the invalid input, but what that means, to bypass all up to a newline ?
For instance :
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/* read an int memorizing its value in v,
return 0 in case of EOF else a non null value */
int readInt(const char * msg, int * v)
{
for (;;) {
fputs(msg, stdout);
if (scanf("%d", v) == 1)
return 1;
/* not a number or EOF, bypass all up to \n */
int c;
while ((c = fgetc(stdin)) != '\n')
if (c == EOF)
return 0;
puts("invalid value"); /* message may be also get in argument */
}
}
/* read a string up to a \n
remove extra spaces at the beginning and end
simplify internal multiple spaces
accept any character and do not manage in a special way characters like like '-'
a non empty string must be read
read at most sz-1 characters in s then place the null character (as fgets), sz must be > 1
if the line too long bypass the rest of the input up to \n
return 0 in case of EOF else a non null value */
int readStr(const char * msg, char * s, size_t sz)
{
fputs(msg, stdout);
/* read the first char bypassing spaces including \n */
if (scanf(" %c", s) == 0)
// EOF
return 0;
size_t index = 1;
int c;
sz -= 1;
while (index != sz) {
c = fgetc(stdin);
if ((c == EOF) || (c == '\n'))
break;
if (!isspace(c))
s[index++] = c;
else if (s[index - 1] != ' ')
s[index++] = ' ';
}
s[(s[index - 1] != ' ') ? index : index-1] = 0;
// bypass possible rest of the line
while ((c != EOF) && (c != '\n'))
c = fgetc(stdin);
return 1;
}
/* ******************* */
struct estruturaCarro {
int id, potencia, avariado;
char name[11];
} carro;
int main()
{
do {
if (!readInt("\n ID......:", &carro.id) ||
!readInt("\n Potencia:", &carro.potencia) ||
!readInt("\n Avariado:", &carro.avariado) ||
!readStr("\n NAME:", carro.name, sizeof(carro.name))) {
puts("EOF");
return -1;
}
else
printf("%-2d %-3d %-1d '%-10s' \n\n", carro.id, carro.potencia, carro.avariado, carro.name);
} while (strcmp(carro.name, "end"));
return 0;
}
Compilation and execution:
pi#raspberrypi:/tmp $ gcc -pedantic -Wextra -Wall r.c
pi#raspberrypi:/tmp $ ./a.out
ID......:aze
invalid value
ID......:qsd
invalid value
ID......:1
Potencia:2
Avariado:3
NAME:aze u iiiiiiiiiiiiiiiiii
1 2 3 'aze u iiii'
ID......:11
Potencia:22
Avariado:0
NAME: end
11 22 0 'end '
pi#raspberrypi:/tmp $
When you read in your file and supposing it was produced doing fprintf(fp, "%-2d %-3d %-1d %-10s", ...) :
char line[21]; /* each line has 20 characters newline included */
while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "%d %d %d", &carro.id, &carro.potencia, &carro.avariado) != 3)
/* abnormal case, invalid file */
break; /* anything else you want to do */
/* the string starts at the index 9 and has 10 characters out of the newline */
memcpy(carro.name, line + 9, 10);
carro.name[10] = 0;
/* ... */
}
note the name have spaces at the end if its length is less than 10 characters
Or you can read in a way similar to the previous on stdin.
Hello sorry if Im not understandable, Im new in c programming and Im not the best English writer.
My question: I cant understand how can I use backspace while using the code and I glad if someone could explain me how it works.
#include <stdio.h>
int main()
{
char name[30], ch;
int i = 0;
printf("Enter name: ");
while(ch != '\n') // terminates if user hit enter
{
ch = getchar();
name[i] = ch;
i++;
}
name[i] = '\0'; // inserting null character at end
printf("Name: %s", name);
return 0;
}
When I run this program I can actually write my name and while I'm writing I can use backspace to delete characters and then continue writing, how is that possible? because from what I understand this code enters any char to name array right after I'm tapping.
Thank you, Jonatan.
Yoni, you have two good answers, but for completeness, I'll provide the remaining information you had questions with in the comments.
First when taking any input your want to display and allow minimal user editing of, you will need to put your keyboard in non-cannonical mode so that each keypress is available to your program as the key is typed -- no need to wait for the user to press Enter. You handle this with tcgetattr (terminal get attributes) and tcsetattr (terminal set attributes). *non-cannonical mode is what windows getch() provides.
Essentially you set up your read-loop to read with getchar or fgetc (if you want to be able to read the values from stdin or a file.) You control the read loop with:
#define MAXPW 32 /* constant for max input length */
int main (int argc, char **argv) {
int c,
idx = 0; /* buf index */
char pw[MAXPW] = "", /* buf for passwd */
mask = argc > 1 ? *argv[1] : 0; /* mask off by default */
FILE *fp = stdin;
...
/* read chars from fp, mask w/mask char */
while ((idx + 1 < MAXPW && (c = fgetc (fp)) != '\n' && c != EOF) ||
(idx == MAXPW - 1 && c == 127))
{
Note carefully, you are reading a character while (1) (space_remains AND c isn't '\n' or EOF) OR (2) (space_remains AND backspace_key_pressed)
In that even you handle the two cases (1) was it a normal char - add it; or (2) was it a backspace character, then backup, overwrite char with space and backup again, e.g.
if (c != 127) { /* not the backspace characters */
if (' ' - 1 < mask && mask < 127) /* if mask valid ASCII */
fputc (mask, stdout); /* output mask char */
else
fputc (c, stdout); /* output normal char */
pw[idx++] = c; /* store char, adv index */
}
else if (idx > 0) { /* handle backspace (del) */
fputc (0x8, stdout); /* backup */
fputc (' ', stdout); /* overwrite with space */
fputc (0x8, stdout); /* backup again */
pw[--idx] = 0; /* nul-termiante after current */
}
note: if your mask character is a printable character, then the mask character is output, e.g.
enter passwd: ********
If the mast is not printable (default is nul), then the text is output. You can set the mask character as the 1st argument to the program (in quotes), e.g.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <termios.h>
#include <errno.h> /* for errno */
#include <unistd.h> /* for EINTR */
#define MAXPW 32 /* constant for max input length */
int main (int argc, char **argv) {
int c,
idx = 0; /* buf index */
char pw[MAXPW] = "", /* buf for passwd */
mask = argc > 1 ? *argv[1] : 0; /* mask off by default */
FILE *fp = stdin;
struct termios old_kbd_mode; /* orig keyboard settings */
struct termios new_kbd_mode;
if (tcgetattr (0, &old_kbd_mode)) { /* save orig settings */
fprintf (stderr, "%s() error: tcgetattr failed.\n", __func__);
return -1;
} /* copy old to new */
memcpy (&new_kbd_mode, &old_kbd_mode, sizeof(struct termios));
/* put keyboard in non-canonical/no echo mode */
new_kbd_mode.c_lflag &= ~(ICANON | ECHO); /* new kbd flags */
new_kbd_mode.c_cc[VTIME] = 0;
new_kbd_mode.c_cc[VMIN] = 1;
if (tcsetattr (0, TCSANOW, &new_kbd_mode)) {
fputs ("error: tcsetattr failed.\n", stderr);
return -1;
}
fputs ("enter passwd : ", stdout); /* set passwd prompt */
/* read chars from fp, mask w/mask char */
while ((idx + 1 < MAXPW && (c = fgetc (fp)) != '\n' && c != EOF) ||
(idx == MAXPW - 1 && c == 127))
{
if (c != 127) { /* not the backspace characters */
if (' ' - 1 < mask && mask < 127) /* if mask valid ASCII */
fputc (mask, stdout); /* output mask char */
else
fputc (c, stdout); /* output normal char */
pw[idx++] = c; /* store char, adv index */
}
else if (idx > 0) { /* handle backspace (del) */
fputc (0x8, stdout); /* backup */
fputc (' ', stdout); /* overwrite with space */
fputc (0x8, stdout); /* backup again */
pw[--idx] = 0; /* nul-termiante after current */
}
}
pw[idx] = 0; /* null-terminate final string */
/* restore original keyboard mode */
if (tcsetattr (0, TCSANOW, &old_kbd_mode)) {
fputs ("error: tcsetattr failed.\n", stderr);
return -1;
}
printf ("\nstored passwd: %s\n", pw);
}
An Editable Input
Running the program without a mask, say the user enters:
$ ./bin/backspace
enter passwd : my_password_is_bad
(the user thinks about and says "oh, that's not good" and can now hit the backspace key 3 times which leaver her looking at:
$ ./bin/backspace
enter passwd : my_password_is_
Now she completes her input:
$ ./bin/backspace
enter passwd : my_password_is_good
stored passwd: my_password_is_good
The operations work exactly the same with the mask character displayed. With all mask characters displayed and the user forgot what she had entered, she could simply backspace over all characters show (and keep smashing the backspace key if she feels like it, and then proceed again to enter a correct password (name, whatever). Example with a mask char of '*', e.g.
$ ./bin/backspace '*'
enter passwd : *******************
stored passwd: my_password_is_good
It's a handy way to provide minimal edit capabilities to the user in some circumstances. If you don't need to mask the user input, then you can completely do away with changing the keyboard mode.
Look things over and let me know if you have questions.
Userspace C's stdio won't talk directly with the hardware. It'll talk with the OS. And the OS will usually do quite a bit of preprocessing on the keystrokes it receives before it sends them to an application. On a UNIX-like OS much of the preprocessing will be done by your terminal driver, which can be set to reset to raw mode in which case you will actually receive the backspace too. Playing with the terminal driver is not standardized by the C standard, though.
On Linux, I can do:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char name[30], ch;
int i = 0;
printf("Enter name: ");
system("stty raw");
while(ch != '\n' && i < sizeof(name)) // terminates if user hit enter
{
ch = getchar();
name[i] = ch;
i++;
}
name[i] = '\0'; // inserting null character at end
printf("Name: %s", name);
system("stty sane"); //set some sane settings to the terminal
return 0;
}
and then I get the raw characters (I need to type shift+Enter to send the \n).
I didn't test the code, but the main idea is something like this:
while(ch != '\n') // terminates if user hit enter
{
ch = getchar();
// if this is a backspace character,
// lower the index and delete the last char
if(ch == 0x08){
name[--i] = 0x00;
}else{
// other chars will increment the index and fill the current char buffer
name[i++] = ch;
}
}
Late Edit:
Sorry I understood the question wrong I guess. The correct answer would be this:
Suppose you entered this: 1235[0x08]4 into the terminal.
Your char array would be:
[0x31, 0x02, 0x33, 0x35, 0x08, 0x34]
And when you print it, it'll execute like this order and it'll print char by char. Likewise, 5 would be printed and backspaced so fast you wouldn't notice.
And here's another question that may give you some other idea that how backspace works in some environments:
The "backspace" escape character '\b': unexpected behavior?
In this scenario, user input is taken from stdin using fgets. Normally to end a while loop when a user hits enter I would use strcmp between the fgets value and \n, but we are not allowed to use #include <string.h> in this particular assignment. Using C99.
You can't, since the fgets() function returns when it finds an \n
I'm assuming you mean when the user input a single \n and nothing else. Itmight be a better idea to use fgetc() instead, which will return the \n.
This means you need to buffer the inout yourself, something like this:
char inputBuffer[120] = "";
char ch;
char chCount = 0;
while (1) {
ch = fgetc(stdin);
if (ch == '\n') {
/* Empty buffer? */
if (inputBuffer[0] == '\0')
/* Oui! */
break;
/* Buffer isn't empty - do something with it... */
fprintf(stdout, "Input buffer: %s\n", inputBuffer);
/* Clear the buffer for the next line of input and reset the
* counter. */
inputBuffer[0] = '\0';
chCount = 0;
}
else {
if (chCount < 119) {
/* Add the byte to the buffer. */
inputBuffer[chCount++] = ch;
inputBuffer[chCount] = '\0';
}
}
}
The above loop will output any input string or break if a single \n is entered.