Okay, so after reading both: How to read a specific line in a text file in C (integers) and What is the easiest way to count the newlines in an ASCII file? I figured that I could use the points mentioned in both to both efficiently and quickly read a single line from a file.
Here's the code I have:
char buf[BUFSIZ];
intmax_t lines = 2; // when set to zero, reads two extra lines.
FILE *fp = fopen(filename, "r");
while ((fscanf(fp, "%*[^\n]"), fscanf(fp, "%*c")) != EOF)
{
/* globals.lines_to_feed__queue is the line that we _do_ want to print,
that is we want to ignore all lines up to that point:
feeding them into "nothingness" */
if (lines == globals.lines_to_feed__queue)
{
fgets(buf, sizeof buf, fp);
}
++lines;
}
fprintf(stdout, "%s", buf);
fclose(fp);
Now the above code works wonderfully, and I'm extrememly pleased with myself for figuring out that you can fscanf a file up to a certain point, and then use fgets to read whatever data is at said point into a buffer, instead of having to fgets every single line and then fprintf the buf, when all I care about is the line that I'm printing: I don't want to be storing strings that I could care less about in a buffer that I'm only going to use once for a single line.
However, the only issue I've run into, as noted by the // when set to zero, reads two extra lines comment: when lines is initialized with a value of 0, and the line I want is like 200, the line I'll get will actually be line 202. Could someone please explain what I'm doing wrong here/why this is happening and whether my quick fix lines = 2; is fine or if it is insufficient (as in, is something really wrong going on here, and it just happens to work?)
There are two reasons why you have to set the lines to 2, and both can be derived from the special case where you want the first line.
On one hand, in the while loop the first thing you do is use fscanf to consume a line, then you check if the lines counter matches the line you want. The thing is that if the line you want is the one you just consumed you are out of luck. On the other hand you are basically moving through lines by finding the next \n and incrementing lines after you check if the current line is the one you're after.
These two factors combined cause the offset in the lines count, so the following is a version of the same function taking them into account. Additionally it also contains a break; statement once you get to the line you are looking for, so that the while loop stops looking further into the file.
void read_and_print_line(char * filename, int line) {
char buf[BUFFERSIZE];
int lines = 0;
FILE *fp = fopen(filename, "r");
do
{
if (++lines == line) {
fgets(buf, sizeof buf, fp);
break;
}
}while((fscanf(fp, "%*[^\n]"), fscanf(fp, "%*c")) != EOF);
if(lines == line)
printf("%s", buf);
fclose(fp);
}
Just as another way of looking at the problem… Assuming that your global specifies 1 when the first line is to be printed, 2 for the second, etc, then:
char buf[BUFSIZ];
FILE *fp = fopen(filename, "r");
if (fp == 0)
return; // Error exit — report error.
for (int lineno = 1; lineno < globals.lines_to_feed_queue; lineno++)
{
fscanf(fp, "%*[^\n]");
if (fscanf(fp, "%*c") == EOF)
break;
}
if (fgets(buf, sizeof(buf), fp) != 0)
fprintf(stdout, "%s", buf);
else
…requested line not present in file…
fclose(fp);
You could replace the break with fclose(fp); and return; if that's appropriate (but do make sure you close the file before exiting; otherwise, you leak resources).
If your line numbers are counted from 0, then change the lower limit of the for loop to 0.
First, about what is wrong here: this code is unable to read the very first line in the file (what happens if globals.lines_to_feed__queue is 0?). It would also miscount lines shall the file contain successive newlines.
Second, you must realize that there is no magic. Since you don't know at which offset the string in question lives, you have to patiently read file character by character, counting end-of-strings along the way. It doesn't matter if you delegate the reading/counting to fgets/fscanf, or fgetc each character for manual inspection - either way an uninteresting piece of file will make its way from the disk into the OS buffers, and then into the userspace for interpretation.
Your gut feeling is absolutely correct: the code is broken.
Related
I am trying to read a file of unknown size line by line including single or multiple newline characters.
for example if my sample.txt file looks like this
abc cd er dj
text
more text
zxc cnvx
I want my strings to look something like this
string1 = "abc cd er dj\n";
string2 = "text\n\n";
string3 = "more text\n\n\n";
string4 = "zxc convex";
I can't seem to come up with solution that works properly. I have tried following code to get the length of each line including newline characters but it gives me incorrect length
while((temp = fgetc(input)) != EOF) {
if (temp != '\n') {
length++;
}
else {
if (temp == '\n') {
while ((temp = fgetc(input)) == '\n') {
length++;
}
}
length = 0;
}
}
I was thinking, if I can get length of each line including newline character(s) and then I can malloc string of that length and then read that size of string using fread but I am not sure if that would work because I will have to move the file pointer to get the next string.
I also don't want to use buffer because I don't know the length of each line. Any sort of help will be appreciated.
If the lines are just short and there aren't many of them, you could use realloc to reallocate memory as needed. Or you can use smaller (or larger) chunks and reallocate. It's a little more wasteful but hopefully it should average out in the end.
If you want to use just one allocation, then find the start of the next non-empty line and save the file position (use ftell). Then get the difference between the current position and the previous start position and you know how much memory to allocate. For the reading yes you have to seek back and forth but if it's not to big all data will be in the buffer to it's just modifying some pointers. After reading then seek to the saved position and make it the next start position.
Then you could of course the possibility to memory-map the file. This will put the file contents into your memory map like it was all allocated. For a 64-bit system the address space is big enough so you should be able to map multi-gigabyte files. Then you don't need to seek or allocate memory, all you do is manipulate pointers instead of seeking. Reading is just a simply memory copying (but then since the file is "in" memory already you don't really need it, just save the pointers instead).
For a very simple example on fseek and ftell, that is somewhat related to your problem, I put together this little program for you. It doesn't really do anything special but it shows how to use the functions in a way that could be used for a prototype of the second method I discussed above.
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *file = fopen("some_text_file.txt", "r");
// The position after a successful open call is always zero
long start_of_line = 0;
int ch;
// Read characters until we reach the end of the file or there is an error
while ((ch = fgetc(file)) != EOF)
{
// Hit the *first* newline (which differs from your problem)
if (ch == '\n')
{
// Found the first newline, get the current position
// Note that the current position is the position *after* the newly read newline
long current_position = ftell(file);
// Allocate enough memory for the whole line, including newline
size_t bytes_in_line = current_position - start_of_line;
char *current_line = malloc(bytes_in_line + 1); // +1 for the string terminator
// Now seek back to the start of the line
fseek(file, start_of_line, SEEK_SET); // SEEK_SET means the offset is from the beginning of the file
// And read the line into the buffer we just allocated
fread(current_line, 1, bytes_in_line, file);
// Terminate the string
current_line[bytes_in_line] = '\0';
// At this point, if everything went well, the file position is
// back at current_position, because the fread call advanced the position
// This position is the start of the next line, so we use it
start_of_line = current_position;
// Then do something with the line...
printf("Read a line: %s", current_line);
// Finally free the memory we allocated
free(current_line);
}
// Continue loop reading character, to read the next line
}
// Did we hit end of the file, or an error?
if (feof(file))
{
// End of the file it is
// Now here's the tricky bit. Because files doesn't have to terminated
// with a newline, at this point we could actually have some data we
// haven't read. That means we have to do the whole thing above with
// the allocation, seeking and reading *again*
// This is a good reason to extract that code into its own function so
// you don't have to repeat it
// I will not repeat the code my self. Creating a function containing it
// and calling it is left as an exercise
}
fclose(file);
return 0;
}
Please note that for brevity's sake the program doesn't contain any error handling. It should also be noted that I haven't actually tried the program, not even tried to compile it. It's all written ad hoc for this answer.
Unless you are trying to write your own implementation, you can use the standard POSIX getline() function:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
char *line = NULL;
size_t len = 0;
ssize_t read;
fp = fopen("/etc/motd", "r");
if (fp == NULL)
exit(1);
while ((read = getline(&line, &len, fp)) != -1) {
printf("Retrieved line of length %zu :\n", read);
printf("%s", line);
}
if (ferror(fp)) {
/* handle error */
}
free(line);
fclose(fp);
return 0;
}
You get the wrong length. The reason is that before you enter the loop:
while ((temp = fgetc(input)) == '\n')
you forgot to increment length as it has just read a \n character. So those lines must become:
else {
length++; // add the \n just read
if (temp == '\n') { // this is a redundant check
while ((temp = fgetc(input)) == '\n') {
length++;
}
ungetc(temp, input);
}
EDIT
After having read the first non \n, you now have read the first character of the next line, so you must unget it:
ungetc(temp, input);
Alright, so I am working on linux and emacs for the first time using C, and coming from pretty beginner level java programming in eclipse, my new CS course is really daunting. SO much stuff has been thrown at me as if I already knew it...
Anyway, the current part of my assignment I am working on involves reading in text from a file (doing that by piping a text file as standard input into my program). Currently I had three functions, a main function where I read in the file / call other functions, a function that reverses the order of a single word (apple becomes elppa) with *char beg and *char end as parameters, and a function that reverses the order of every word in a line of words, calling the previous function and taking a char *str as a parameter.
I am having trouble reading in the files in my main method in a way that makes it easy to utilize these functions. Here's a snippet of how I am reading them in currently, but as you can see I haven't figured out a way to store a line of words and send that into my function (I need to reverse line by line, so I can't just add every single char to one long array)
enter code here``
char line[8192]
int location = 0;
FILE *in = stdin;
int buff = 0;
while (buff = fgetc(in))
{
fprintf(stderr, "Character is: %d '%c' \n", buff, (char)buff);
if (buff == EOF)
{
break;
}
line[location] = (char)buff;
location++;
}
line[location] = '\0';
If you want to get a whole line, you can do this:
char line[MAX_LINE_SIZE];
fscanf(in, "%[^\n]", line);
//do whatever you need with the line
fscanf(in, "%[\n]", line);
The first fscanf call reads a whole line and store in variable line.
But it doesn't skip that line! If you use it again, it will store the very same line.
The second fscanf call is for this: it stores '\n' in variable line and skips the line you read previously.
If you want, you can create another buffer to get the '\n' and skip the line:
char garbage[2];
fscanf(in, "%[\n]", garbage);
I hope this helps.
I need to save every line of text file in c in a variable.
Here's my code
int main()
{
char firstname[100];
char lastname[100];
char string_0[256];
char string[256] = "Vanilla Twilight";
char string2[256];
FILE *file;
file = fopen("record.txt","r");
while(fgets(string_0,256,file) != NULL)
{
fgets(string2, 256, file);
printf("%s\n", string2);
if(strcmp(string, string2)==0)
printf("A match has been found");
}
fclose(file);
return 0;
}
Some lines are stored in the variable and printed on the cmd but some are skipped.
What should I do? When I tried sscanf(), all lines were complete but only the first word of each line is printed. I also tried ffscanf() but isn't working too. In fgets(), words per line are complete, but as I've said, some lines are skipped (even the first line).
I'm just a beginner in programming, so I really need help. :(
You're skipping over the check every odd number of lines, as you have two successive fgets() calls and only one strcmp(). Reduce your code to
while(fgets(string_0,256,file) != NULL)
{
if( ! strcmp(string_0, string2) )
printf("A match has been found\n");
}
FWIW, fgets() reads and stores the trailing newline, which can cause problem is string comparison, you need to take care of that, too.
As a note, you should always check the return value of fopen() for success before using the returned pointer.
I am pretty new to C Programming and I'm having problems with my code. My program opens a .txt file and stores the data inside it in a string. After that, the data stored in a string is copied into another .txt file.
FILE * fp = fopen("New.txt", "a");
FILE * fp2 = fopen("File.txt", "r");
while(fgets(str, 100, fp2) != NULL) {
fputs(str, fp);
}
fclose(fp);
fclose(fp2);
Now my problem here is, I want to store the strings in a single line, but my program always goes to the next line. For example, I want the data to be:
One Two Three
What my code does:
One
Two
Three
Can you help me? Thanks in advance.
fgets reads a line, but retains the new-line character at the end. When you write the line, that new line is printed as well. You can remove the new-line character explicitly, for example:
int len = strlen(str);
if (len && str[len - 1] == '\n') str[len - 1] = '\0';
Alternatively, you could choose another approach: Read the file character-wise with getc and print out all characters except new lines with putc. (That approach works only if you don't want to do other things that require you to know the contents of each line.)
while (fgets(line, sizeof line, f)) {
int len = strlen(line);
if (length!=len-1){
fputs("max errorz\n", stderr);
exit(1);
}
break;
}
while ((c = getc(f)) != EOF) {
putchar(c);
}
I have a problem displaying all the contents in a text file. At the moment, the first while loop runs successfully, it checks the length of a file (characters), and if it matches the argument. I have finished this one, but for my 2nd loop after checking if the input matches the length of file , now i want to fully display the text file. BUT, it only displays 2/3 lines of the file. the first line looks like it was "taken" by the first loop.
Anyone know why this is?
I have tried putting the 2nd loop first, and it works (displays the full contents of the file).
When the first loop reads from the file, it advances the read offset. After, all you'd expect this to work, right?
fgets(line1, sizeof line1, f);
fgets(line2, sizeof line2, f);
/* line1 and line2 now hold the first two lines */
So calling getc after fgets behaves the same way - it reads from where you left off.
Just move back to the beginning again between loops:
fseek(stream, 0L, SEEK_SET);
or as pzaenger suggests, the equivalent
rewind(f);
When you call fgets on FILE* f, f's position in the file moves forward so subsequent calls will not read the same data. When you go to use getc f points to where the first loop left it, in the middle of the file. You want to put f back to the beginning of the file. Try rewind(f) as #pzaenger suggests.
After fetching some data from the file, file pointer moves some position forward from the starting position. if you want to set back the file pointer to starting position of the file you need to use rewind().
while (fgets(line, sizeof line, f)) {
int len = strlen(line);
if (length!=len-1){
fputs("max errorz\n", stderr);
exit(1);
}
break;
}
rewind(f); // fix
while ((c = getc(f)) != EOF) {
putchar(c);
}
1 - You are not necessary reading the whole lenght of your file. Read the documentation:
http://www.cplusplus.com/reference/cstdio/fgets/
2 - Why putting a break into a while command without any if around? This means that your break is always hit and so it means that you don't need the while. See the same link as before.
3 - You print only part of the file because you've already read part of it using the while, you have to rewind your pointer as suggested by others. Again see
http://www.cplusplus.com/reference/cstdio/rewind/?kw=rewind