C looping using EOF, how to read next line? - c

I'm facing an issue while trying to resolve a particular problem in C. User input must be read in encrypted form and the output must be the decrypted message, that is, it is a replacement cipher.The problem is that when I read the first input, the program loops printing the decrypted message, it doesn't ask to read other inputs. I'm really confused about this as I was told that I should use EOF, something I'm not very knowledgeable about. I could even solve it another way, but I need to implement the EOF, and because I don't know that, I'm having difficulty solving the exercise.
I am aware that the code I created is not the best for the resolution, however, it might work if someone helps me with this loop issue.
Source Code
#include <stdio.h>
#include <string.h>
void alterChar(int i, char phrase[])
{
for (i = 0; phrase[i] != '\0'; i++)
{
if (phrase[i] == '#')
{
phrase[i] = 'a';
}
if (phrase[i] == '&')
{
phrase[i] = 'e';
}
if (phrase[i] == '!')
{
phrase[i] = 'i';
}
if (phrase[i] == '*')
{
phrase[i] = 'o';
}
if (phrase[i] == '#')
{
phrase[i] = 'u';
}
printf("%s\n", phrase);
}
}
int main()
{
int i;
char phrase[256];
while (scanf("%[^\n]s", phrase) != EOF)
{
///scanf(" %[^\n]s", phrase);
alterChar(i, phrase);
}
return 0;
}

The line
while (scanf("%[^\n]s", phrase) != EOF)
is wrong, for several reasons:
You seem to be undecided on whether to use the %s or the %[] conversion format specifier. The character s is not part of the %[] format specifier. Therefore, when using your format string, scanf will attempt to match a literal s. This is probably not what you want, so it should be removed from the format string.
If scanf is unable to match any characters because the line is empty, it will return 0 and will not touch the contents of phrase. However, you are incorrectly treating the return value of 0 as success.
If the user enters more than 255 characters, then you will be writing to the array phrase out of bounds, causing undefined behavior (i.e your program may crash). Therefore, you should limit the number of matched characters to 255 characters, so that the function won't write more than 256 characters (including the terminating null character of the string).
For the reasons stated above, you should change the line to the following:
while ( scanf( "%255[^\n]", phrase ) != 1 )
Also, this scanf statement will not consume the newline character from the input stream, so the next function call to scanf will fail. Therefore, you must call a function to consume the newline character from the input stream, for example by calling getchar, or by telling scanf to consume all whitespace input beforehand, like this:
while ( scanf( " %255[^\n]", phrase ) != 1 )
Alternatively, you could use the function fgets instead (which I recommend), as that function will also consume the newline character and write it to the string.
while ( fgets( phrase, sizeof phrase, stdin ) != NULL )
When using the function fgets, you should also verify that an entire line was read in, for example by writing the following code afterwards:
//verify that entire line was read in
if ( strchr( phrase, '\n' ) == NULL )
{
printf( "line to long for input buffer!\n" );
exit( EXIT_FAILURE );
}
Note that if you decide to use the function fgets, you will have an additional newline character at the end of the line, instead of only a '\0', so in the function alterChar, you will have to change the line
for (i = 0; phrase[i] != '\0'; i++)
to
for (i = 0; phrase[i] != '\n'; i++)
Another problem in your code is that the line
alterChar(i, phrase);
does not make sense. You are passing the value of an uninitialized variable to the function.
You should probably change the function prototype from
void alterChar(int i, char phrase[])
to
void alterChar( char phrase[] )
and maybe also give the function a better name, as it does not deal with single characters, but whole strings, and also prints it:
void alterAndPrintPhrase( char phrase[] )
Also, in that function, you should move the line
printf("%s\n", phrase);
outside the loop.
After applying all the fixes mentioned above, your code should look like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void alterAndPrintPhrase( char phrase[] )
{
for ( int i = 0; phrase[i] != '\n'; i++ )
{
if (phrase[i] == '#')
{
phrase[i] = 'a';
}
if (phrase[i] == '&')
{
phrase[i] = 'e';
}
if (phrase[i] == '!')
{
phrase[i] = 'i';
}
if (phrase[i] == '*')
{
phrase[i] = 'o';
}
if (phrase[i] == '#')
{
phrase[i] = 'u';
}
}
printf( "%s\n", phrase );
}
int main()
{
char phrase[256];
while ( fgets( phrase, sizeof phrase, stdin ) != NULL )
{
//verify that entire line was read in
if ( strchr( phrase, '\n' ) == NULL )
{
printf( "line to long for input buffer!\n" );
exit( EXIT_FAILURE );
}
alterAndPrintPhrase( phrase );
}
return 0;
}
This program has the following behavior:
Input:
Th!s !s # t&st.
Output:
This is a test.

