Confusion when dealing with files - c

I'm writing a program that takes a string from one file, a string from another file and writes them to a third file in separate columns. I really have two questions, If I use fgets to get the string from the files and it stops at the end of the line. Will it automatically know to start from the next line for the next string. Also, how can I format the input to make two columns. An example would be...
first string is "John" from the first file.
second string is "Appleseed" from the second file.
third file would have in it "John______________________Appleseed"
The second line of the third file would have "Benny__________________________ Backburner"
Just to format columns.

Do you want underscores, or will spaces suffice? It's much simpler with spaces. You can read the specification of printf()
to see the details of what the format strings do.
while (fgets(buffer1, sizeof(buffer1), fp1) != 0 &&
fgets(buffer2, sizeof(buffer2), fp2) != 0)
{
buffer1[strcspn(buffer1, "\n")] = '\0';
buffer2[strcspn(buffer2, "\n")] = '\0';
fprintf(fp3, "%-25s %s\n", buffer1, buffer2);
}
This reads one line from each of the first two files, removes the newlines from the buffer, and then formats them with the first column left-justified in a width of 25, and the second printed after 3 spaces.
If you must use underscores instead of spaces, then you need something like this:
char uscore[256];
memset(uscore, '_', sizeof(uscore)-1);
uscore[sizeof(uscore)-1] = '\0';
while (fgets(buffer1, sizeof(buffer1), fp1) != 0 &&
fgets(buffer2, sizeof(buffer2), fp2) != 0)
{
buffer1[strcspn(buffer1, "\n")] = '\0';
buffer2[strcspn(buffer2, "\n")] = '\0';
int len1 = max(0, 25 - strlen(buffer1));
fprintf(fp3, "%s%*.*s%s\n", buffer1, len1, len1, uscore, buffer2);
}
Putting this together, illustrating both techniques at once:
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
if (argc != 4)
{
fprintf(stderr, "Usage: %s infile-1 infile-2 outfile\n", argv[0]);
return 1;
}
FILE *fp1 = fopen(argv[1], "r");
FILE *fp2 = fopen(argv[2], "r");
FILE *fp3 = fopen(argv[3], "w");
if (fp1 == 0 || fp2 == 0 || fp3 == 0)
{
fprintf(stderr, "%s: failed to open one of the files %s, %s or %s\n",
argv[0], argv[1], argv[2], argv[3]);
return 1;
}
char uscore[256];
memset(uscore, '_', sizeof(uscore)-1);
uscore[sizeof(uscore)-1] = '\0';
char buffer1[1024];
char buffer2[1024];
while (fgets(buffer1, sizeof(buffer1), fp1) != 0 &&
fgets(buffer2, sizeof(buffer2), fp2) != 0)
{
buffer1[strcspn(buffer1, "\n")] = '\0';
buffer2[strcspn(buffer2, "\n")] = '\0';
fprintf(fp3, "%-25s %s\n", buffer1, buffer2);
int len1 = strlen(buffer1);
if (len1 < 28)
len1 = 28 - len1;
else
len1 = 0;
fprintf(fp3, "%s%*.*s%s\n", buffer1, len1, len1, uscore, buffer2);
}
fclose(fp1);
fclose(fp2);
fclose(fp3);
return 0;
}
Sample input file data.1:
California
Esoteric
Mismatch
Unexpected
Non-sequitur
Extra-long word list from file 1
Sample input file data.2:
Drought
Persecution
Preliminary
Adequate
Pusillanimous
Rather long word from file.2 too
Example output:
California Drought
California__________________Drought
Esoteric Persecution
Esoteric____________________Persecution
Mismatch Preliminary
Mismatch____________________Preliminary
Unexpected Adequate
Unexpected__________________Adequate
Non-sequitur Pusillanimous
Non-sequitur________________Pusillanimous
Extra-long word list from file 1 Rather long word from file.2 too
Extra-long word list from file 1Rather long word from file.2 too
There are endless tweaks you can make depending on a more precise definition of the format you want. Amongst other things, you can make sure there's a minimum of 3 underscores between the first and second words in the 'must have underscores' example. You could limit the lengths of the strings that are printed.
The code should check that it gets a newline within the first 1023 bytes; it doesn't.

