Not reading from stdin properly - c

I'm trying to mimic the behavior of the unix utility cat, but when I call a command of the form:
cat file1 - file2 - file3
My program will output file1 correctly, then read in from stdin, then when I press EOF, it will print file 2 then file 3, without reading from stdin for the second time.
Why might this be?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ASCII_LENGTH 255
int printfile(FILE *source, int N);
int main(int argc, char *argv[])
{
int currentarg = 1; //the argument number currently being processed
FILE *input_file;
//if there are no arguments, dog reads from standard input
if(argc == 1 || currentarg == argc)
{
input_file = stdin;
printfile(input_file,0);
}
else
{
int i;
for(i = currentarg; i < argc; i++)
{
printf("%d %s\n",i,argv[i]);
//if file is a single dash, dog reads from standard input
if(strcmp(argv[i],"-") == 0)
{
input_file = stdin;
printfile(input_file,0);
fflush(stdin);
fclose(stdin);
clearerr(stdin);
}
else if ((input_file = fopen(argv[i], "r")) == NULL)
{
fprintf(stderr, "%s: %s: No such file or directory\n", argv[0], argv[i]);
return 1;
}
else
{
printfile(input_file,0);
fflush(input_file);
fclose(input_file);
clearerr(input_file);
}
}
}
return 0;
}
int printfile(FILE *source, int N)
{
//used to print characters of a file to the screen
//characters can be shifted by some number N (between 0 and 25 inclusive)
char c;
while((c = fgetc(source)) != EOF)
{
fputc((c+N)%ASCII_LENGTH,stdout);
}
printf("***** %c %d",c,c==EOF);
return 0;
}

For one thing, you can't expect to be able to read from stdin after you've closed it:
fclose(stdin);

fflush(stdin); is undefined behaviour, as is fflush on all files open only for input. That's sort of like flushing the toilet and expecting the waste to come out of the bowl, because fflush is only defined for files open for output! I would suggest something like for (int c = fgetc(stdin); c >= 0 && c != '\n'; c = fgetc(stdin)); if you wish to discard the remainder of a line.
Furthermore, fgetc returns int for a reason: Inside the int will be an unsigned char value or EOF. c should be an int, not a char. EOF isn't a character! It's a negative int value. This differentiates it from any possible characters, because successful calls to fgetc will only return a positive integer rather than a negative EOF. fputc expects input in the form of an unsigned char value. char isn't required to be unsigned. Providing your fgetc call is successful and you store the return value into an int, that int should be safe to pass on to fputc.

Related

Copying one file to another: scanf keeps looping

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

How to delete blank lines from a txt file with c, in linux - whitout Bash

I tried creating a .c program that when it is run it takes a file and it prints only the lines on which there is something (a space, a letter, a number....etc) not the blank lines.
I need to run this on a virtual machine using ubuntu(it's running the newest version of ubuntu). So far I have only managed to print it's contents but not on lines like they are in the file.
The code:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char *name = argv[1];
FILE *f = fopen(name, "r");
char x;
while(fscanf(f, "%c" , &x) > 0)
{
printf("%c", x);
if(x == '\n')
{
printf("\n");
}
}
}
file contents:
as
d
3
results:
asd3
desired result:
as
d
3
First, you have no error checking. That makes your program difficult to use.
Second, you output every character unconditionally and then output newlines an extra time. What you want to do is output every character once, unless it's a newline right after a newline (as that would create an empty line) in which case you don't want to output it.
Here's the code fixed up:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
if (argc < 2)
{
fprintf (stderr, "An argument is required\n");
return -1;
}
char *name = argv[1];
FILE *f = fopen(name, "r");
if (f == NULL)
{
fprintf (stderr, "Unable to open file for reading\n");
return -1;
}
char x, px = '\n';
while(fscanf(f, "%c" , &x) > 0)
{
// don't output a newline after a newline
if ((x != '\n') || (px != '\n'))
printf("%c", x);
// keep track of what character was before the next one
px = x;
}
}
It really would be much easier to just read each line in and then output the line if it's non-empty.
You can use fgets() function which gets the entire line including the new line character (\n), After you read the line, you can skip printing the line if the first character (line[0]) is newline character.
Here is the code segment that does it, You need to error checking for argc and file existence as done by #David Schwartz
char line[200];
while (fgets(line, 100, fp))
{
if (line[0] != '\n')
printf(line);
}
This should work.

