How to peek ahead when processing an input file (processing 2 lines) - c

When going through a text file line by line I would like to be able to look ahead to the next line and inspect it while working on the current line. I am working in the C language. I believe that fseek() or other similar functions would help me with this but I'm unsure and don't know how to use them. I would want to achieve something to the effect of:
fp = fopen("test-seeking.txt", "r");
while((fgets(line, BUFMAX, fp))) {
// Peek over to next line
nextline = ...;
printf("Current line starts with: %-3.3s / Next line starts with %-3.3s\n",
line, nextline);
}
I appreciate any help.

Indeed you can use fseek and try something like this:
fp = fopen("test-seeking.txt", "r");
while ((fgets(line, BUFMAX, fp))) {
// Get the next line
fgets(nextline, BUFMAX, fp);
// Get the length of nextline
int nextline_len = strlen(nextline);
// Move the file index back to the previous line
fseek(fp, -nextline_len, SEEK_CUR); // Notice the - before nextline_len!
printf("Current line starts with: %-3.3s / Next line starts with %-3.3s\n", line, nextline);
}
Another way is to use fgetpos and fsetpos, like this:
fp = fopen("test-seeking.txt", "r");
while ((fgets(line, BUFMAX, fp))) {
// pos contains the information needed from
// the stream's position indicator to restore
// the stream to its current position.
fpos_t pos;
// Get the current position
fgetpos(fp, &pos);
// Get the next line
fgets(nextline, BUFMAX, fp);
// Restore the position
fsetpos(fp, &pos);
printf("Current line starts with: %-3.3s / Next line starts with %-3.3s\n", line, nextline);
}

The following code is inspired by #Jean-François Fabre comment. It will use a 2D character array lineBuffer which is used to hold the lines. The first read line is written to index 0 lineBuffer[0] and the second line to lineBuffer[1]. After that the writings are alternating between index 0 and 1 with the help of a toggle variable lineSel. As last step the curLine pointer will be set to nextLine.
As result you can use curLine and nextLine within the loop.
If you have a file that consists of:
line 1
line 2
line 3
...
You will work with:
curLine = "line 1\n"
nextLine = "line 2\n"
curLine = "line 2\n"
nextLine = "line 3\n"
...
See live example with stdin instead of a file on ideone.
Code:
#include <stdio.h>
#define BUFMAX 256
#define CURLINE 0
#define NEXTLINE 1
#define TOGGLELINE (CURLINE ^ NEXTLINE)
int main ()
{
FILE* fp = fopen("test-seeking.txt", "r");
char lineBuffer[2][BUFMAX];
char* curLine;
char* nextLine;
int lineSel;
if (fp != NULL)
{
if ((curLine = fgets(lineBuffer[CURLINE], BUFMAX, fp)))
{
for (lineSel = NEXTLINE;
(nextLine = fgets(lineBuffer[lineSel], BUFMAX, fp));
lineSel ^= TOGGLELINE)
{
printf("Current line: \"%s\" / Next line \"%s\"\n",
curLine, nextLine);
curLine = nextLine;
}
}
fclose(fp);
}
return 0;
}

Related

Reading a text file and ignoring commented lines in C

I'm currently working on making a tiny computer in C for a programming assignment and I'm stuck on one part.
I'm stuck on how to correctly ignore comments in a text file that I'm reading in. (Example of file input below).
Input File
5 5 //IN 5
6 7 //OUT 7
3 0 //STORE 0
5 5 //IN 5
6 7 //OUT 7
3 1 //STORE 1
1 0 //LOAD 0
4 1 //SUB 1
3 0 //STORE 0
6 7 //OUT 7
1 1 //LOAD 1
6 7 //OUT 7
7 0 //END
The first input on each new line is an operation, the 2nd input being an address. I am planning to have a switch statement for each op and then calling the appropriate function. This is my current layout for reading in the file:
//file handling
int c;
FILE * file;
file = fopen(name, "r");
if (file){
printf("Run.\n");
while ((c = getc(file)) != EOF){
op = c;
switch (op){
case 5:
print("Inside case 5\n");
}
}
fclose(file);
}
How can I ignore the // on each line and skip to the next line in the file?
Call fgets to get a full line:
fgets(buffer, 100, file);
and then extract the two numbers from the line:
sscanf(buffer, "%d%d", &instruction, &address);
how to correctly ignore comments in a text file that I'm reading in
How can I ignore the // on each line and skip to the next line in the file?
Read the line using fgets()
char buf[80];
if (fgets(buf, sizeof buf, file)) {
Look for the // with strstr() #Steve Summit and lop off the string at that point.
char *slash_slash = strstr(buf, "//");
if (slash_slash) {
*slash_slash = '\0';
}
Continue processing the line as desired.
...
}
By using fgets and strtok you can read line by line and split the string acording to the // delimiter. Here's an example (it is not fully checked, but it's the main idea):
FILE *f = fopen("file.txt", "r");
if (f== NULL)
{
printf("opening file failed\n");
return 1;
}
char buf[256] = { 0 };
while (fgets(buf, 256, f))
{
char *s = strtok(buf, "//");
if (s == NULL)
{
printf("s == NULL\n");
}
else
{
printf("%s\n", s);
}
memset(buf, 0, 256);
}
fclose(f);
EDIT: I just realized that this is not exactly what you were looking for. However, you can still use it in order to first ignore the comment, and then break the given string into operation and address, or whatever that is...