If I use fgets to get the string from the files and it stops at the end of the line. Will it automatically know to start from the next line for the next string.
If the line can be completely stored in the buffer then Yes, it will (see below for further explanation).
However, there is not really something like lines in the data read from the file. It is more like a continuous stream of bytes. If you have a file in your editor that looks like:
a
b
c
the data that fgets see is like more like this byte stream:
a\nb\nc\n
The first call to fgets will read the a and the \n leaving the remaining input as
b\nc\n
The next call to fgets will read the b and the \n and thereby it works as if it starts from the "next line" but it really just continues from where the last call stopped.
Also notice what happens if the line is longer than your buffer. If the file is
abcd
efgh
and you do
fgets(buffer, 3, f)
then the first call to fgets will give ab\0 and the next call will continue reading cd\0.
In other words - if the line is too long to be completely stored in the buffer, fgets will not continue from "the next line". If you always want to continue from the next line, you must add code to read from the file until you read a \n
Also, how can I format the input to make two columns.
Well, your question doesn't include sufficient details to come up with exact code, e.g. what should be the spacing between columns, what to do if the input is larger than the spacing, etc.
In any case - see https://stackoverflow.com/a/45295262/4386427 (by Jonathan Leffler) which give you some good hints.

Related

Simple C program to read a file line by line