i think just putting the printf outside of the loop help and use feof (fetch en of file) to know where you are.
#include <stdio.h>
#include <string.h>
void alterChar(char phrase[]) {
for (int i = 0; phrase[i] != '\0'; i++) {
if (phrase[i] == '#') {
phrase[i] = 'a';
}
if (phrase[i] == '&') {
phrase[i] = 'e';
}
if (phrase[i] == '!') {
phrase[i] = 'i';
}
if (phrase[i] == '*') {
phrase[i] = 'o';
}
if (phrase[i] == '#') {
phrase[i] = 'u';
}
}
printf("%s\n", phrase);
}
int main() {
char phrase[256];
while (!feof(stdin)) {
scanf(" %[^\n]s", phrase);
alterChar(phrase);
}
return 0;
}

Related

What is the best way to match a string to specified format?

The format that I want to match the string to is "from:<%s>" or "FROM:<%s>". The %s can be any length of characters representing an email address.
I have been using sscanf(input, "%*[fromFROM:<]%[#:-,.A-Za-z0-9]>", output). But it doesn't catch the case where the last ">" is missing. Is there a clean way to check if the input string is correctly formatted?
You can't directly tell whether trailing literal characters in a format string are matched; there's no direct way for sscanf()) to report their absence. However, there are a couple of tricks that'll do the job:
Option 1:
int n = 0;
if (sscanf("%*[fromFROM:<]%[#:-,.A-Za-z0-9]>%n", email, &n) != 1)
…error…
else if (n == 0)
…missing >…
Option 2:
char c = '\0';
if (sscanf("%*[fromFROM:<]%[#:-,.A-Za-z0-9]%c", email, &c) != 2)
…error — malformed prefix or > missing…
else if (c != '>')
…error — something other than > after email address…
Note that the 'from' scan-set will match ROFF or MorfROM or <FROM:morf as a prefix to the email address. That's probably too generous. Indeed, it would match: from:<foofoomoo of from:<foofoomoo#example.com>, which is a much more serious problem, especially as you throw the whole of the matched material away. You should probably capture the value and be more specific:
char c = '\0';
char from[5];
if (sscanf("%4[fromFROM]:<%[#:-,.A-Za-z0-9]%[>]", from, email, &c) != 3)
…error…
else if (strcasecmp(from, "FROM") != 0)
…not from…
else if (c != '>')
…missing >…
or you can compare using strcmp() with from and FROM if that's what you want. The options here are legion. Be aware that strcasecmp() is a POSIX-specific function; Microsoft provides the equivalent stricmp().
Use "%n". It records the offset of the scan of input[], if scanning got that far.
Use it to:
Detect scan success that include the >.
Detect Extra junk.
A check of the return value of sscanf() is not needed.
Also use a width limit.
char output[100];
int n = 0;
// sscanf(input, "%*[fromFROM:<]%[#:-,.A-Za-z0-9]>", output);
sscanf(input, "%*[fromFROM]:<%99[#:-,.A-Za-z0-9]>%n", output);
// ^^ width ^^
if (n == 0 || input[n] != '\0') {
puts("Error, scan incomplete or extra junk
} else [
puts("Success");
}
If trailing white-space, like a '\n', is OK, use " %n".
Regarding the first part of the string, if you want to accept only FROM:< or from:< , then you can simply use the function strncmp with both possibilities. Note, however, that this means that for example From:< will not be accepted. In your question, you implied that this is how you want your program to behave, but I'm not sure if this really is the case.
Generally, I wouldn't recommend using the function sscanf for such a complex task, because that function is not very flexible. Also, in ISO C, it is not guaranteed that character ranges are supported when using the %[] format specifier (although most common platforms probably do support it). Therefore, I would recommend checking the individual parts of the string "manually":
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
bool is_valid_string( const char *line )
{
const char *p;
//verify that string starts with "from:<" or "FROM:<"
if (
strncmp( line, "from:<", 6 ) != 0
&&
strncmp( line, "FROM:<", 6 ) != 0
)
{
return false;
}
//verify that there are no invalid characters before the `>`
for ( p = line + 6; *p != '>'; p++ )
{
if ( *p == '\0' )
return false;
if ( isalpha( (unsigned char)*p ) )
continue;
if ( isdigit( (unsigned char)*p ) )
continue;
if ( strchr( "#:-,.", *p) != NULL )
continue;
return false;
}
//jump past the '>' character
p++;
//verify that we are now at the end of the string
if ( *p != '\0' )
return false;
return true;
}
int main( void )
{
char line[200];
//read one line of input
if ( fgets( line, sizeof line, stdin ) == NULL )
{
printf( "Input failure!\n" );
exit( EXIT_FAILURE );
}
//remove newline character
line[strcspn(line,"\n")] = '\0';
//call function and print result
if ( is_valid_string ( line ) )
printf( "VALID\n" );
else
printf( "INVALID\n" );
}
This program has the following output:
This is an invalid string.
INVALID
from:<john.doe#example.com
INVALID
from:<john.doe#example.com>
VALID
FROM:<john.doe#example.com
INVALID
FROM:<john.doe#example.com>
VALID
FROM:<john.doe#example!!!!.com>
INVALID
FROM:<john.doe#example.com>invalid
INVALID

print each letter after '.' for example if I enter a..bcde..fg..h the program will print bfh

I'm new to C, I have been asked to make a program in C asking to print each letter after a '.' after a user has entered an input.
For example if the user enters a..bcd..e.f..gh the output should be befg
which is the exact example I have been given in class.
I assume this would need to use pointers but I am unsure how to deal with this question, here is what I have tried to do so far. I know it is not correct, please help me understand how to use pointers to deal with this question.
#include <stdio.h>
int main() {
char *c, count =0;
printf("enter some characters");
scanf("%s", &c);
while( c != EOF ) {
if (c != '.') {
count ++;
}
else; {
printf("%s", c);
}
}
}
The program can look the following way
#include <stdio.h>
#define N 100
int main( void )
{
char s[N];
const char DOT = '.';
printf( "Enter some characters: " );
fgets( s, N, stdin );
for ( char *p = s; *p; ++p )
{
if ( p[0] == DOT && p[1] != DOT ) putchar( p[1] );
}
putchar( '\n' );
}
Its output might look like
Enter some characters: a..bcd..e.f..gh
befg
Take into account that here any symbol after a dot (except the dot itself) is printed. You can add a check that there is a letter after a dot.
You don't really need pointers for this, or even an array. Basically it's a simple state engine: read each character, if '.' is encountered, set a flag so the next character is printed.
#include <stdio.h>
int main() {
int c, flag = 0;
while ((c = getchar()) != EOF) {
if (c == '.')
flag = 1;
else if (flag) {
putchar(c);
flag = 0;
}
}
return 0;
}
There are some errors in your code:
- char* c means a pointer to one or more characters.
But where does it point to?
- scanf reads a string up to an "white space". White space characters are the space itself, a newline, a tab character or an EOF. scanf expects a format string and a pointer to a place in memory where it places what it reads. In your case c points to an undefined place and will overwrite whatever there is in memory.
- why do you place a ";" after the else? The else clause will end with the ";". So your program will do the print every time.
It helps you a lot if you format your code in a more readable way and give the variable names that give hint what they are used for.
Another very important thing is to initialize every variable that you declare. Errors with uninitialized variables are sometimes very hard to find.
I would do it this way:
#include <stdio.h>
int main(int argc, char* argv[])
{
// I read every single character. The getchar function returns an int!
int c = 0;
// This marks the program state whether we must print the next character or not
bool printNext = false;
printf("enter some characters");
// We read characters until the buffer is empty (EOF is an integer -1)
do
{
// Read a single character
c = getchar();
if ( c == '.')
{
// After a point we change our state flag, so we know we have to print the next character
printNext = true;
}
else if( c != EOF )
{
// When the character is neither a point nor the EOF we check the state
if( printNext )
{
// print the character
printf( "%c", c );
// reset the state flag
printNext = false;
}
}
// read until the EOF occurs.
}
while( c != EOF );
}
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
int main()
{
char letter;
char *c;
c = malloc(256);
printf("enter the string : ");
scanf("%s", c);
while( (letter=*(c)) != '\0' )
{
if (letter == '.')
{
c++;
letter=*c;
if(letter!='.')
printf("%c",letter);
else
{
while(letter=='.')
{
c++;
letter=*c;
}
printf("%c",letter);
}
}
c++;
}
printf("\n");
}

Replace "a", "an", "the" with blank space in a text file

A friend of mine gave me a task to write a program to
replace "a", "an", "the" with blank space in a text file in C.
I wrote that program, but that went too lengthy as I checked "a", "an", "the" individually.
For example, I replaced "a" by
NOTE: fs is pointer to the source file and ft is pointer to the target file.
while(fgets(str, 100, fs) != NULL)
{
for(i = 0; str[i] != '\0'; i++)
{
if (str[i] == ' ' ||
str[i] == '.' ||
str[i] == ',' ||
str[i] == '\n' ||
str[i] == '\t')
{
if (str[i+1] == 'a' || str[i+1] == 'A')
{
if (str[i+2] == ' ' ||
str[i+2] == '.' ||
str[i+2] == ',' ||
str[i+2] == EOF ||
str[i+2] == '\0' ||
str[i+2]== '\n' ||
str[i+2]== '\t')
{
str[i+1]=' ';
}
}
}
}
fputs(str,ft);
}
Is there a shorter way to do the same?
Take care of the fact that "a", "an", "the" can be the first words in the source file.
Use the fscanf and fprintf functions so that scanning the file would be easy for you and you can easily check for 'a, an, the':
char s[50];
while(fscanf(fp, "%s", s) != EOF)
{
if(strcmp(s, "a") == 0 || strcmp(s, "an") == 0 || strcmp(s, "the") == 0)
{
char c = ' ';
fprintf(ft, "%s", c);
}
else
{
fprintf(ft, "%s", s);
}
}
You can read the input char-by-char, using for example getchar, or always remember the last few chars, even if they were in the previous buffer. This way you need to remember the previous two chars, and the current char in a little "rolling-array", that you would reset at each word boundary.
Using a fixed sized buffer with fgets, or fscanf, you need a lot of codeing to handle special cases. There are a few, for example the lines don't start with space or tab, but a line parhaps starts with "the". In that case, there will be no such character before the word. The same thing is true about whitespace following a word. You can get around these things by allocating a bit more space for the buffer, fill the first char with ' ' , and call fgets this way:
fgets(str + 1, 99, fs)
But you still have the problem of words at boundaries, where your buffer ends with "... t" and the next fgets gives you "he ..." .
Just keep an array of 3 chars, and the current length of the array, resetting the length to zero at each word boundary.
I think this code works for a reasonably plausible definition of the problem:
#include <ctype.h>
#include <stdio.h>
#include <string.h>
static char const *words[] = { "a", "the", "an" };
enum { NUM_WORDS = sizeof(words) / sizeof(words[0]) };
static void mapword(char *word, int len)
{
char lower[256];
word[len] = '\0';
for (int i = 0; i <= len; i++)
lower[i] = tolower(word[i]);
for (int i = 0; i < NUM_WORDS; i++)
{
if (strcmp(words[i], lower) == 0)
{
putchar(' ');
return;
}
}
fputs(word, stdout);
}
int main(void)
{
char word[256];
int c;
size_t nletters = 0;
while ((c = getchar()) != EOF)
{
/*
** This would break if you enter a word with 256 characters
** ending in 'a' because the word would be split after 255
** characters and the trailing 'a' would then match the
** next buffer full, which is an awfully improbable event.
*/
if (!isalpha(c) || nletters >= sizeof(word)-1)
{
if (nletters > 0)
{
mapword(word, nletters);
nletters = 0;
}
putchar(c);
}
else
word[nletters++] = c;
}
if (nletters > 0)
{
/*
** Since a text file should end with a newline, the program
** should not get here!
*/
mapword(word, nletters);
}
return 0;
}
For example, given the first three lines of the question as input:
A friend of mine gave me a task to write a program to
replace "a", "an", "the" with blank space in a text file in c.
I wrote that program but that went too lengthy as I checked "a", "an", "the" individually.
the output from the program is:
friend of mine gave me task to write program to
replace " ", " ", " " with blank space in text file in c.
I wrote that program but that went too lengthy as I checked " ", " ", " " individually.
If you want to use some system command then your life is easy. sed is linux command to serve your requirement.
You can do as follows
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char * argv[])
{
system("sed 's/an//g;s/a//g;s/the//g' file");
}
If file contain
replace “a”, “an”, “the” with blank space in a text file
Output is
replce “”, “”, “” with blk spce in text file
Caution : This code replace space every where when it found matching pattern.So it not check for matching whole word.

