How can i read a specific char inside a file? - c

This would be enough to read the first character 'a' inside fp
file.txt
abcdef
// readchar.c
FILE *fp = fopen("file.txt", "r");
int c = fgetc(fp);
but how can i read (e.g.) the 3rd character?

A text file is a kind of sequential file. To read a specific item, you must read everything preceding it in the file.
Operating systems let you read a file anywhere by moving a so called file pointer. The position in the file where read take place can be changed by seeking into the file. There are several ways to handle file I/O. The one you found invoke fopen to open the file and get a file pointer, fgetcto read the next character where the file pointer points and advance it by one character. You have fgetsto read a complete line. fseek to more the file pointer somewhere else. fcloseto close the file. And other similar function.
Back to the text file. Assuming we have a file containing: two lines containing:
Hello world,
Programming rocks!
If you want to read the 5th character of the first line, it is easy: just position the file pointer with fseek to the 5th position in the file (the first character is at position zero). Then read it with fgetc.
Now if you need to read the 5th character of the second line, whatever the first line is, you cannot use fseek because you don't know the length of the first line without reading the line first.
To read the Nth character on the Mth line, you must read M lines, throwing away data except the last one (You simply read all line in a for/loop into the same buffer). And then access the Nth character in the buffer where you just read the last line. Make that buffer an array of char and you have direct access to the Nth character.

Related

How does fgets() keep track of what line it's on?

This code correctly reads a file line-by-line, stores each line in line[] and prints it.
int beargit_add(const char* filename) {
FILE* findex = fopen(".beargit/.index", "r");
char line[FILENAME_SIZE];
while(fgets(line, sizeof(line), findex)) {
strtok(line, "\n");
fprintf(stdout, "%s\n", line);
}
fclose(findex);
return 0;
}
However, I am baffled as to why using fgets() in the while loop actually reads the file line-by-line. I am brand new to C after having learned Python and Java.
Since each call to fgets() is independent, where is C remembering which line it is currently on each time you call it? I thought it might have to do with changing the value FILE* index points to, but you are passing the pointer into fgets() by value so it could not be modified.
Any help in understanding the magic of C is greatly appreciated!
It's not fgets keep track, findex does this for you
findex is a FILE type, which includes infomation about a open file such as file descriptor, file offset
FILE is a encapsulation of I/O in OS.
more about: FILE
Object type that identifies a stream and contains the information needed to control it, including a pointer to its buffer, its position indicator and all its state indicators.
and the offset is to keep track of the file, each time you read the file, it starts from the offset. All these work are done by FILE, it do for you, and for fgets
more info about offset offset wiki
I thought it might have to do with changing the value FILE* index points to
it's not the value of the pointer itself that is changed. The pointer points to a structure (of type FILE) which contains information about the stream associated with that structure/pointer. Presumably, one of the members of that structure contains a "cursor" that points to the next byte to be read.
Or maybe it's just got a file descriptor int (like on many Unices) and I/O functions just call out to the kernel in order to obtain information about the file descriptor, including the current position.

fgets not reading the beginning of a line

I am having trouble reading a few lines of text from a file using fgets. The file is some basic user data that is written to a file within the bundle the first time the plugin is launched. Any subsequent launch of the plugin should result in the user data being read and cross referenced to check the users authenticity.
The data is always 3 lines long and is written with frwite exactly as it should be and is opened with fopen.
My original theory was to just call fgets 3 times reading each line into it's own char array which is part of a data struct. The problem is the first line is read correctly, the second line is read as though the position indicator starts on the next line but offset by the number of characters read from line 1. The third line is then not read at all.
fgets is not returning any errors and is behaving as though it has read the data it should have so i'm obviously missing something.
Anyway here's a portion of my code hopefully someone can some shed some light on my mistakes!
int length;
fgets(var.n, 128, regFile);
length = strlen(var.n);
var.n[length-1] = NULL;
fgets(var.em, 128, regFile);
length = strlen(var.em);
var.em[length-1] = NULL;
fgets(var.k, 128, regFile);
length = strlen(var.k);
var.k[length-1] = NULL;
fclose(regFile);
Setting the last character in each string to NULL is just to remove the /n
This sequence of code outputs the whole of line 1, the second half of line 2 and none of line 3.
Thanks to #alvits for the answer to this one:
fwrite() is not compatible with fgets(). Files created using fwrite() should use fread() to read them ?>back in. Both fwrite() and fread() operates on binary streams unless explicitly converted to and from >strings. fgets() is compatible with fputs(), both operates on strings.
I used fputs() to write my data instead and it read back in perfectly.
In POSIX systems, including Linux, there is no differentiation between binary and text files. When opening a file stream, the b flag is ignored. This is described in fopen().
You might ask "how would you differentiate text from binary files?". The contents differentiate them. How the contents are written makes them a binary or text file.
Look at the signature size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream). You'll notice that it writes the contents of *ptr with size describing the size of each members, nmemb. The written stream is not converted to string. If you were to write 97 it will write the binary 97 which in ascii is A. Binary data does not obey string terminations. Presence of \n and \0 in data is literally written as is.
Now look at the signature int fputs(const char *s, FILE *stream). It writes the string content of *s. If you were to write 97, it will have to be a string "97" which is not A. String termination is obeyed. \n is automatically converted to the O/S supported newline (CRLF or LF).
You can coerce fwrite() to behave like fputs() but not the other way around. For example, if you declare ptr as a pointer to string and calculate the size exactly as the length of the content excluding string terminator, you'll be able to write it out as text instead of binary. You will also need to handle \0 and \n and convert them to O/S supported newline. Writing the entire string buffer will write everything including and past the string terminators.

