Copying one file to another: scanf keeps looping - c

I am creating a program that takes two command-line arguments: the first line argument is the name of the file to be copied and the second is the new file. If the second argument is missing, copy the file to stdout. If both arguments are missing, the program should read from stdin and print to stdout ie. ./a.out input.txt output.txt
I did the following but I'm facing a problem where scanf keeps looping and does not quit:
int main(int argc, char *argv[]) `
{
char text[10];
FILE *input;
FILE *output;
char ch;
printf("%s", argv[0]);
if (argc == 3)
{
input = fopen(argv[1], "r");
output = fopen(argv[2], "w");
while ((ch = fgetc(input)) != EOF)
{
fputc(ch, output);
ch = fgetc(output);
}
}
if (argc == 2)
{
input = fopen(argv[1], "r");
while ((ch = fgetc(input)) != EOF)
{
printf("%c", ch);
}
printf("\n");
}
if (argc == 1)
{
scanf("%c", text);
// here it keeps looping
}
fclose(input);
return 0;
}
Screen shot with cursor at end of line after some input before return is hit

It's not looping, it's waiting for input. You've requested scanf("%c"), a character, but scanf won't continue until you press enter. I think you meant scanf("%s") or getch(), but it isn't clear.
It will throw an error on the next line as fclose() is closing a FILE* that isn't initialized in the case of only 1 argument.
You could probably rework this homework example to use fopen() on STDIN/STDOUT in the case of missing parameters so that you aren't writing redundant code.

There are multiple problems in your code:
You output argv[0] to stdout: if argc < 3, you are supposed to copy to stdout, this extra output is corrupting the output. You might instead output to stderr or remove this line completely.
you do not check for fopen failure, causing undefined behavior if either file cannot be opened.
ch has type char which is too small to accommodate for all return values of fgetc(). On machines with 8-bit bytes, fgetc() has 257 possible return values, you must make ch an int to reliably distinguish EOF from all other return values.
in the fgetc() / fputc() loop, you read 2 bytes in each iteration of the loop but only write one byte.
the scanf() on the last case is simply waiting for input as you are supposed to copy from stdin to stdout, but the program will stop as soon as you hit the Enter key. You should just use the same fgetc()/fputc() loop for all copying loops.
you should include <stdio.h>
Here is a modified version:
int main(int argc, char *argv[]) {
FILE *input = stdin;
FILE *output = stdout;
int ch;
if (argc > 1) {
input = fopen(argv[1], "r");
if (input == NULL) {
fprintf("cannot open input file %s\n", argv[1]);
return 1;
}
}
if (argc > 2) {
output = fopen(argv[2], "w");
if (output == NULL) {
fprintf("cannot open output file %s\n", argv[2]);
return 1;
}
}
while ((ch = fgetc(input)) != EOF) {
fputc(ch, output);
}
if (argc > 2) {
fclose(output);
}
if (argc > 1) {
fclose(input);
}
return 0;
}

Related

Printing the first 10 line of a file in C

I'm new to programming in C. And I'm trying to print the first 10 lines of a text file. When I run my program with a text file containing 11 lines of text, only the first line is displayed. I'm not sure why it does that, but I suspect there is something wrong in my while loop. Can someone please help me?
#include <stdio.h>
int main(int argc, char *argv[]){
FILE *myfile;
char content;
int max = 0;
// Open file
myfile = fopen(argv[1], "r");
if (myfile == NULL){
printf("Cannot open file \n");
exit(0);
}
// Read the first 10 lines from file
content = fgetc(myfile);
while (content != EOF){
max++;
if (max > 10)
break;
printf ("%c", content);
content = fgetc(myfile);
}
fclose(myfile);
return 0;
}
You have been already advised to use fgets. However, if your file has lines of unknown length, you may still want to use fgetc. Just make sure you count only newlines, not all characters:
int max = 0;
int content;
while ((content = fgetc(myfile)) != EOF && max < 10){
if (content == '\n') max++;
putchar(content);
}
fgetc() returns the next character in the file, not the next line. You probably want to use fgets() instead, which reads up to the next newline character into a buffer. Your code should probably end up with something like:
// allocate 1K for a buffer to read
char *buff = malloc(1024);
// iterate through file until we are out of data or we read 10 lines
while(fgets(buff, 1024, myfile) != NULL && max++ < 10) {
printf("%s\n", buff);
}
free(buff);
// close your file, finish up...
Read more about fgets() here: https://www.tutorialspoint.com/c_standard_library/c_function_fgets.htm
fgetc function reads the next character not the next ine. for reading the number of lines you should use fgets function. this function reads the full string till the end of the one line and stores it in a string.
your code Shuld be as:-
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *myfile;
char content[200];
int max = 0;
// Open file
myfile = fopen(argv[1], "r");
if (myfile == NULL)
{
printf("Cannot open file \n");
exit(0);
}
// Read the first 10 lines from file
fgets(content, 200, myfile);
while (content != EOF)
{
max++;
if (max > 10)
break;
printf("%s", content);
fgets(content, 200, myfile);
}
fclose(myfile);
return 0;
}

How to swap characters in a file specified (file name and characters) in the command line?

I'm learning about file commands and trying to write some programs. I want to specify two characters in command line, where the second one will replace the first one, every time the first character is found. The command line input would be [program_name].exe [file].txt [old_char] [new_char]. I came across this thread which had the same problem and I tried to fix my code by looking at the answer, but it creates an infinite loop.
int main(int argc, char *argv[])
{
FILE *fp;
char *string = {"Hellx"};
char c;
if ((fp = fopen(argv[1], "w")) != NULL)
{
fputs(string, fp);
printf("Successfully written.\n");
fclose(fp);
if ((fp = fopen(argv[1], "r+")) != NULL)
{
while ((c = fgetc(fp)) != EOF)
{
if (c == *argv[2])
{
fseek(fp, ftell(fp) - 1, SEEK_SET);
fprintf(fp, "%c", *argv[3]);
printf("Successfully changed.\n");
}
}
fclose(fp);
}
else
printf("Error on opening!");
}
else
printf("Error on writing!");
return 0;
}
So the output for this would be: Helloelloelloelloelloelloello..., while it should just change x to o. What's the problem with this code?
Your code does not work because you do not call fseek() or rewind() when switching from writing back to reading. Also note that you do not need to call ftell() to step back: you can use -1L and SEEK_CUR. It is also safer to open the file in binary more for this kind of file patching in place.
Furthermore, fgetc() returns an int value that does not fit in a char. Use int type for c to detect EOF reliably. Also note that the byte value returned by fgetc() when successful is the value of an unsigned char so comparing it to a char might fail for non-ASCII bytes.
Here is a modified version:
#include <stdio.h>
int main(int argc, char *argv[]) {
FILE *fp;
const char *string = { "Hellx" };
if (argc < 4) {
fprintf("missing command line arguments\n");
return 1;
}
char *filename = argv[1];
unsigned char c1 = argv[2][0];
unsigned char c2 = argv[3][0];
if ((fp = fopen(filename, "w")) == NULL) {
fprintf(stderr, "cannot open %s for writing\n", filename);
return 1;
}
fputs(string, fp);
printf("Successfully written.\n");
fclose(fp);
if ((fp = fopen(filename, "rb+")) == NULL) {
printf("Cannot reopen %s\n", filename);
return 1;
}
while ((c = fgetc(fp)) != EOF) {
if (c == c1) {
fseek(fp, -1L, SEEK_CUR);
fputc(c2, fp);
fseek(fp, 0L, SEEK_CUR);
printf("Successfully changed.\n");
}
}
fclose(fp);
return 0;
}
The man page for fopen(), in the section which discusses file opening modes, says
When the "r+", "w+", or "a+" access type is specified, both reading and writing are enabled (the file is said to be open for "update"). However, when you switch from reading to writing, the input operation must encounter an EOF marker. If there is no EOF, you must use an intervening call to a file positioning function. The file positioning functions are fsetpos, fseek, and rewind. When you switch from writing to reading, you must use an intervening call to either fflush or to a file positioning function.
(my bolding)
So I suggest adding
fflush(fp);
after the fprintf() statement, as no repositioning is needed.
As mentioned, you also should change the type to
int c;
so that EOF -1 can be distinguished from data 0xFF.
The reasons for why your program is failing were already extensively debated and solved in other answers and comments.
Is there a short way to do this without "r+" and without fflush?
There is, more than one, here is an example where the file is opened to read and write, this way there is no need to always be opening and closing it, using w+ flag, it will also create the file if it doesn't exist:
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp;
int c;
char *string = {"Hellx"};
if (argc > 3)
{
if ((fp = fopen(argv[1], "w+")) != NULL) // open to write and read
{
fputs(string, fp);
printf("Successfully written.\n");
rewind(fp); // back to the beginning of the file
while ((c = fgetc(fp)) != EOF)
{
if(c == argv[2][0]) // if the character exists...
{
fseek(fp, -1, SEEK_CUR);
fprintf(fp, "%c", argv[3][0]); // replace
printf("Successfully changed.\n");
fseek(fp, 0, SEEK_CUR);
}
}
fclose(fp);
}
else
fprintf(stderr, "Error on writing!");
}
else
fprintf(stderr, "Too few arguments!");
}
Footnote:
I agree with William Pursell and Weather Vane, a more robust way to do this would be to use two different files.

C: Printing words from a file using fgetc

I'm trying to write a simple main function that takes in a file through the command line, and prints all the words in the file.
However, I've followed several tutorials that just don't seem to work. Here's my code:
int main(int argc, char *argv[]) {
if (argc < 2) return 1;
char * filename = argv[1];
FILE* file = fopen(filename, "r");
if(file == NULL) {
perror("Error opening file");
return(1);
}
char c;
while((c = fgetc(file)) != EOF)
{
if(c == ' ' || c == '\n')
{
printf("\n");
}
else
{
printf("%c", c);
}
}
printf("Done.");
fclose(file);
return 0;
}
So far it has returned nothing, and doesn't show an error. The command line looks like this:
C:\Users\<USERNAME>\Desktop\C_C++>program.exe < file.txt
C:\Users\<USERNAME>\Desktop\C_C++>program.exe > file.txt
C:\Users\<USERNAME>\Desktop\C_C++>
It's using a windows command line. I didn't know whether or not I had to use > or < when passing in a file parameter, but I've also just used the file's name in place of filename in FILE* file = fopen(filename, "r"); to just open it directly instead of passing the filename as a parameter, but nothing shows.
I have no idea what is going wrong.
fgetc() returns int, not char. You need to declare
int c;
Otherwise, there's a possibility that the test for EOF will never succeed.
Your program expects the filename as a command-line argument, it doesn't read from standard input. So you have to give the filename as an argument, not use input redirection. The program is exiting immediately because argc is 1.
program.exe file.txt
You should add an error message so you'll know this is the problem:
if (argc < 2) {
printf("Usage: %s filename\n", argv[0]);
return 1;
}

Segmentation fault (core dumped) due to fgets - I think

but I keep getting this error when I run this program. I think it's because of the fgets function. I tried initializing the input variable to NULL to see if that'll help, but it didn't. I also have a hunch that I might need to malloc to solve the problem. But your help is highly appreciated.
int main(int argc, char* argv[])
{
char* input = NULL;
// ensure one and only one command line argument
if (argc != 2)
{
printf("Usage: %s [name of document]\n", argv[0]);
return 1;
}
// open a new document for writing
FILE* fp = fopen(argv[1], "w");
// check for successful open
if(fp == NULL)
{
printf("Could not create %s\n", argv[1]);
return 2;
}
// get text from user and save to file
while(true)
{
// get text from user
printf("Enter a new line of text (or \"quit\"):\n");
fgets(input, 50, stdin);
// if user wants to quit
if (input != NULL && strcmp(input, "quit") == 0)
{
free(input);
break;
}
// if user wants to enter text
else if (input != NULL)
{
fputs(input, fp);
fputs("\n", fp);
printf("CHA-CHING!\n\n");
free(input);
}
}
// close the file and end successfuly
fclose(fp);
return 0;
}
You never malloc-ed input, so yeah, fgets is dereferencing the NULL pointer as its buffer, and that's going to die. Either change input to a stack array (and remove the free for it) or actually call malloc to allocate memory so input isn't pointing to NULL.
Their are some problems in your code.
You have not allocated memory to input character pointer. Hence you can't store characters in it, hence you get segmentation fault.
Also you are freeing more than once, which is incorrect.
So, a code, with the above modification would be something like this:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char* argv[])
{
char* input = malloc(sizeof(char) * 50);
// ensure one and only one command line argument
if (argc != 2)
{
printf("Usage: %s [name of document]\n", argv[0]);
return 1;
}
// open a new document for writing
FILE* fp = fopen(argv[1], "w");
// check for successful open
if(fp == NULL)
{
printf("Could not create %s\n", argv[1]);
return 2;
}
// get text from user and save to file
while(1)
{
// get text from user
printf("Enter a new line of text (or \"quit\"):\n");
fgets(input, 50, stdin);
// if user wants to quit
if (input != NULL && strcmp(input, "quit\n") == 0)
{
free(input);
break;
}
// if user wants to enter text
else if (input != NULL)
{
fputs(input, fp);
fputs("\n", fp);
printf("CHA-CHING!\n\n");
// free(input);
}
}
// close the file and end successfuly
fclose(fp);
return 0;
}
Hope it helps your problem.
Cheers.
While you can use malloc() here, it is not really necessary. You can #define a reasonable maximum line length, and declare a character array to hold the input. If you do this, you can remove the frees from your code.
You also have an issue with the way that you are using fgets(). The trailing \n is kept by fgets(), but your comparisons are ignoring this. Consequently, input is never equal to "quit", and is certainly never NULL. I have included some code that removes the trailing newline after reading into input; the code also clears any remaining characters from the input stream, which is possible in the event that the user enters more than MAXLINE - 1 characters. The test for text input is then simply if (input[0]). Alternatively, you could change your tests to take into account the extra '\n' character.
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define MAXLINE 1000
int main(int argc, char* argv[])
{
char input[MAXLINE];
char *ch; // used to remove newline
char c; // used to clear input stream
// ensure one and only one command line argument
if (argc != 2)
{
printf("Usage: %s [name of document]\n", argv[0]);
return 1;
}
// open a new document for writing
FILE* fp = fopen(argv[1], "w");
// check for successful open
if(fp == NULL)
{
printf("Could not create %s\n", argv[1]);
return 2;
}
// get text from user and save to file
while(true)
{
// get text from user
printf("Enter a new line of text (or \"quit\"):\n");
fgets(input, MAXLINE, stdin);
// remove trailing newline
ch = input;
while (*ch != '\n' && *ch != '\0') {
++ch;
}
if (*ch) {
*ch = '\0';
} else { // remove any extra characters in input stream
while ((c = getchar()) != '\n' && c != EOF)
continue;
}
// if user wants to quit
if (strcmp(input, "quit") == 0)
{
break;
}
// if user wants to enter text
else if (input[0])
{
fputs(input, fp);
fputs("\n", fp);
printf("CHA-CHING!\n\n");
}
}
// close the file and end successfuly
fclose(fp);
return 0;
}
I think it's because of the fgets function.
Yes: passing NULL pointer to fgets makes no sense, isn't allowed, and will cause a crash.
I might need to malloc to solve the problem.
You need to pass a pointer to a suitable buffer for fgets to read input into. Whether that buffer is malloced, a local or a global array, is irrelevant.
TL;DR: think about what you are doing.

Printing out each line of an input file twice?

I am writing code which very simply reads in a file and prints out what was in the file appropriately.
I have always struggled with getting such a program to terminate upon end of file and think I've found the appropriate solution, however each line is printing twice in my output, for a reason beyond me.
Here is my main file:
int main(int argc, char *argv[]) {
// insure 2 arguments given, one for a.out and one for the test file
if (argc != 2) {
// result if request fails
printf("Requires 2 arguments. Be sure to include test file location\n");
return 0;
}
FILE *fp; //open the file
fp = fopen(argv[1], "r");
char option;
int key;
int i = 0;
while (fscanf(fp, "%c %d", &option, &key) != EOF) {
printf("%d\n", key);
}
}
The key is printing twice!
Hopefully this is a simple error I'm just overlooking due to overexposure to the problem.
You probably want:
fscanf(fp, "%c %d\n", &option, &key);
And you also want to check the return value of fscanf to make sure it equals 2.
In the first iteration of your loop, the newline is not being consumed.
In the second iteration, the newline is consumed and put in option, and the %d does not match, and fscanf returns 1. key is unchanged which is why it gets printed again.
In the third iteration, fscanf finally returns EOF.
General rule: Always check return values to ensure they are what you expect. (You also violate this rule by failing to check the return from fopen.) At worst it does nothing; at best, it helps you debug problems like this.
#include <stdio.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Requires 1 argument - a file name\n");
return 1;
}
FILE *fp; //open the file
if ((fp = fopen(argv[1], "r")) == 0)
{
fprintf(stderr, "Failed to open file %s\n", argv[1]);
return 1;
}
char option;
int key;
while (fscanf(fp, "%c %d", &option, &key) == 2)
printf("%d\n", key);
return 0;
}
Note the changes in error reporting, and in the file reading process. The code is still probably not quite what you want; you might get the newline after the number after the first line of input stored in option after the first line. Fixing that requires fgets() and sscanf():
#include <stdio.h>
int main(int argc, char *argv[])
{
if (argc != 2)
{
fprintf(stderr, "Requires 1 argument - a file name\n");
return 1;
}
FILE *fp; //open the file
if ((fp = fopen(argv[1], "r")) == 0)
{
fprintf(stderr, "Failed to open file %s\n", argv[1]);
return 1;
}
char buffer[1024];
while (fgets(buffer, sizeof(buffer), fp) != 0)
{
char option;
int key;
if (fscanf(fp, "%c %d", &option, &key) == 2)
printf("%d\n", key);
else
{
fprintf(stderr, "Format mismatch on %s", buffer);
fclose(fp); // Not 100% necessary here, but tidiness is important
return 1;
}
}
fclose(fp); // Not 100% necessary here, but tidiness is important.
return 0;
}
Although I closed fp before the end, it is not crucial when the program is about to exit, and return from main() is pretty much equivalent to exit(). If it was in a function other than main() though, it is very important to ensure that you free any resource you allocate, such as the file stream fp.
Warning: uncompiled code. Caveat Lector.

Resources