fgetc to skip from point to new line

I am trying to get fgetc to read through a file and skip from a certain indicator until a new line. This seems like a simple question, but I can't find any documentation on it.
Here is an example of my question:
read this in ; skip from semicolon on to new line
My best guess at a solution would be to read in the entire file, and for each line use strtok to skip from ; to the end of the line. Obviously this is horrible inefficient. Any ideas?
*I need to use fgetc or something like fgetc that will parse the file character by character
Easiest thing to do is read the entire line in, then truncate if there a ;.
char buffer[1024], * p ;
if ( fgets(buffer, sizeof(buffer), fin) )
{
if (( p= strchr( buffer, ';' ))) { *p = '\0' ; } // chop off ; and anything after
for ( p= buffer ; ( * p ) ; ++ p )
{
char c= * p ;
// do what you want with each character c here.
}
}
When you do the read, buffer will initially contain:
"read this in ; skip from semicolon on to new line\n\0"
After you find the ; in the line and stick a '\0' there, the buffer looks like:
"read this in \0 skip from semicolon on to new line\n\0"
So the for loop starts at r and stops at the first \0.
//Function of compatible fgets to read up to the character specified by a delimiter.
//However file stream keep going until to newline.
//s : buffer, n : buffer size
char *fgets_delim(char *s, int n, FILE *fp, char delimiter){
int i, ch=fgetc(fp);
if(EOF==ch)return NULL;
for(i=0;i<n-1;++i, ch=fgetc(fp)){
s[i] = ch;
if(ch == '\n'){
s[i+1]='\0';
break;
}
if(ch == EOF){
s[i]='\0';
break;
}
if(ch == delimiter){
s[i]='\0';//s[i]='\n';s[i+1]='\0'
while('\n'!=(ch = fgetc(fp)) && EOF !=ch);//skip
break;
}
}
if(i==n-1)
s[i] = '\0';
return s;
}
Given a requirement to use fgetc(), then you are probably supposed to echo everything up to the first semicolon on the line, and suppress everything from the semicolon to the end of the line. I note in passing that getc() is functionally equivalent to fgetc() and since this code is about to read from standard input and write to standard output, it would be reasonable to use getchar() and putchar(). But rules are rules...
#include <stdio.h>
#include <stdbool.h>
int main(void)
{
int c;
bool read_semicolon = false;
while ((c = fgetc(stdin)) != EOF)
{
if (c == '\n')
{
putchar(c);
read_semicolon = false;
}
else if (c == ';')
read_semicolon = true;
else if (read_semicolon == false)
putchar(c);
/* else suppressed because read_semicolon is true */
}
return 0;
}
If you don't have C99 and <stdbool.h>, you can use int, 0 and 1 in place of bool, false and true respectively. You can use else if (!read_semi_colon) if you prefer.