Reading line by line in C

Currently to read a file line by line in C I am using:
char buffer[1024];
while(fgets(buffer, sizeof(buffer), file) != NULL) {
//do something with each line that is now stored in buffer
}
However there is no guarantee in the file that the line will be shorter than 1024. What will happen if a line is longer than 1024? Will the rest of the line be read in the next iteration of the while loop?
And how can I read line by line without a maximum length?
Yes, the rest of the line will be read in the next iteration.
You can detect whether or not you read a whole line by inspecting the last character of the string (i.e. the one before the null terminator) to see if it is '\n' or not -- fgets passes '\n' through to you.
There is no Standard C function which will read a line whilst dynamically allocating enough memory for it, however there is a POSIX function getline() which does that. You could write your own that uses fgets or otherwise to do the reading, in a loop with realloc, of course.
From the standards §7.19.7.2,
char *fgets(char * restrict s, int n, FILE * restrict stream);
The fgets function reads at most one less than the number of
characters specified by n from the stream pointed to by stream into the
array pointed to by s. No additional characters are read after a
new-line character (which is retained) or after end-of-file. A null
character is written immediately after the last character read into
the array.
From MSDN,
fgets reads characters from the current stream position to and including the first newline character, to the end of the stream, or until the number of characters read is equal to n – 1, whichever comes first. The newline character, if read, is included in the string.
So, yes fgets will read the rest of the line in next iteration if the it doesn't encounters the newline character within sizeof(buffer)-1 range.
If you want to read the whole line in one shot, then it is better to go with malloc and, if needed, reallocing the memory as per your needs.

can't read string from file in c

I'm trying to read a text file line by line and printing the first 17 characters.
FILE *devices;
devices = NULL;
devices = fopen("devices.txt", "r");
char deviceaddr[17];
char addr[17];
char line[1024];
while (fgets(line,1024,devices) != NULL)
{
fscanf(devices,"%s", deviceaddr);
printf("%s\n", deviceaddr);
}
fclose(devices);
the output should be 00:07:80:4C:0E:EEfor the first line but it gives 6.
The while loop is reading a line of text, then the fscanf will read the next set of text (and possibly overrun that buffer incidentally). It seems as if you should just be printing the desired data inside the loop from the buffer line.
For example, suppose there are three lines of text.
00:07:80:4C:0E:EE --> ends up line buffer line
second --> ends up in deviceaddr
third line --> ends up in line (unless the fscanf did not consume newline)
There's no way the output can be "00:07:80:4c:0E:EE", since
that would result in undefined behavior, due to buffer
overrun—the string requires 18 bytes, but you only provide
17. You should never us "%s" in an fscanf without
specifying the length.
And you're calling fscanf on devices after having read
a line from it; if you're reading line by line, you want to use
sscanf on the line you've read.

Read file in array line by line

Can you set any index of array as starting index i.e where to read from file? I was afraid if the buffer might get corrupted in the process.
#include <stdio.h>
int main()
{
FILE *f = fopen("C:\\dummy.txt", "rt");
char lines[30]; //large enough array depending on file size
fpos_t index = 0;
while(fgets(&lines[index], 10, f)) //line limit is 10 characters
{
fgetpos (f, &index );
}
fclose(f);
}
You can, but since your code is trying to read the full contents of the file, you can do that much more directly with fread:
char lines[30];
// Will read as much of the file as can fit into lines:
fread(lines, sizeof(*lines), sizeof(lines) / sizeof(*lines), f);
That said, if you really wanted to read line by line and do it safely, you should change your fgets line to:
// As long as index < sizeof(lines), guaranteed not to overflow buffer
fgets(&lines[index], sizeof(lines) - index, f);
Not like this no. There is a function called fseek that will take you to a different location in the file.
Your code will read the file into a different part of the buffer (rather than reading a different part of the file).
lines[index] is the index'th character of the array lines. Its address is not the index'th line.
If you want to skip to a particular line, say 5, then in order to read the 5th line, read 4 lines and do nothing with them, them read the next line and do something with it.
If you need to skip to a particular BYTE within a file, then what you want to use is fseek().
Also: be careful that the number of bytes that you tell fgets to read for you (10) is the same as the size of the array you are putting the line into (30) - so this is not the case right now.
If you need to read a part of a line starting from a certain character within that line, you still need to read the whole line, then just choose to use a chunk of it starting someplace other than the beginning.
Both of these examples are like requesting a part of a document from a website or a library - they're not going to tear out a page for you, you get the whole document, and you have to flip to what you want.

Resources