What I would like to do is read the whole first line of the file but then after the first line only read the following lines until whitespace is hit. My end goal is to ask the user what line they want to edit by adding/subtracting time to said line.
Sample File
My test file
00:19.1 123456
00:35.4 testing whitespace end
Desired Output
1: My test file
2: 00:19.1
3: 00:35.4
Code:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fptr1, *fptr2;
char filechar[40];
char c[50];
int line_number = 1;
int replace_line, temp = 1;
printf("Please enter a file name: ");
scanf("%s", &filechar);
if ((fptr1 = fopen(filechar, "r")) == NULL)
{
printf("Error locating desired file");
exit(1);
}
c = getc(fptr1);
while (c != EOF)
{
//printf("%d: %c",line_number, c);
printf("%s",c);
c = getc(fptr1);
//line_number++;
}
return 0;
}
In C you have character oriented input functions (e.g. getchar, fgetc), you have formatted input functions (e.g. the scanf family) and then you have line oriented input functions. (e.g. fgets and POSIX getline). When you are reading lines of data, line oriented input functions are the proper tool for the job. (taking user input with scanf has many pitfalls that new (and even not so new) C programmers fall into)
All line oriented functions read and include the '\n' in the buffer they fill. You can, and should, remove the newline from the resulting buffer if it will be used later on in your code. A simple
size_t n = strlen (buf);
if (buf[n-1] == '\n')
buf[--n] = 0;
is all you need to overwrite the trailing '\n' with a nul-terminating character. If you are just printing the line immediately and not storing it for later use, then it's not worth removing the newline (just account for it in your output format string).
Putting those pieces together, you can read each line, handle the first by simply outputting it, and for each remaining line, parse the time (presumable some elapsed time) from the full string read by fgets with sscanf and format the output as you specify. E.g.
#include <stdio.h>
#define MAXC 64 /* define constants, don't use magic number in code */
int main (int argc, char **argv) {
char buf[MAXC] = ""; /* buffer to hold each line -- size as reqd */
int line = 1;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, sizeof buf, fp)) { /* read each line in file */
char et[MAXC] = ""; /* buffer for holding time */
if (line == 1) /* if 1st line, just print */
printf ("%d : %s", line, buf); /* note: \n included by fgets */
else {
if (sscanf (buf, "%s", et) != 1) { /* parse up to first whitespace */
fprintf (stderr, "error: invalid conversion, line %d\n", line);
return 1;
}
printf ("%d : %s\n", line, et); /* output elapsed time only */
}
line++; /* increment line count */
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
note: you should protect against buffer overrun on parse by including a field-width specifier in the sscanf format string (e.g. sscanf (buf, "%63s", et), and that is one place that all you can do is include magic numbers in your code because there is no way to directly specify a variable width specifier for sscanf -- unless you creatively use sprintf to create the format string ahead of time -- but that's for another day..
Example Input File
$ cat dat/et.txt
My test file
00:19.1 123456
00:35.4 testing whitespace end
Example Use/Output
$ ./bin/et <dat/et.txt
1 : My test file
2 : 00:19.1
3 : 00:35.4
Look things over and let me know if you have any further questions.
(note: I take the filename as the first argument to the program, or read from stdin if no filename is given. C provides for command line arguments -- use them. It's fine to prompt for input if needed, otherwise, its far easier just to specify arguments on the command line :)
Please try if this C code can help you. It just reads the file line by line and replaces whitespace with the string termination character \0.
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
char* replace_char(char* str, char find, char replace){
char *current_pos = strchr(str,find);
while (current_pos){
*current_pos = replace;
current_pos = strchr(current_pos,find);
}
return str;
}
int main(void)
{
FILE * fp;
char * line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("/home/developer/CLionProjects/untitled4/download.out", "r");
if (fp == NULL)
exit(EXIT_FAILURE);
int count=0;
while ((read = getline(&line, &len, fp)) != -1) {
if (count==0) printf("%s", line);
else printf("%s\n", replace_char(line, ' ', '\0'));
count++;
}
fclose(fp);
if (line)
free(line);
exit(EXIT_SUCCESS);
}
File
My test file
00:19.1 123456
00:35.4 testing whitespace end
Output
My test file
00:19.1
00:35.4

Using fgets to read and print multiple lines from .txt file

FILE *fp = fopen("story.txt", "r");
if(fp == NULL){
printf("\nError opening file.\nExiting program.\n");
exit(1);
}
char text[100];
while(fgets(text, 100, fp) != NULL){
printf("%s", text);
}
printf("\n");
fclose(fp);
I'm trying to print the first 100 characters of a text file, including new lines, however when I use the code above it presents some weird behavior. First of all, it only prints the very last line of the text file, which itself is under 100 characters. Also, if I include two print statements in the while loop i.e.
while(fgets(text, 100, fp) != NULL){
printf("%s", text);
printf("%s", text);
}
It prints a lot more than 125 chars of the text file (somewhere in the thousands, it's a big text file), and the contents of said text is a bunch of seemingly random segments from the file in one constant stream, no new lines or anything.
So I guess my question is is there any way to use fgets so that it prints the text in the file, starting from the top, and includes new lines? I eventually have to use this to turn a text file into a character array, so that I can make a new, modified character array based off of that array, which will be printed to a new text file. So if there is a better way to approach that end goal, that would be appreciated.
EDIT: after some discussion in the comments I've realized that the text I am using is just one big block of text with carriage returns and no newlines. I guess at this point my main problem is how to turn this text file with carriage returns into a character array.
If the goal is to read a text file in lines of 100 characters, and to do away with the carriage returns, you can still use fgets() as long as you remember that fgets() will take characters up to and including the next newline, or until one less than the specified number of characters has been read.
The code below reads a line of text, up to BUFFER_SZ-1 characters, increases the memory allocation to hold a new line of text, and copies the line into the allocated space, removing carriage returns and any trailing newlines. Note that the address of the reallocated space is first stored in a temporary pointer. realloc() returns a null pointer in the event of an allocation error, and this step avoids a potential memory leak.
Since the lines are broken at BUFFER_SZ-1 characters, words may be split across lines, and you may want to develop additional logic to handle this. It would be more efficient to reallocate in larger chunks and less frequently, rather than once for every line, as is done here. Also note that it may be useful to open the file in binary mode to more closely parse line endings.
#include <stdio.h>
#include <stdlib.h>
#define BUFFER_SZ 100
int main(void)
{
FILE *fp = fopen("story.txt", "r");
if (fp == NULL) {
fprintf(stderr, "Unable to open file\n");
exit(EXIT_FAILURE);
}
char buffer[BUFFER_SZ];
char (*text)[sizeof buffer] = NULL;
char (*temp)[sizeof buffer] = NULL;
size_t numlines = 0;
while (fgets(buffer, sizeof buffer, fp) != NULL) {
++numlines;
/* Allocate space for next line */
temp = realloc(text, sizeof(*text) * numlines);
if (temp == NULL) {
fprintf(stderr, "Error in realloc()\n");
exit(EXIT_FAILURE);
}
text = temp;
/* Copy buffer to text, removing carriage returns and newlines */
char *c = buffer;
char *line = text[numlines-1];
while (*c != '\n' && *c != '\0') {
if (*c != '\r') {
*line++ = *c;
}
++c;
}
*c = '\0';
}
if (fclose(fp) != 0) {
fprintf(stderr, "Unable to close file\n");
}
for (size_t i = 0; i < numlines; i++) {
printf("%s\n", text[i]);
}
free(text);
return 0;
}
Another option would be to replace the carriage returns with newlines. This may be what OP had in mind. The above program is easily modified to accomplish this. Note that the \n is removed from the printf() statement that displays the results, since newlines are now included in the strings.
...
/* Copy buffer to text, converting carriage returns to newlines */
char *c = buffer;
char *line = text[numlines-1];
while (*c != '\n' && *c != '\0') {
if (*c == '\r') {
*line++ = '\n';
} else {
*line++ = *c;
}
++c;
}
*c = '\0';
}
if (fclose(fp) != 0) {
fprintf(stderr, "Unable to close file\n");
}
for (size_t i = 0; i < numlines; i++) {
printf("%s", text[i]);
}
...
It doesn't copy one line onto the end of another. It simply reuses the buffer you keep passing it. If you want multiple lines stored, copy them to another buffer, and concatenate them. (See: strcat)

How to read all the lines in a file and store them in a string

I am trying to read some commands which should be passed to my program from a file. The commands are on different lines, so I guess this means that they are separated by \n character. This is my command reading section:
FILE *fop;
char command[50];
fopen("mbr.op", "r");
while(!feof(fop))
{
fscanf(fop,"%s[^\n]", command);
printf("%s\n", command);
}
fclose(fop);
This prints some words that are in the file, but not all, and not in the expected order. How could I change this code to achieve the desired result?
You open your file incorrectly (it returns a FILE pointer which is associated with the opened file), fopen should be used as this -
fop=fopen("mbr.op", "r");
And while(!feof(fop)) should not be used .
You can write your loop as follows -
while(fscanf(fop,"%[^\n]%*c", command)==1)
{
printf("%s\n", command);
}
Note - Also check if file was opened successfully.
FILE *fop;
char command[50];
fop = fopen("mbr.op", "r"); /* get the file pointer */
if (fop == NULL) exit(1); /* check if the file successfully opened */
while(fscanf(fop,"%49[^\n]%*c", command) == 1) /* see notes after this code */
{
printf("%s\n", command);
}
fclose(fop);
Notes on the usage of fscanf():
They say using feof() is not good.
Yon won't need the s.
Specify the maximum length to read to avoid buffer overflow.
Added %*c to have it skip the newline character.
This code above won't work well if there are blank lines.
I think using fgets() is better to read lines.
FILE *fop;
char command[51]; /* added 1 byte to store the newline character */
fop = fopen("mbr.op", "r");
if (fop == NULL) exit(1);
while(fgets(command, sizeof(command), fop))
{
/* remove the newline character */
char* p;
for(p = command; *p != '\0' && *p != '\n'; p++);
*p = '\0';
/* print what is read */
printf("%s\n", command);
}
fclose(fop);

Wrap long lines from a file for output in console window in c program

Here is my code but it is not making any output to console window. I need to print from output file and also wrap the lines for a particular number of characters, say, 20 character per line:
#include <stdio.h>
#define SIZE 100
int
main (int argc, char *argv[])
{
FILE *fp = NULL;
char line[SIZE] = { 0 };
int i = 0;
for (i = 0; i < argc; i++)
{
printf ("The argc %d is %s\n", i, argv[i]);
}
fp = fopen (argv[1], "r");
if (fp == NULL)
{
printf ("Can't open input file\n");
}
else
{
while (!feof (fp))
{
if (fgets (line, sizeof line, fp))
{
printf ("%s", line);
}
}
}
if (fclose (fp) != 0)
{
printf ("Error closing file\n");
}
return 0;
}
Your code makes no attempt to do line wrapping.
What you need to do (broadly speaking) is as follows:
Maintain a temporary buffer
Read each line of the file
If the line read is \n (only), output the buffer followed by \n, and go to 2.
If the buffer is empty, THEN replace the buffer by the line that is read ELSE append a space to the buffer and append the line read to the buffer.
If the buffer has more than 20 characters (if you are line wrapping on twenty characters), find the 20th character, and count backwards until you find a white space. Output the buffer up to that character with a \n, and replace the buffer by the data in the buffer after the whitespace.
At the end, output any remaining characters in the buffer followed by \n.
Implementing this is left as an exercise for the reader (we can't do all your homework for you).
Please also see #JoachimPileborg's wise advice re reading 'Why is while ( !feof (file) ) always wrong?'

Counting the number of words using C from a text file

Hey I have been trying to count the number of words in my text file, to load up a bunch of words for a Hangman game, from C but I am hitting a brick wall. This piece of code I am using is supposed I am using this piece of code;
FILE *infile;
FILE *infile;
char buffer[MAXWORD];
int iwant, nwords;
iwant = rand() %nwords;
// Open the file
infile = fopen("words.txt", "r");
// If the file cannot be opened
if (infile ==NULL) {
printf("The file can not be opened!\n");
exit(1);
}
// The Word count
while (fscanf(infile, "%s", buffer) == 1) {
++nwords;
}
printf("There are %i words. \n", nwords);
fclose(infile);
}
If anyone has anyone has any suggestions on how to fix this I would be very grateful.
The text file has 1 word per line, with 850 words.
Applied the buffer suggestion, however the word count still came out at 1606419282.
The correction of putting
int nwords = 0;
Worked!! Thank you very much!
So the words are one entry per line?
while (fscanf(infile, "%s", &nwords) == 1); {
++nwords;
}
Doesn't do what you think it does. It reads a string in nwords, which isn't a string.
If you want to do it like this then you need to allocate a string ie char buffer[XXX] which is long enough to contain the longest lien in your data file and use:
while (fscanf(infile, "%s", buffer) == 1) {
++nwords;
}
The variable nwords is never initialized. You cannot assume it to start out as zero.
If it were, you'd get a crash ("divide by zero") on the next line, whose purpose eludes me:
iwant = rand() %nwords;
So, replace
int iwant, nwords;
iwant = rand() %nwords;
by
int nwords = 0;
After reading the first word and whitespace after it, your fscanf RETURNS to input buffer the whitespace. So, the next time you read EMPTY word.
Change proposed:
fscanf(infile, "%s ", &buffer) // notice the space!!! And & before buffer
It will throw off ALL whitespace till the next word. It should work.
P.S. Better not use [f]scanf :-)

Resources