char pointer allocates random characters

I am writing a small C code for class that plays Hangman with an already input name. One section requires I allow the output of the input phrase with * in the place of all letters, but not punctuation. Similarly, at the end of the phrase, the name of the user is put in parentheses and is meant to be printed as is. The first section of the code works fine, the first while loop which places the asterisks, but the second while loop seems to fail every time and seems to store nonsense and random characters everytime the program is run. Here is the program I have so far.
#include <stdio.h>
#include <stdlib.h>
int main()
{
int guesses = 3;
int limit = 41;
char quote[42] = "I just wrote this game in C! (Josh Masher)";
char strArr[42];
char *quoP;
quoP = &quote[0];
char *strP;
strP = &strArr[0];
while (*quoP != '.' && *quoP != '!' && *quoP != '?') {
if (isalpha(*quoP)) {
*strP = '*';
} else if (*quoP == ' ' || *quoP == ',') {
*strP = *quoP;
}
strP++;
quoP++;
}
while (*quoP != NULL) {
*strP = *quoP;
strP++;
quoP++;
}
}
any ideas?
EDIT
I rewrote the code slightly, and erased the random character problem, but it is more complicated now.
int main()
{
int guesses = 3;
int limit = 41;
char quote[42] = "I just wrote this game in C! (Alex Butler)\0";
char strArr[42];
char *quoP;
quoP = &quote[0];
char *strP;
strP = &strArr[0];
int counter = 0;
while (*quoP != '\0') {
if (*quoP == '.' || *quoP == '!' || *quoP == '?' || counter == 1) {
counter = 1;
}
if (isalpha(*quoP)) {
if (counter == 0) {
*strP = '*';
} else if (counter == 1) {
*strP = *quoP;
}
} else {
*strP = *quoP;
}
printf("%c", *strP);
strP++;
quoP++;
}
}
Add *strP = '\0' after the last while loop to terminate the string.
Also, (*quoP != NULL) should be (*quoP != '\0') . The type of NULL is pointer, the type of *quoP is character. Your program will still work, but it's misleading.
Also might want to include ctype.h
Good luck with the rest of your project.
The first loop doesn't work fine. If it encounters unhandled punctuation (such as &), it will skip right over and leave junk there.
You do not null-terminate the string either, as others have pointed out in the comments. You would do better to copy the string first (with strncpy) and then stamp characters with * as you deem appropriate. That means you only have one loop, and it'll be a lot simpler:
strncpy( strArr, quote, sizeof(strArr) );
for( char *s = strArr; !strchr(".!?", *s); s++ )
{
if( isalpha(*s) ) *s = '*';
}
Also, NULL is a pointer. Null-termination is an unfortunate name for this. You can write the value 0 or '\0', but not NULL.

Resources