Inserting text into a txt file

I'm trying to accomplish the following:
I have a text file with the last printable character "]" in a separate line.
This line is not necessary to be the last line of the file. Some blank lines (line returns) can be there.
The purpose of the project is to insert new text before the line with "]".
The way I try to implement this is to search the file from the end of the file to find the line number with the character "]" (char_line).
Copy line by line from the original file rules1.txt to rules2.txt up to the line char_line. Next step is to append the new text with "]" at the end.
After that, I can delete the original file and rename the new file from rules2.txt to rules1.txt.
The problem I have is that the program finds the line with the character "]" and I can do a printf and see the correct line number.
I am assigning char_line = "%d".
When I'm using if(i < char_line) the file is copied all the way to EOF.
If I assign a numerical value, char_line = 23, the file is copied up to line 22, which is what I want.
This is the part of the code which should find line number for "]", copy line by line rules1.txt to rules2.txt up the line with "]".
#include <stdio.h>
int main(void) {
int end, loop, line;
char str[64];
FILE *file;
FILE *write;
int char_line;
int ret;
file = fopen("rules1.txt", "r");
if (file == NULL) {
printf("Failed to open file\n");
return -1;
}
int ch, line_num = 0;
do {
ch = fgetc(file);
if(ch == '\n')
line_num++;
} while (ch != EOF);
// last line doesn't end with a new line!
// but there has to be a line at least before the last line
if(ch != '\n' && line_num != 0)
line_num++;
fclose(file);
line = line_num-1;
start:
file = fopen("rules1.txt", "r");
if (file == NULL) {
printf("Failed to open file\n");
return -1;
}
for(end = loop = 0;loop<line;++loop){
if(0==fgets(str, sizeof(str), file)){//include '\n'
end = 1;//can't input (EOF)
break;
}
}
if(!end)
if (strncmp ("]", str, 1) == 0){
char_line ="%d";
goto next;
} else if(line >1){
line == line--;//WTF?
fclose(file);
goto start;
} else //What to do here?
next:
file = fopen("rules1.txt", "r");
write = fopen("rules2.txt", "w");
char linec [64]; /* line size */
int i = 0;
while (fgets(linec, sizeof(linec), file)) { /* read line from file */
i++;
if(i < char_line) {
fprintf (write , linec); /* write line to file */
}
}
fclose(file);
fclose(write);
return(0);
}
You have declared int char_line; and later you code
char_line ="%d";
This is nonsense. Since a literal string is some char[] array (better think of it as some constant array), decayed to a pointer, and assigning a pointer to an int does not make sense at all. On many machines (x86-64 notably), a pointer has 64 bits but an int has only 32 bits.
Please, compile your code with all warnings and debug info, so gcc -Wall -Wextra -g with GCC, improve your code to get no warnings, then use the debugger gdb.
Take several days to read some good books on C programming. Be aware and work hard to avoid undefined behavior. Use the debugger to run your program step by step and query its state to understand what is happening.
Read the documentation of every standard function you are using such as fgets.
You probably want a loop that reads every line, and copies sometimes that line to another file.

fscanf doesn't read the first char of first word (in c)

