c count lines in a file avoid EOF - c

On numerous sources, you can find a simple C program to count the number of lines in a file. I'm using one of these.
#include <stdio.h>
int main(int argc, char* argv[]) {
FILE *file;
long count_lines = 0;
char chr;
file = fopen(argv[1], "r");
while ((chr = fgetc(file)) != EOF)
{
count_lines += chr == '\n';
}
fclose(file); //close file.
printf("%ld %s\n", count_lines, argv[1]);
return 0;
}
However, it fails to count the num. of lines in Top2Billion-probable-v2.txt. It stops on the line
<F0><EE><E7><E0><EB><E8><FF>
and outputs
1367044 Top2Billion-probable-v2.txt
when it should output 1973218846 lines. wc -l somehow avoids the problem (and is amazingly faster).
Should I give up with a correct C implementation of counting the number of lines of a file or how should I space the special characters as wc does?

fgetc() returns the character read as an unsigned char cast to an int or EOF. Hence declaring chr as int instead of char should solve the issue.

Related

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.

Get the length of each line in file with C and write in output file

I am a biology student and I am trying to learn perl, python and C and also use the scripts in my work. So, I have a file as follows:
>sequence1
ATCGATCGATCG
>sequence2
AAAATTTT
>sequence3
CCCCGGGG
The output should look like this, that is the name of each sequence and the count of characters in each line and printing the total number of sequences in the end of the file.
sequence1 12
sequence2 8
sequence3 8
Total number of sequences = 3
I could make the perl and python scripts work, this is the python script as an example:
#!/usr/bin/python
import sys
my_file = open(sys.argv[1]) #open the file
my_output = open(sys.argv[2], "w") #open output file
total_sequence_counts = 0
for line in my_file:
if line.startswith(">"):
sequence_name = line.rstrip('\n').replace(">","")
total_sequence_counts += 1
continue
dna_length = len(line.rstrip('\n'))
my_output.write(sequence_name + " " + str(dna_length) + '\n')
my_output.write("Total number of sequences = " + str(total_sequence_counts) + '\n')
Now, I want to write the same script in C, this is what I have achieved so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
input = FILE *fopen(const char *filename, "r");
output = FILE *fopen(const char *filename, "w");
double total_sequence_counts = 0;
char sequence_name[];
char line [4095]; // set a temporary line length
char buffer = (char *) malloc (sizeof(line) +1); // allocate some memory
while (fgets(line, sizeof(line), filename) != NULL) { // read until new line character is not found in line
buffer = realloc(*buffer, strlen(line) + strlen(buffer) + 1); // realloc buffer to adjust buffer size
if (buffer == NULL) { // print error message if memory allocation fails
printf("\n Memory error");
return 0;
}
if (line[0] == ">") {
sequence_name = strcpy(sequence_name, &line[1]);
total_sequence_counts += 1
}
else {
double length = strlen(line);
fprintf(output, "%s \t %ld", sequence_name, length);
}
fprintf(output, "%s \t %ld", "Total number of sequences = ", total_sequence_counts);
}
int fclose(FILE *input); // when you are done working with a file, you should close it using this function.
return 0;
int fclose(FILE *output);
return 0;
}
But this code, of course is full of mistakes, my problem is that despite studying a lot, I still can't properly understand and use the memory allocation and pointers so I know I especially have mistakes in that part. It would be great if you could comment on my code and see how it can turn into a script that actually work. By the way, in my actual data, the length of each line is not defined so I need to use malloc and realloc for that purpose.
For a simple program like this, where you look at short lines one at a time, you shouldn't worry about dynamic memory allocation. It is probably good enough to use local buffers of a reasonable size.
Another thing is that C isn't particularly suited for quick-and-dirty string processing. For example, there isn't a strstrip function in the standard library. You usually end up implementing such behaviour yourself.
An example implementation looks like this:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#define MAXLEN 80 /* Maximum line length, including null terminator */
int main(int argc, char *argv[])
{
FILE *in;
FILE *out;
char line[MAXLEN]; /* Current line buffer */
char ref[MAXLEN] = ""; /* Sequence reference buffer */
int nseq = 0; /* Sequence counter */
if (argc != 3) {
fprintf(stderr, "Usage: %s infile outfile\n", argv[0]);
exit(1);
}
in = fopen(argv[1], "r");
if (in == NULL) {
fprintf(stderr, "Couldn't open %s.\n", argv[1]);
exit(1);
}
out = fopen(argv[2], "w");
if (in == NULL) {
fprintf(stderr, "Couldn't open %s for writing.\n", argv[2]);
exit(1);
}
while (fgets(line, sizeof(line), in)) {
int len = strlen(line);
/* Strip whitespace from end */
while (len > 0 && isspace(line[len - 1])) len--;
line[len] = '\0';
if (line[0] == '>') {
/* First char is '>': copy from second char in line */
strcpy(ref, line + 1);
} else {
/* Other lines are sequences */
fprintf(out, "%s: %d\n", ref, len);
nseq++;
}
}
fprintf(out, "Total number of sequences. %d\n", nseq);
fclose(in);
fclose(out);
return 0;
}
A lot of code is about enforcing arguments and opening and closing files. (You could cut out a lot of code if you used stdin and stdout with file redirections.)
The core is the big while loop. Things to note:
fgets returns NULL on error or when the end of file is reached.
The first lines determine the length of the line and then remove white-space from the end.
It is not enough to decrement length, at the end the stripped string must be terminated with the null character '\0'
When you check the first character in the line, you should check against a char, not a string. In C, single and double quotes are not interchangeable. ">" is a string literal of two characters, '>' and the terminating '\0'.
When dealing with countable entities like chars in a string, use integer types, not floating-point numbers. (I've used (signed) int here, but because there can't be a negative number of chars in a line, it might have been better to have used an unsigned type.)
The notation line + 1 is equivalent to &line[1].
The code I've shown doesn't check that there is always one reference per sequence. I'll leave this as exercide to the reader.
For a beginner, this can be quite a lot to keep track of. For small text-processing tasks like yours, Python and Perl are definitely better suited.
Edit: The solution above won't work for long sequences; it is restricted to MAXLEN characters. But you don't need dynamic allocation if you only need the length, not the contents of the sequences.
Here's an updated version that doesn't read lines, but read characters instead. In '>' context, it stored the reference. Otherwise it just keeps a count:
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h> /* for isspace() */
#define MAXLEN 80 /* Maximum line length, including null terminator */
int main(int argc, char *argv[])
{
FILE *in;
FILE *out;
int nseq = 0; /* Sequence counter */
char ref[MAXLEN]; /* Reference name */
in = fopen(argv[1], "r");
out = fopen(argv[2], "w");
/* Snip: Argument and file checking as above */
while (1) {
int c = getc(in);
if (c == EOF) break;
if (c == '>') {
int n = 0;
c = fgetc(in);
while (c != EOF && c != '\n') {
if (n < sizeof(ref) - 1) ref[n++] = c;
c = fgetc(in);
}
ref[n] = '\0';
} else {
int len = 0;
int n = 0;
while (c != EOF && c != '\n') {
n++;
if (!isspace(c)) len = n;
c = fgetc(in);
}
fprintf(out, "%s: %d\n", ref, len);
nseq++;
}
}
fprintf(out, "Total number of sequences. %d\n", nseq);
fclose(in);
fclose(out);
return 0;
}
Notes:
fgetc reads a single byte from a file and returns this byte or EOF when the file has ended. In this implementation, that's the only reading function used.
Storing a reference string is implemented via fgetc here too. You could probably use fgets after skipping the initial angle bracket, too.
The counting just reads bytes without storing them. n is the total count, len is the count up to the last non-space. (Your lines probably consist only of ACGT without any trailing space, so you could skip the test for space and use n instead of len.)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]){
FILE *my_file = fopen(argv[1], "r");
FILE *my_output = fopen(argv[2], "w");
int total_sequence_coutns = 0;
char *sequence_name;
int dna_length;
char *line = NULL;
size_t size = 0;
while(-1 != getline(&line, &size, my_file)){
if(line[0] == '>'){
sequence_name = strdup(strtok(line, ">\n"));
total_sequence_coutns +=1;
continue;
}
dna_length = strlen(strtok(line, "\n"));
fprintf(my_output, "%s %d\n", sequence_name, dna_length);
free(sequence_name);
}
fprintf(my_output, "Total number of sequences = %d\n", total_sequence_coutns);
fclose(my_file);
fclose(my_output);
free(line);
return (0);
}

