The input text file is like so:
Hello my
name is
mark.
and
im
going
to
love
c!
Code:
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]){
FILE *pFile;
char x[60];
pFile = fopen("test0.txt","r");
if(pFile != NULL){
while(fscanf(pFile, " %60s", x) == 1){
printf("%s",x);
}
}
}
Output text file is:
Hellomynameismark.andimgoingtolovec!
I want the Output to be like:
Hello my name is mark. and im going to love c!
Very new C programmer so only know the basics.
Edit----
int main(int argc, char *argv[]){
FILE *pFile;
char x[60],line[60];
pFile = fopen("test0.txt","r");
while(!feof(pFile)){
fgets(line, 60, pFile);
strtok(line, "\r\n");
printf("%s", line );
}
fclose(pFile);
Output:
Hello myname is mark.andim goingtolovec!
This does not leave spaces between new lines. However if I take out the strtok line the output will be like this:
Hello my
name is
mark.
and
im
going
to
love
c!
--Edit
.sp 2
.ce
This is an example file for formatting.
.sp 1
The above line
was formatted using a .ce 1 command, which means 'centre
the following line',
so it should appear in the
centre of the page.
The paragraph was separated from the heading using
a .sp 1 command to create a single blank line.
There should also be two blank lines above the centred heading to make reading it slightly easier.
The simple answer is:
while(fscanf(pFile, " %59[^\n]%*c", x) == 1)
Here %[^\n] uses the character class [stuff] to read everything up to the newline. %*c simply reads and discards the newline without adding it to the match count for fscanf.
However for line-oriented input, you should really use one of the line-oriented functions provided by the standard library (e.g. fgets or POSIX getline).
Using fgets & strtok
As you have taken from the comment, the use of feof is going to cause you nothing but grief. You will want to simply use the return of fgets to determine end of file. Here is an example that puts all the pieces of the puzzle together:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXWDS 20
#define MAXCHR 60
int main (int argc, char **argv) {
char line[MAXCHR] = {0};
char *words[MAXWDS] = {NULL};
FILE *pFile = NULL;
size_t i, index = 0;
/* open file for reading (if provided), or read from stdin */
if (!(pFile = argc > 1 ? fopen (argv[1], "r") : stdin)) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (line, 60, pFile))
{
char *p = line;
/* split line into tokens, stored in words[] */
for (p = strtok (p, " \r\n"); p; p = strtok (NULL, " \r\n")) {
words[index++] = strdup (p); /* allocate & copy */
if (index == MAXWDS) /* check pointer limit */
break;
}
}
if (pFile != stdin) fclose (pFile);
/* output in a single line */
for (i = 0; i < index; i++) {
printf (" %s", words[i]);
free (words[i]); /* free allocated memory */
}
putchar ('\n');
return 0;
}
Compile
gcc -Wall -Wextra -o bin/fgets_strtok fgets_strtok.c
Output
$ ./bin/fgets_strtok dat/hellomark.txt
Hello my name is mark. and im going to love c!
Note: to simply print the line out with spaces between the words, as long as there is already a space between each of the words in each line, there is no reason to go to the trouble to separate each line into individual words, you can simply print the contents of each line out in a space separate fashion. The only issue you run into using fgets is that it will also read the newline (or carriage return, newline) as part of the string. That is simple to remove. You can replace the entire read loop with:
while (fgets (line, 60, pFile))
{
size_t len = strlen (line);
/* strip trailing newline (or carriage return newline ) */
while (len && (line[len-1] == '\n' || line[len-1] == '\r'))
line[--len] = 0; /* overwrite with null-terminating char */
words[index++] = strdup (line); /* allocate & copy */
if (index == MAXWDS) /* check pointer limit */
break;
}
Output
$ ./bin/fgets_mark <dat/hellomark.txt
Hello my name is mark. and im going to love c!
Standard Way to Read from File Only (not File or stdin)
I apologize for the getting ahead of you a bit by including a way to either open a file (if provided on the command line) or read from stdin (if no filename was provided). The standard way is to first check that the correct number of arguments were provided on the command line, and then open the filename provided, validate it is open, and then process input. What I did was throw a ternary operator into the fopen command that said.
pFile = argc > 1 ? fopen (argv[1], "r") : stdin
The right side of the '=' sign is a ternary operator, which is simply a shorthand for if -> then -> else. What it does is ask is argc > 1? If that tests true, then pFile = fopen (argv[1], "r");. If argc > 1 tests false, then pFile = stdin;
See if the standard way makes more sense:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXWDS 20
#define MAXCHR 60
int main (int argc, char **argv) {
char line[MAXCHR] = {0};
char *words[MAXWDS] = {NULL};
FILE *pFile = NULL;
size_t i, index = 0;
/* validate sufficient input */
if (argc < 2 ) {
fprintf (stderr, "error: insufficient input, usage: %s filename\n", argv[0]);
return 1;
}
/* open file provided on command line for reading */
pFile = fopen (argv[1], "r");
if (!pFile) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (line, 60, pFile)) /* read each line in file */
{
size_t len = strlen (line);
/* strip trailing newline (or carriage return newline ) */
while (len && (line[len-1] == '\n' || line[len-1] == '\r'))
line[--len] = 0; /* overwrite with null-terminating char */
words[index++] = strdup (line); /* allocate & copy */
if (index == MAXWDS) /* check pointer limit */
break;
}
if (pFile != stdin) fclose (pFile);
/* output in a single line */
for (i = 0; i < index; i++) {
printf (" %s", words[i]);
free (words[i]); /* free allocated memory */
}
putchar ('\n');
return 0;
}
A simple state machine does the trick - no line length limitation.
#include <stdio.h>
int main(void) {
FILE *pFile = fopen("test0.txt","r");
if(pFile != NULL) {
int previous_isspace = 0;
int ch;
for (;;) {
ch = fgetc(pFile);
if (ch == EOF) break;
if (isspace(ch)) {
previous_isspace = 1;
} else {
if (previous_isspace == 1) {
fputc(' ', stdout);
}
previous_isspace = 0;
fputc(ch, stdout);
}
}
fclose(pFile);
fputc('\n', stdout); // If code should have a \n at the end
}
}
I think, It is sufficient take a look am i miss anything.
if(pFile != NULL){
// while(fscanf(pFile, " %60s", x) == 1){
while (fgets(x, sizeof(x), pFile) != NULL) {
token = strtok(x,"\r\n");
if(token != NULL)
printf("%s ",x);
else
printf("%s",x);
}
fclose(pFile);
}
Related
I want to read a list of words from a file, which has one word per line.
The words should have up to 4 characters each. How can I produce an error if one of the lines is longer than that?
I tried reading the words using fgets
char buf[5];
fgets(buf, 5, stdin);
and with scanf
char buf[5];
scanf("%4s", &buf);
but in both cases it splits long lines into smaller lines. For example qwerasdf is read as two words, qwer and asdf. Is there a way to detect that it tried to read a long line with more than 4 characters and give an error instead?
The only alternative I can think of is reading the input character-by-character and taking care of everything by myself. But is there a simpler solution using functions from the standard library?
You could check for the length of the read string and since fgets also reads the newline character, you could explicitly check for '\n' as the last input character.
char buf[6];
while (fgets(buf, sizeof(buf), stdin)) {
if (strlen(buf) > 5
|| (strlen(buf) == 5 && buf[strlen(buf) - 1] != '\n')) {
fprintf(stderr, "line too long\n");
exit(EXIT_FAILURE);
}
}
The buffer must consist of at least six characters: 4 input characters + 1 newline character + the string terminating NUL byte.
You are making an excellent choice reading with fgets(), the only rule-of-thumb you are breaking is don't skimp on buffer size. But, even if you do, you can handle things properly with fgets().
When you read a line from a file, fgets() (or POSIX getline()) read and include the '\n' as part of the buffer they fill (if there is room). If you are expecting up to 4-characters, then a buffer size of 5 is too-short-by-one to accommodate all your characters, the nul-terminating character, and the '\n'. Your circumstance attempting to read a 4-character line ("cats") with a 5-character buffer with fgets() would result in buf holding:
+---+---+---+---+---+
| c | a | t | s | \0| --> '\n' remains unread
+---+---+---+---+---+
You can gracefully handle that as well (but better not to skimp on buffer size) To gracefully handle the issue you need to check:
if '\n' is the last char in the buffer, complete line read, trim '\n' by overwriting with nul-terminating character;
otherwise, read next char;
if next char is '\n', then OK, you read all chars and there wasn't room for the '\n' which you just read and checked -- continue reading the next line;
else if next char is EOF, then you read all characters in the last line in a file with a non-POSIX end-of-file (no '\n' after the final line of data), break read loop you found EOF;
else additional character remain unread in the line, read and discard characters until the next '\n' or EOF is found
Putting that logic together, you could do:
#include <stdio.h>
#include <string.h>
int main (void) {
char buf[5];
while (fgets (buf, 5, stdin)) { /* read each line */
if (strchr (buf, '\n')) /* if '\n' found - line read */
buf[strcspn (buf, "\n")] = 0; /* nul-termiante at '\n' */
else { /* otherwise */
int c = getchar(); /* read next chars */
if (c == '\n') /* if '\n', OK read next line */
continue;
else if (c == EOF) /* if EOF, OK, non-POSIX eof */
break;
fputs ("error: line too long - discarding remainder.\n", stderr);
for (; c != '\n' && c != EOF; c = getchar()) {}
}
}
}
Look things over and let me know if you have further questions.
Here I made this function to read the file
char by char and returns only one line per call
so now you can read your file line by line, the
type Line has an array of chars value where we store
the line and an int hasNextLine 1 or 0 (bool)
that tell you if the file has another line or no,
this is handy when you loop over the file line by line.
#include <stdlib.h>
#include <stdio.h>
typedef struct {
char *value;
int hasNextLine;
} Line;
Line * getLine(FILE *file) {
Line *line = (Line *)malloc(sizeof(Line));
if(line == NULL) {
return NULL;
}
line->value = NULL;
line->hasNextLine = 1;
int n = 0, c;
while(1) {
c = getc(file);
char *tmpStr = (char *)realloc(line->value, n + 2);
if(tmpStr == NULL) {
line->hasNextLine = -1;
return line;
}
line->value = tmpStr;
if(c == EOF) {
line->hasNextLine = 0;
line->value[n] = '\0';
return line;
}
if(c == '\n') {
line->value[n] = '\0';
return line;
}
line->value[n] = c;
n++;
}
return line;
}
Usage:
// example reading one line
int main() {
FILE *f = fopen("your_file.txt", "r");
if(f == NULL) {
printf("File not found!");
return 1;
}
Line *l = getLine(f);
if(l != NULL) {
printf("%s\n", l->hasNextLine != -1 ? l->value :
"Error: while getting the line");
free(l->value);
free(l);
}
fclose(f);
return 0;
}
// example reading the whole file
int main() {
FILE *f = fopen("your_file.txt", "r");
if(f == NULL) {
printf("File not found!");
return 1;
}
Line *l;
int hasNextLine;
while(1) {
l = getLine(f);
if(l != NULL) {
printf("%s\n", l->hasNextLine != -1 ? l->value :
"Error: while getting the line");
free(l->value);
hasNextLine = l->hasNextLine;
free(l);
}
if(hasNextLine <= 0) {
break;
}
}
fclose(f);
return 0;
}
you can make a custom function for user input
char * sgetLine(char *msg) {
printf("%s", msg);
Line *l = getLine(stdin);
char *strLine = NULL;
if(l == NULL) {
return NULL;
}else {
if(l->hasNextLine == -1) {
free(l->value);
free(l);
return NULL;
}
strLine = l->value;
free(l);
return strLine;
}
}
so now you can use one function call to print
the question and to get the answer (char array)
int main() {
char *l = sgetLine("What is your name? ");
if(l != NULL) {
printf("%s\n", l);
}
free(l);
return 0;
}
I want to ignore/skip the comments in a text file when I use fgets.
The problem is that I only can skip a comment if the first character in a line starts is #. Comments starts with # in my text file. But there are some # in my file.txt that are not the first character of a line, like so;
#Paths
A B #Path between A and B.
D C #Path between C and D.
A is my first node, B is my second node and when # comes I want to ignore the rest of text until the next line. My new node should be D and C etc. I can only use "r" in fopen function.
I have tried fgets but it reads line by line and fgetc doesn't help either.
bool ignore_comments(const char *s)
{
int i = 0;
while (s[i] && isspace(s[i])) i++;
return (i >= 0 && s[i] == '#');
}
FILE *file;
char ch[BUFSIZE];
file = fopen("e.txt", "r");
if (file == NULL) {
printf("Error\n");
fprintf(stderr, "ERROR: No file input\n");
exit(EXIT_FAILURE);
}
while(fgets(ch, BUFSIZE, file) != NULL)
{
if (line_is_comment(ch)) {
// Ignore comment lines.
continue;
printf("%c",*ch);
}
fscanf(file, "%40[0-9a-zA-Z]s", ch);
....
}
the following proposed code:
performs the desired functionality
cleanly compiles
properly checks for errors
this answer uses a state machine, based on: 'InComment'
and now, the proposed code:
#include <stdio.h>
#include <stdlib.h>
int main( void )
{
int InComment = 0;
FILE *fp = fopen( "file.txt", "r" );
if( !fp )
{
perror( "fopen to read -file.txt- failed" );
exit( EXIT_FAILURE );
}
int ch;
while( (ch = fgetc(fp)) != EOF )
{
if( ch == '#' )
{
InComment = 1;
}
else if( ch == '\n' )
{
InComment = 0;
fputc( ch, stdout );
}
else if( !InComment )
{
fputc( ch, stdout );
}
}
fclose( fp );
}
Also method names are different, but am I right with this version ?
Ignore my dirty method line_is_comment - from first version unless you want to play with ;-)
Extended test input:
#Paths
A B #Path between A and B.
D C #Path between C and D.
E F
G H
Output:
rest of line read
AB rest of line read
DC rest of line read
EF rest of line read
GH rest of line read
#include <stdio.h>
bool line_is_comment(const char *s)
{
char *commentPos = const_cast<char*>(strchr(s, '#'));
if(commentPos != NULL) {
*commentPos = 0; // cut-off chars after comment
//return true; // or false then to accept the line
return commentPos == s;
}
return false;
}
#define BUFSIZE 50
int main()
{
FILE *file;
char ch[BUFSIZE];
file = fopen("e.txt", "r");
if (file == NULL) {
printf("Error\n");
fprintf(stderr, "ERROR: No file input\n");
exit(EXIT_FAILURE);
}
int x;
while(!feof(file)) {
x = fscanf(file, "%40[0-9a-zA-Z]s", ch);
if(x == 0) {
ch[0] = fgetc(file);
if(ch[0] == '#' || ch[0] == '\n') {
if(ch[0] != '\n') fgets(ch, BUFSIZE, file);
printf(" rest of line read\n");
}
} else if(x<0) break;
else {
printf("%c",*ch); // continue with ... undisclosed part here
}
}
return 0;
}
You can also make use of strcspn to trim all comments (and if not present, trim the line-endings from your buffer) in a single simple call. Where you would normally trim the line-ending from the buffer read by fgets() with:
ch[strcspn (ch, "\r\n")] = 0; /* trim line-ending */
You can simply add the "#" character to your reject list and nul-terminate there if a comment is present. That would reduce the complete task of removing comments beginning with '#' and outputting the newly formatted line to:
while (fgets (ch, BUFSIZE, fp)) { /* read every line */
ch[strcspn (ch, "#\r\n")] = 0; /* trim comment or line-ending */
puts (ch); /* output line w/o comment */
}
A short example taking the file to read as the first argument to the program (or reading from stdin by default if no argument is given), you could do:
#include <stdio.h>
#include <string.h>
#define BUFSIZE 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char ch[BUFSIZE];
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (ch, BUFSIZE, fp)) { /* read every line */
ch[strcspn (ch, "#\r\n")] = 0; /* trim comment or line-ending */
puts (ch); /* output line w/o comment */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
Example Input File
Borrowing Tom's example file :)
$ cat dat/comments_file.txt
#Paths
A B #Path between A and B.
D C #Path between C and D.
E F
G H
Example Use/Output
$ ./bin/comments_remove <dat/comments_file.txt
A B
D C
E F
G H
Look things over and let me know if you have further questions.
I need to sum up the numbers from each line in the file like this e.g.:
1 2 3
10 -1 -3
and the result I should write to another file in each line likes this:
6
6
And I have the problem when in each line after the last number in reading file have more spaces, for example, maybe I use the '_' to show this problem:
When my function works:
10_11_12 '\n'
1_2_3 '\n'
and when my function doesn't work:
10_11_12_ _ _ '\n'
1_2_3 '\n'
I think I know where is the problem, but I have no idea how to fix it.
It's my function here:
int num=0;
char s;
while(fscanf(file, "%d", &num)==1){
fscanf(file, "%c", &s);
sum+=num;
if(s=='\n'){
fprintf(res_file, "%d\n", sum);
sum=0;
}
}
The problem is that fscanf is expecting a pointer to a char. Within your function, you are using a regular char, s.
char s;
You can fix your issue by making s a pointer. First, Allocate memory.
char *s = malloc(sizeof(char) + 1);
Now we can properly scan into the variable, s, and then check for the newline character. The only difference here is now we check for the newline by dereferencing s.
if (*s == '\n')
Don't forget to clean up the memory leak with free()!
free(s);
I was able to get the desired output using the code below.
#include <stdio.h>
#include <stdlib.h>
int processInputFile(char *filename)
{
FILE *ifp;
int buffer = 0;
char *newline = malloc(sizeof(char) + 1);
int sum = 0;
if ((ifp = fopen(filename, "r")) == NULL)
{
fprintf(stderr, "Failed to open \"%s \" in processInputFile.\n", filename);
return -1;
}
while(fscanf(ifp, "%d", &buffer) == 1)
{
fscanf(ifp, "%c", newline);
sum += buffer;
if (*newline == '\n')
{
printf("%d\n", sum);
sum = 0;
}
}
free (newline);
fclose(ifp);
}
int main(int argc, char **argv)
{
if (argc < 2)
{
printf("Proper syntax: ./a.out <n>\n");
return -1;
}
processInputFile(argv[1]);
return 0;
}
Any kind of line-by-line processing in C is easier done by reading the line first, and then processing it. fgets(3) handles end-of-line for you; then you just need to scan what it read. Plus, in the real world, some lines won't scan: either they'll have errors, or your scan won't be general enough. When that happens, it's awfully handy to write the input to standard error, so you can see what you're looking at.
Here's a complete program that does what you want. It assumes lines are less than 80 bytes long and doesn't protect against invalid input, though.
#include <stdio.h>
#include <err.h>
int main( int argc, char *argv[] ) {
char line[80];
static const char *filename = "sum.dat";
FILE *input;
if( (input = fopen(filename, "r")) == NULL ) {
err(1, "could not open %s", filename);
}
for( int nlines = 0;
fgets(line, sizeof(line), input) != NULL;
nlines++ )
{
double value, sum = 0;
int n;
for( char *p = line; sscanf(p, "%lf%n", &value, &n) > 0; p += n ) {
sum += value;
}
printf( "line %d: sum = %lf\n", nlines, sum );
}
return 0;
}
Reading with a line-oriented input function like fgets() or POSIX getline() ensures that a complete line of input is consumed on each call. (don't skimp on buffer size). strtol was created to convert an unknown number of values per-line into long. You walk-a-pointer down your buffer by utilizing the endptr parameter filled by strtol after a successful conversion to point to the next character after the last digit converted.
This allows a simple method to use a pair of pointers, p your start-pointer and ep your end-pointer to work through an entire line converting values as you go. The basic approach is to call strtol, validate it succeeded, and then set p = ep; to advance to the start of your next conversion. strtol ignores leading whitespace.
Putting it altogether, you could do:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
/* (don't skimp on buffer-size) */
int main (int argc, char **argv) {
char buf[MAXC]; /* buffer to hold each line read */
size_t n = 0; /* line-counter */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
char *p = buf, *ep = p; /* pointer and end-pointer */
int sum = 0; /* variable to hold sum */
if (*buf == '\n') /* ignore empty lines */
continue;
while (*p && *p != '\n') {
errno = 0;
long tmp = strtol (p, &ep, 0); /* convert to temp long */
if (p == ep) { /* validate digits were converted */
fputs ("error: no digits extracted.\n", stderr);
break;
}
else if (errno) { /* validate no under/overflow occurred */
fputs ("error: underflow/overflow occurred.\n", stderr);
break;
}
else if (tmp < INT_MIN || INT_MAX < tmp) { /* validate in range */
fputs ("error: tmp exceeds range of int.\n", stderr);
break;
}
sum += tmp; /* add tmp to sum */
p = ep; /* set p to end-ptr (one past last digit used) */
}
n++; /* advance line counter */
printf ("sum line [%2zu] : %d\n", n, sum); /* output sum */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
(note: the if (*buf == '\n') which tests if the first character in the line is a newline character and simple skips to the next line, no need to worry about converting values in a empty line)
Example Use/Output
Using your data in dat/sumlines.txt produces the expected results.
$ ./bin/sumline dat/sumlines.txt
sum line [ 1] : 6
sum line [ 2] : 6
Let me know if you have further questions.
** Updated 26/10 -> First of all thank you all for you help, I am getting closer now, I need more work and studying but I really appreciate you are helping me a lot :-)
Still don't know why the first "rain" word in the input.txt file is not getting the positive output from strcmp, and from the cmd I can see the "<" bracket don't appear except for the LAST line which is the line that works.
Also checked the highlighted response from Removing trailing newline character from fgets() input
and even if I change the code to the following:
while( fgets (line, sizeof line, fp)!=NULL ) {
/* remove \n from at the end of the str buffer*/
char * pos;
/*
if ((pos = strchr(line, '\n')) != NULL)
*pos = '\0';
*/
line[strcspn(line, "\n")] = 0;
I get the same result as if I use the if block instead. Maybe I'm getting extra \0 which might be the case. Anyone has a link where I can read about the delimiters I just used, or a nice reference of a debugger, etc. ... which I will have a look as soon as I come here? Thank you so much in advance!
read5.c version: Now from that input.txt file, it had an extra space on the last "rain" word, I removed the space, and it was able to find and get that last word compare as a true result, running in the strcmp if block. but that was the only string that was a true positive result from that if block.
on the cmd I can see:
$./read5 input.txt rain output.txt sun
>Maria
>rain
>manel
>Bla bla
<rain>
Found it! rain
On the output.txt it becomes:
Maria
rain
manel
Bla bla
sun
read5.c
#include <stdio.h>
#include <string.h>
/**
* Compile program:
* gcc read3.c -o read3
*
*/
int main (int argc, char **argv) {
FILE *fp, *fo;
char *compare, *replace;
char line[246];
if (argc <= 4){
printf(">Missing arguments on the command line.\n");
printf(">Be sure you run the program as\n\"./read3 input.txt compare outout.txt replace\"\n\n");
}
/* opening file for reading */
fp = fopen(argv[1] , "r");
if(fp == NULL){
perror("Error opening input file");
return 1;
}
compare = argv[2];
fo = fopen(argv[3], "w");
if(fo == NULL){
perror("Error opening output file");
return 1; //TODO check if: return 1 because it was expected, right?
}
replace = argv[4];
/*
printf(); made to test version 2
//printf("We are going to compare %s\n", compare);
//printf("We are going to replace it with %s\n", replace);
*/
while( fgets (line, sizeof line, fp)!=NULL ) {
/* remove \n from at the end of the str buffer*/
char * pos;
if ((pos = strchr(line, '\n')) != NULL)
*pos = '\0';
/* print str enclosed in <> so we can see what str actually contains */
//printf("Inside the loop, got the string: %s\n", line);
//printing the strings with defined delimiters
printf("<%s>\n", line);
if(strcmp(compare, line) == 0){
printf("Found it! %s \n", line);
fprintf(fo, "%s\n", replace);
}
else{
fprintf(fo, "%s\n", line);
}
}
fclose(fp);
fclose(fo);
return 0;
}
First question with no edits: 25/10
I need to make a program that is run like this:
./read2 input.txt rain output.txt sun
It reads the input.txt, searches for rain string and if finds it, replaces it with sun string and outputs all the text from input.txt with the replacements to the output.txt.
But with the code that I have so far, the strcmp is not comparing the strings I want, maybe it has the extra space that I get on the command line, I don't know... for now what is doing is copying everything from input.txt to output.txt... It's running the else block always...
Read2.c:
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
FILE *fp, *fo;
char str[60];
//char* token;
/* opening file for reading */
fp = fopen(argv[1], "r");
char *compare = argv[2];
fo = fopen(argv[3], "w+");
char *replace = argv[4];
if (fp == NULL) {
perror("Error opening file");
return(-1);
}
//printf("We are going to compare %s\n", compare);
//printf("We are going to replace it with %s\n", replace);
while (fgets(str, 60, fp) != NULL) {
/* writing content to stdout */
//Take the \n out
//token = strtok(str, "\n");
printf("Inside the loop, got the string: %s\n", str);
if (strcmp(compare, str) == 0) {
//puts(str);
printf("Found it! %s \n", str);
fprintf(fo, "%s", replace);
} else {
fprintf(fo, "%s", str);
}
}
fclose(fp);
return(0);
}
input.txt:
Maria
rain
manel
Bla bla
rain
Ouput.txt becomes exactly as input.txt and before it was empty, so the code is working, except the if block that tests with strcmp.
The problem is the \n at the end of the str buffer. fgets adds the \n at end end of the line it reads, you need to get rid of it before comparing.
This is what you need:
while (fgets(str, 60, fp) != NULL) {
/* remove \n from at the end of the str buffer*/
char *pos;
if ((pos = strchr(str, '\n')) != NULL)
*pos = '\0';
/* print str enclosed in <> so we can see what str actually contains */
printf("Inside the loop, got the string: <%s>\n", str);
if (strcmp(compare, str) == 0) {
printf("Found it! %s\n", str);
fprintf(fo, "%s\n", replace);
}
else {
fprintf(fo, "%s\n", str);
}
}
Look at the comments in the code for explanations.
read.c
#include <stdio.h>
#include <string.h>
/**
* How to compile program:
* gcc read.c -o read
*
* How to run the program:
* .> ./read input.txt rainy output.txt sunny
* (On Windows MinGW compiler, simply:
* .> read input.txt rainy output.txt sunny - without ./)
*
*/
int main (int argc, char **argv) {
FILE *fp, *fo;
char *compare, *replace;
char line[246];
if (argc <= 4){
printf(">Missing arguments on the command line.\n");
printf(">Be sure you run the program as\n\"./read input.txt compare outout.txt replace\"\n\n");
}
/* Opening files for reading */
fp = fopen(argv[1] , "r");
if(fp == NULL){
perror("Error opening input file");
return 1;
}
compare = argv[2];
fo = fopen(argv[3], "w");
if(fo == NULL){
perror("Error opening output file");
return 1;
}
replace = argv[4];
while( fgets (line, (sizeof line), fp)!=NULL ) {
line[strcspn(line, "\n")] = 0;
if(strcmp(compare, line) == 0){
printf("Found it! %s \n", line);
fprintf(fo, "%s\n", replace);
}
else{
fprintf(fo, "%s\n", line);
}
}
fclose(fp);
fclose(fo);
return 0;
}
/*
Important info
strcspn ::
Locate first occurrence of character in string,
after locating the first occurrence of \n, replaces it by 0.
Sources::
https://stackoverflow.com/questions/2693776/removing-trailing-newline-character-from-fgets-input/28462221#28462221
Used to debug:
.>printf("1st: Reads input.txt, removes '\\n' from fgets, and prints it \n");
.>printf("2nd: Compares each line with 'rainy' \n");
.>printf("<%s>\n", line);
*/
input.txt
cloudy
rainy
chilly
rainy
rainy
Your approach fails because the lines read from the input file contain a trailing newline '\n' that makes the comparison return non zero.
You can strip the newline before comparing with the search string.
Note that there are other problems:
you should verify that enough command line arguments have been passed by testing argc > 4.
there is no need to open the output file in update mode "w+", "w" is simpler and better.
60 bytes is a bit small for the line array, limiting the longest line handled correctly to 58 bytes.
Here is an improved version:
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv) {
FILE *fp, *fo;
char *compare, *replace;
char line[256];
if (argc <= 4) {
printf("missing command line arguments\n");
return 1;
}
fp = fopen(argv[1], "r");
if (fp == NULL) {
perror("Error opening input file");
return 1;
}
compare = argv[2];
fo = fopen(argv[3], "w");
if (fo == NULL) {
perror("Error opening output file");
return 1;
}
replace = argv[4];
while (fgets(line, sizeof line, fp) != NULL) {
line[strcspn(line, "\n")] = '\0';
if (strcmp(line, compare) == 0) {
printf("fount it!);
fprintf(fo, "%s\n", replace);
} else {
fprintf(fo, "%s\n", line);
}
}
fclose(fp);
fclose(fo);
return 0;
}
Note that long lines will be broken into chunks that fit in the line array, so there may be false positives with the above naive approach.
You can remove this limitation completely with this inner loop:
int c;
int pos = 0;
int cmplen = strlen(compare);
for (;;) {
c = getc(fp);
if (c == '\n' || c == EOF) {
if (pos == cmplen) {
fprintf(fo, "%s", replace);
} else
if (pos > 0) {
fprintf(fo, "%*s", pos, compare);
}
pos = 0;
if (c == EOF)
break;
} else {
if (pos >= 0) {
if (compare[pos] == (char)c) {
pos++;
continue;
}
if (pos > 0) {
fprintf(fo, "%*s", pos, compare);
}
pos = -1;
}
}
putc(c, fo);
}
I'm learning c by myself and I'm actually trying to code a program which count the numbers of TAb in a file and if a line has a tab, I want to print the whole line and the numbers of tabs in this line. And if it is not muhch more difficult, I want you to help me to do that also if a line has more than 80 characters, print this line and the numbers of characters
I have this main function:
include <stdio.h> /* printf */
/* Prototype from tablen.c */
void tablen(const char *filename);
int main(int argc, char **argv)
{
if (argc < 2)
{
printf("Usage: tablen filename\n");
printf("where: filename - file to process.\n");
return -1;
}
tablen(argv[1]);
return 0;
}
This main function is very basic so I hope there is no error there.
And this function too:
include <stdio.h> /* FILE, fopen, feof, fgets, fclose */
include <string.h> /* strlen */
void tablen(const char *filename)
{
/*Variables*/
int i; /*loop controller */
int tabs = 0; /*number of tabs*/
int line = 0; /*current line*/
int size_string; /*size of the string*/
File *file; /* open and read the file */
file = fopen(filename, "rt"); /*open the file for read text*/
size_string = strlen(filename);
/*if we can read the file*/
if(file)
{
/*while we don't reach the end of file, we still reading*/
while (!feof(file))
{
for(i = 0; i < size_string; i++)
{
if(filename[i] == 9) /*ASCII value of TAB is 9 or '\'*/
{
tabs++;
}
if(tabs > 0)
{
printf("# %i: (tabs: %i) |", line, tabs);
}
if(filename[i] == '\n')
{
line++;
tabs = 0;
}
}
}
}
}
I've write this pseudo-code, I think it is correct
For counting tabs:
First open a file for read/text
while there are more line in the file(and reading one by one) we count the number of tabs
if we found a line with tabs, print the line and the count of tabs
Of course we close the file
For checking line lengths
First open a file for read/text and hile there are more lines in the file, we heck the length of each line.
If the line is longer than 80 characters we print that line with length information
I don't know if I am in the correct way because is the first time I try to deal with files
For counting number of tabs in a line at a time, it is better to use getline() function.
getline() reads a line from file stream and returns number of characters in read line. Read manual page of getline() for more information.
You can take a look at below code for the solution to your problem
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
FILE * fp;
char * line = NULL;
size_t len = 0;
ssize_t read;
int tabs=0,totaltabs=0,i;
fp = fopen(argv[1], "r");
if (fp == NULL)
exit(EXIT_FAILURE);
while ((read = getline(&line, &len, fp)) != -1) {
printf("Retrieved line of length %d\n", read);
for(i=0;i<read;i++){
if(line[i] == '\t')
tabs++;
}
if(tabs){
printf("line = %s\nNumber of tabs = %d\n",line,tabs);
totaltabs = totaltabs+tabs;
tabs=0;
}
if(read >=80)
printf("%s\n",line);
}
if (line)
free(line);
exit(EXIT_SUCCESS);
}