Segmentation Fault on my while loop

I am trying to count the number of lines and characters whatever they may be in a file that I specify from argv. But I get a segmentation fault when I hit the while loop for some reason. The program runs fine without the while loop, though it only goes through once.
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
if(argc != 2) {
return 0;
}
FILE *fp;
char c;
int lines = 0;
int chs = 0;
fp = fopen(argv[1], "r");
//Segmentation Fault happens here on the while loop
while((c = fgetc(fp)) != EOF) {
if(c == '\n') {
lines += 1;
}
else {
chs += 1;
}
}
printf("Charaters: %d\n", chs);
printf("lines: %d\n", lines);
if(fp){
fclose(fp);
}
return 0;
}
Your code needs to be follow Idiomatic C more closely.
You should validate fopen immediately, instead of after you've already attempted to use fp.
fgetc returns int, not char. This is because it needs to return side-channel information about the status of the stream (i.e. EOF), this information cannot be represented by char, but you can safely cast the int value to char if the value is not EOF.
Your code treats \r as a regular character when it is commonplace for \r\n to represent a line-break (not just a solitary \n), you might want to consider how you handle different character classes.
Your program does not handle non-trivial encodings (i.e. it will only correctly handle files in your system's native encoding, presumably ASCII). You should use a Unicode library to correctly read individual characters from a file: for example your program will treat a surrogate-pair in UTF-8 as two characters instead of 1, and would incorrectly count UTF-16 files.
Better:
FILE* fp = fopen( argv[1], "r" );
if( !fp ) {
printf( "Could not open file \"%s\" for reading.\r\n", argv[1] );
return 1;
}
int lines = 0;
int chars = 0;
int nc;
while( ( nc = fgetc( fp ) ) != EOF ) {
char c = (char)nc;
if ( c == '\n' ) lines++;
else if( c != '\r' ) chars++;
}
printf( "Characters: %d\r\nLines: %d\r\n", chars, lines );
fclose( fp );
return 0;

Debug Assertion Error in C

got some code here that won't compile correctly because it is saying that my pointer is already null when i am testing for a not null expression in my main function. here is the code :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXCODE 53
#define MAXMESSAGE 256
void getCode(char *codeIn, char *filename) {
FILE *codeFile;
/* Open the file with the code */
codeFile = fopen(filename, "r");
if (codeFile == NULL) {
printf("Error opening the code file - program terminated\n");
exit(1);
}
/* Read the first (and assumed only) line from the file */
fgets(codeIn, MAXCODE, codeFile);
/* Terminate the string with /0 */
codeIn[MAXCODE] = '\0';
/* Close the file */
fclose(codeFile);
return;
}
int getMessage(int *message, char *filename) {
FILE *messageFile;
int counter = 0;
/* Open the file with the message */
messageFile = fopen(filename, "r");
if (messageFile == NULL) {
printf("Error opening the message file - program terminated\n");
exit(1);
}
/* Read one number at a time from the file and store it */
while (!feof (messageFile))
{
fscanf (messageFile, "%d", (message+counter));
counter++;
}
/* Close the file */
fclose(messageFile);
return (counter);
}
void sortMessage(int *message, int size) {
int i, j, temp;
for (i=0; i<size-1; i++) {
for (j=i; j<size; j++) {
if (message[i]>message[j]) {
temp = message[i];
message[i] = message[j];
message[j] = temp;
}
}
}
return;
}
void decodeMessage(char *codeIn, int *message, int size) {
FILE *outputFile;
int i = 0;
/* Open the output file */
outputFile = fopen("csis.txt", "w");
if (outputFile == NULL) {
printf("Error opening the output file - program terminated\n");
exit(1);
}
for (i=0; i< size; i++) {
fprintf(outputFile, "%c", codeIn[message[i]%100]);
printf("%c", codeIn[message[i]%100]);
}
printf("\n");
/* Close the file */
fclose(outputFile);
return;
}
int main(int argc, char *argv[])
{
char code[MAXCODE];
int msg[MAXMESSAGE];
int msgSize;
if (argc != 3) {
printf("This program takes two arguments: the name of the file with the code, and the name of the file with the encoded message\n");
}
getCode(code, argv[1]);
msgSize = getMessage(msg, argv[2]);
sortMessage(msg, msgSize);
decodeMessage(code, msg, msgSize);
return;
}
So basically my code is using two files called codefile.txt and msgfile.txt to decode the secret message and write the decoded sequence to a new text file called csis.
As woolstar pointed out in the comments, you don't need to NUL terminate your codeIn array following fgets, because fgets will do that for you. In fact, this constitutes an overflow which we can best see by considering what happens when MAXCODE is 1: codeIn contains only one element: codeIn[0], and accessing codeIn[1] is an error.
Similarly, since MAXCODE is 53 and that's how many elements pointed to by codeIn, codeIn[message[i]%100] is suspicious because there's a potential for message[i]%100 to be an invalid index. While we're on this note, it might be wise to make message[i] an unsigned int so that it can't be negative. The format specifier (for printf and scanf) corresponding to unsigned int is %u.
while ( !feof(messageFile) ) is wrong because the EOF flag isn't set until an attempt is made at reading. Between attempting to read and your EOF test, however, you've incremented counter which means you've counted one too many items. Perhaps your loop should look like this:
while (fscanf(messageFile, "%d", (message+counter)) == 1)
{
counter++;
}
Note that this code assumes you've chosen to keep message[i] as an int. If you've chosen to use unsigned int instead, of course you'll want to use the %u format specifier.
You can probably see that feof is mostly superfluous... You can usually test for erroneous reads by checking the return value. Try to avoid feof in the future.
Your main function has a return type of int, yet at the end of it you have a return; statement which doesn't return an int value. Remove that. It's probably causing errors during compilation.
Presumably, when argv != 3 you want to return from main so you don't end up processing invalid arguments... Make sure you return an int value, e.g.
if (argc != 3) {
printf("This program takes two arguments: the name of the file with the code, and the name of the file with the encoded message\n");
return 0;
}

Find instance of different character in a file

In this program, I want to print out the instance of different characters in a file. The output will contain three variable, the number of occurrence, the hex of the letter, and the letter itself. Can someone help me with this? I am stuck!
Results of program should be something like this:
10 instance of character 0x4s (O)
10 instance of character 0x51 (W)
10 instance of character 0x51 (Y)
2 instances of character 0x65 (a)
18 instances of character 0x67 (c)
16 instances of character 0x81 (d)
//here is my program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char FILE_NAME[] = "input.txt";
int main(argc, *argv[]) {
char temp;
char count[255];
FILE *in_file;
int ch;
fp = fopen(FILE_NAME, "r");
if (in_file == NULL) {
printf("Can not open %s \n", FILE_NAME);
exit(0);
}
while (!feof(fp)) {
ch = fgetc(fp);
if(strchr(count, ch)!= NULL)
{
}
}
printf("%d instance of character (%c)", count);
fclose(in_file);
return (0);
}
Here's what you want (based on your code, with many comments by me):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h> // you need this to use isupper() and islower()
const char FILE_NAME[] = "input.txt";
int main(int argc,char *argv[]) {
char temp;
unsigned count[52] = {0}; // An array to store 52 kinds of chars
FILE *fp;
int i;
fp = fopen(FILE_NAME, "r");
if (fp == NULL) {
printf("Can not open %s \n", FILE_NAME);
exit(0);
}
while((temp = fgetc(fp)) != EOF) { // use this to detect eof
if(isupper(temp))
count[26+(temp-'A')]++; // capital letters count stored in 26-51
if(islower(temp))
count[temp-'a']++; // lower letters count stored in 0-25
}
fclose(fp); // When you don't need it anymore, close it immediately.
for(i = 0; i < 26; i++)
if(count[i])
printf("%d instance of character 0x%x (%c)\n", count[i], 'a'+i, 'a'+i);
for(; i < 52; i++)
if(count[i])
printf("%d instance of character 0x%x (%c)\n", count[i], 'A'+i-26, 'A'+i-26);
return (0);
}
Your array count is not a string, so using strchr() on it is not a good idea. Also, it's of type char, so it has very limited range for larger files.
You should probably use something like unsigned long count[256]. Make sure to initialize the counts to 0 before starting.
Also, don't use feof(). Just loop calling fgetc() until the returned character (which, correctly, has type int) is EOF. Cast it to something positive before using it to index into count for the increment.

Resources