loop doesn't end while reading from a textfile

I am typing a very simple C program which reads characters from a file and displays it "encrypted" by replacing each character with the one 3 characters after (Caesar cipher).
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *inFile;
int ch;
int ch1;
if (argc!= 2){
printf("Usage: Code <inputfile> [> Outfile]\n");
return(-1);
}
inFile = fopen(argv[1], "r");
while ((ch1=toupper(fgetc(inFile)))!='\0') {
ch=ch1;
if (ch >=33 && ch <=255) //exclude special chars
ch += 3;
putchar(ch);
}
printf("END\n");
fclose(inFile);
return 0;
}
I compile it with gcc with gcc encode.c and then run ./a textfile. A line follows with the "encoded" text, then, after 2 seconds, the terminal is filled with garbage, specifically an ascii square. I escape it with Ctrl-C. Obviously the while loop never ends, since I never see the END string. I wondered whether '\0' is the problem and i replaced it with NULL but not only the problem persists, but I also receive a warning during compile.

Why is my file IO infinite looping in c?

I am new to file IO in c. I decided to write a simple script in c that copies a file to a new file for practice:
#include <stdio.h>
void main(int argc, char* argv[])
{
if (argc != 3)
{
printf("Usage: ./myFile source destination");
exit(-1);
}
FILE * src = fopen(argv[1], "r");
if (src == NULL)
{
printf("source file not found", argv[1]);
exit(-1);
}
FILE* dest = fopen(argv[2], "w");
unsigned char c;
do {
c = fgetc(src);
fputc(c, dest);
} while (c != EOF);
}
However, I am getting an infinite loop. Is this because I never actually hit a character called EOF?
Also, is there a faster way to write this script aside from reading each character 1 at a time?
Declare c as an int and it'll work.
EOF is not a valid value for a character, because if it were, the presence of that character in a file could mislead code into thinking that file has ended when it actually hasn't. That's precisely why fgetc() actually returns an int, not a char.
Edit: Your code also has another bug: when fgetc() does return EOF, you pass that value to fputc() before ending the loop, causing an extra character to appear at the end of your output file. (The extra character will be whatever you get when you cast EOF to unsigned char on your system, typically character 255 == 0xFF == (unsigned char) -1.) To fix that, you can rewrite your loop like this:
int c;
while ((c = fgetc(src)) != EOF) {
fputc(c, dst);
}
or, if you don't like assigments in loop conditions:
while (1) {
int c = fgetc(src);
if (c == EOF) break;
fputc(c, dst);
}
Anyway, it would be much more efficient to read and write the data in chunks using fread() and fwrite(), e.g. like this:
unsigned char buf[65536];
while (1) {
int n = fread(buf, 1, sizeof(buf), src);
fwrite(buf, 1, n, dst);
if (n < sizeof(buf)) break; /* end of file or read error */
}
Also, it would be a good idea to include some error checking, since both reading and writing a file can fail for a variety of unexpected reasons. You can use ferror() to tell whether an error has occurred on a particular I/O stream.
EOF is not an unsigned char but an int. See the prototype of fgetc:
int fgetc(FILE *stream);

Not reading from stdin properly

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.

Resources