I'm using fscanf function in a c code to read a file contains 1 line of words separated by white spaces, but for example if the first word is 1234, then when I print it the output is 234, however the other words in the file are read correctly, any ideas?
FILE* file = fopen(path, "r");
char arr = getc(file);
char temp[20];
while(fscanf(file,"%s",temp)!= EOF && i<= column)
{
printf("word %d: %s\n",i, temp);
}
char arr = getc(file);
Probably above line is causing to loose the first char.
Here is the posted code, with my comments
When asking a question about a run time problem,
post code that cleanly compiles, and demonstrates the problem
FILE* file = fopen(path, "r");
// missing check of `file` to assure the fopen() was successful
char arr = getc(file);
// this consumed the first byte of the file, (answers your question)
char temp[20];
while(fscanf(file,"%s",temp)!= EOF && i<= column)
// missing length modifier. format should be: "%19s"
// 19 because fscanf() automatically appends a NUL byte to the input
// 19 because otherwise the input buffer could be overrun,
// resulting in undefined behaviour and possible seg fault event
// should be checking (also) for returned value == 1
// this will fail as soon as an `white space` is encountered
// as the following call to fscanf() will not read/consume the white space
// suggest a leading space in the format string to consume white space
{
printf("word %d: %s\n",i, temp);
// the variable 'i' is neither declared nor modified
// within the scope of the posted code
}
char arr = getc(file);
reads the first character from the file stream and iterates the file stream file
you can use rewind(file) after char arr = getc(file) to reset your file stream to the beginning.
Other example:
#include <stdio.h>
int main(void)
{
FILE *f;
FILE *r;
char str[100];
size_t buf;
memset(str, 0, sizeof(str));
r = fopen("in.txt", "r");
f = fopen("out.txt", "w+b");
fscanf(r, "%s", str);
rewind(r); // without this, the first char won't be written
buf = fread(str, sizeof(str), 1, r);
fwrite(str, sizeof(str), 1, f);
fclose(r);
fclose(f);
return (0);
}

Overwrite a line in a file

I'm trying to overwrite a line in a file that contains only unsigned long numbers.
The contents of the file look like this:
1
2
3
4
5
I want to replace a specific number with the number 0. The code I wrote looks like this:
FILE *f = fopen("timestamps", "r+");
unsigned long times = 0;
int pos = 0;
while(fscanf(f, "%lu\n", &times) != EOF)
{
if(times == 3)
{
fseek(f, pos, SEEK_SET);
fprintf(f, "%lu\n", 0);
}
times = 0;
pos = ftell(f);
}
fclose(f);
f = fopen("timestamps", "r");
times = 0;
while(fscanf(f, "%lu\n", &times) != EOF)
{
printf("%lu\n", times);
times = 0;
}
fclose(f);
The output of the program looks like this:
1
2
10
5
Interestingly, if I cat the file, it looks like this:
1
2
10
5
Am I making a mistake in my ftell? Also, why didn't the printf show the missing line that the cat showed?
I could reproduce and fix.
The present problem is that when you open a file in r+ you must call fseek at each time you switch from reading to writing and from writing to reading.
Here, you correctly call fseek before writing the 0, but not after that write and the following read. The file pointer is not correctly positionned and you get undefined behaviour.
Fix is trivial, simply replace :
if(times == 3)
{
fseek(f, pos, SEEK_SET);
fprintf(f, "%lu\n", 0);
}
with
if(times == 3)
{
fseek(f, pos, SEEK_SET);
fprintf(f, "%lu\n", 0);
pos = ftell(f);
fseek(f, pos, SEEK_SET);
}
But BEWARE : it works here because you replace a line by a line of exactly same length. If you tried to replace a line containing 1000 with a line containing 0 you would get an extra line containing 0 on a windows system where end of line is \r\n and 00 on an unix like system with end of line \n.
Because here is what would happen (Windows case) :
Before rewrite :
... 1 0 0 0 \r \n ...
After :
... 0 \r \n 0 \r \n ...
because a sequential file is ... a sequential serie of byte !
The most comfortable way (in my opinion) to change text files is to create a new temporary file, copy the old one, line by line, with whatever changes you need, delete the old (or rename) and rename the temporary file.
Something like
char line[1000];
FILE *original, *temporar;
original = fopen("original", "r");
temporar = fopen("temporar", "w");
while (fgets(line, sizeof line, original)) {
processline(line);
fprintf(temporar, "%s", line);
}
fclose(temporar);
fclose(original);
unlink("original"); // or rename("original", "original.bak");
rename("temporar", "original");
Of course you need to validate all calls in real code.

reading just one line from file in c code

My program reads the specific line from the file properly, however it reads the whole file form the line I specify onward. I am trying to print just one line at a time. How can I make it just read the one line?
The code:
int main()
{
int lineNumber = 5;
static const char filename[] = "Text.txt";
FILE *file = fopen(filename, "r");
int count = 0;
if ( file != NULL )
{
char line[256]; /* or other suitable maximum line size */
while (fgets(line, sizeof line, file) != NULL) /* read a line */
{
if (count == lineNumber)
{
printf("%s", line);
//in case of a return first close the file with "fclose(file);"
}
else
{
count++;
}
}
fclose(file);
}
}
After you've found the desired line, just use a break to exit the loop:
if (count == lineNumber)
{
printf("%s", line);
break;
}
if (count == lineNumber)
{
printf("%s", line);
//in case of a return first close the file with "fclose(file);"
count++;
}
increment count when you get the line you specify,otherwise count will not proceed to indicate next line. That is why your code printed all lines once you got the line you specified. Because the line number count wont increase once it becomes equal to linenumber. so add count++.
You can even break the loop since you don't require rest of the lines to be read after you get the specified line.

Resources