C - read specific line from file - c

I'm trying to read a specific line from a file and I can get the line number but I'm not sure how to go about doing it, this is what I have so far:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char **argv)
{
FILE *file;
file = fopen("temp.txt","r");
char tmp[256]={0x0};
char *tmpline;
int numline = 1;
while(file != NULL && fgets(tmp, sizeof(tmp),file) !=NULL)
{
tmpline = strstr(tmp,"status:green");
if(tmpline) {
printf("%d - %s", numline, tmpline);
}
numline++;
}
if (file != NULL) fclose(file);
return 0;
}
The test file looks like:
s1.server.com
127.0.0.1
status:green
s2.server.com
127.0.0.1
status:red
s3.server.com
127.0.0.1
status:red
s4.server.com
127.0.0.1
status:green
The output that I have is:
3 - status:green
15 - status:green
But what I really want it to show is:
s1.server.com
s4.server.com
I want it to search for "status:green" then go back a few lines to show which server it belongs to

It sounds as if you need to do one of two things.
Simpler option: keep a little circular buffer of lines. Read into line 0, line 1, line 2, ..., line n-1, line 0, line 1, etc. Then, when you see the text you want, look in entry (current_index - 2) mod buffer_size. (Here it sounds as if a buffer size of 3 will suffice.)
More sophisticated option: actually parse the input so that for each block you work out the server name, its IP address and its status, and then display the information you need using that.
The "more sophisticated option" would be substantially more work, but more robust if the syntax of your input ever changes (e.g., with optional extra lines with more information about the server -- multiple IP addresses or multiple names, perhaps).
There are some other things you could do that I think are worse. (1) Call ftell on each line and put the results of that in a circular buffer, and then use fseek when you see "status:green". (2) Read the whole file using code like you currently have, building up a list of "good" servers' line numbers. Then go through the file again and report the good ones. I think these are both uglier and less efficient than the approaches I listed above. There's one possible advantage: you can adapt them to count in "stanzas" separated by blank lines, without needing to parse things properly. That would get you part of the flexibility of the "more sophisticated" approach I mentioned, without needing a proper parser.
And here's a hybrid possibility: don't use a circular buffer, but one whose size can increase if need be. Start at the first entry in the buffer each time you see a blank line. Let the buffer grow if there are "long" stanzas. Then when you see "status:green", do whatever processing you need to on the (presumably complete) stanza now held in your buffer.
None of the above is necessary, of course, if you're sure that the file format will never change.

If the test file (and production file) is well-formed then you can do something like the following (error checking left out for brevity!):
typedef struct _SERVERSTATUS
{
char* name;
char* ip;
char* status;
} SERVERSTATUS;
SERVERSTATUS ss;
ss.name = calloc(256);
ss.ip = calloc(256);
ss.status = calloc(256);
while (!feof(file))
{
fgets(ss.name, file);
fgets(ss.ip, file);
fgets(ss.status, file);
if (!strcmp(ss.status, "status:green"))
printf("%s\n", ss.name);
}
free(ss.name);
free(ss.ip);
free(ss.status);
Edit: You also have to handle the whitespace between the file entries! That's, um, left as an exercise for the questioner

Read the first and third lines in each group. Search for status:green and, if found, print the server name.

Related

C - Append on 2nd line of file

Im trying to append on the 2nd line of my txt file. The format I want for my txt file is the following:
1 2 3 4 5
1.2 3.5 6.4 1.2 6.5
Basicly, I want to append on the first two lines of the file.
void write_stats(int tries, int num_letters, int tries_sucess)
FILE *stats;
stats = fopen("C:\\Users\\rjmal\\Documents\\CLION PROJECTS\\JogoDaForca\\stats.txt", "a");
if(stats == NULL)
{
printf("can't open file\n");
exit(0);
}
fprintf(stats," %d\n",tries);
fprintf(stats," %f",(float)tries_sucess/num_letters);
fclose(stats);
}
How do I make that without making a new line on the file everytime I run my program?
With the code I made, I get something like:
1
3 1.5
1 2.3
Due to the way files and lines are considered in computers, you can not vertically print as you desire. Instead, what you can do is storing all these numbers (i.e. tries AND (float)tries_sucess/num_letters) in two arrays and printing the contents of each array on the same line in that order. In effect, this would be buffering your content before printing and formatting it as you desire.
In that way, you can print all the data into two lines, which now correspond to an array, each.
Alternatively, you can create two char arrays and consider them as actual string buffers and use sprintf to record into them. Then, once you're done, you can print each char array through a single fprintf call.
Assuming you created two sufficiently long char arrays, below is a sample code for new write_stats. It now only serves to record the stats into two buffers.
void write_stats(int tries, int num_letters, int tries_sucess, char* buffer1, char* buffer2)
{
sprintf(buffer1 + strlen(buffer1)," %d\n",tries);
sprintf(buffer2 + strlen(buffer2)," %f",(float)tries_sucess/num_letters);
}
Note that you need to initiate the buffers with 0 to be able to easily make use of strlen function as I did. Also, you will eventually (i.e. when you are done calling write_stats ) need to call fprintf, in a block that buffer1 and buffer2 are defined in, as follows.
FILE *stats;
stats = fopen("C:\\Users\\rjmal\\Documents\\CLION PROJECTS\\JogoDaForca\\stats.txt", "a");
if(stats == NULL)
{
printf("can't open file\n");
exit(0);
}
fprintf(stats,"%s\n%s", buffer1, buffer2);
fclose(stats);
Since there are quite a few details to keep in mind, I think it is best you see this idea at work. See here for a working implementation, with some comments to help elaborate some details. As you may observe, the output given there is horizontal and is in 2 lines, as you described and as given below.
1 3 13 55 233
2.000000 1.600000 1.619048 1.617978 1.618037

File IO does not appear to be reading correctly

Disclaimer: this is for an assignment. I am not asking for explicit code. Rather, I only ask for enough help that I may understand my problem and correct it myself.
I am attempting to recreate the Unix ar utility as per a homework assignment. The majority of this assignment deals with file IO in C, and other parts deal with system calls, etc..
In this instance, I intend to create a simple listing of all the files within the archive. I have not gotten far, as you may notice. The plan is relatively simple: read each file header from an archive file and print only the value held in ar_hdr.ar_name. The rest of the fields will be skipped over via fseek(), including the file data, until another file is reached, at which point the process begins again. If EOF is reached, the function simply terminates.
I have little experience with file IO, so I am already at a disadvantage with this assignment. I have done my best to research proper ways of achieving my goals, and I believe I have implemented them to the best of my ability. That said, there appears to be something wrong with my implementation. The data from the archive file does not seem to be read, or at least stored as a variable. Here's my code:
struct ar_hdr
{
char ar_name[16]; /* name */
char ar_date[12]; /* modification time */
char ar_uid[6]; /* user id */
char ar_gid[6]; /* group id */
char ar_mode[8]; /* octal file permissions */
char ar_size[10]; /* size in bytes */
};
void table()
{
FILE *stream;
char str[sizeof(struct ar_hdr)];
struct ar_hdr temp;
stream = fopen("archive.txt", "r");
if (stream == 0)
{
perror("error");
exit(0);
}
while (fgets(str, sizeof(str), stream) != NULL)
{
fscanf(stream, "%[^\t]", temp.ar_name);
printf("%s\n", temp.ar_name);
}
if (feof(stream))
{
// hit end of file
printf("End of file reached\n");
}
else
{
// other error interrupted the read
printf("Error: feed interrupted unexpectedly\n");
}
fclose(stream);
}
At this point, I only want to be able to read the data correctly. I will work on seeking the next file after that has been finished. I would like to reiterate my point, however, that I'm not asking for explicit code - I need to learn this stuff and having someone provide me with working code won't do that.
You've defined a char buffer named str to hold your data, but you are accessing it from a separate memory ar_hdr structure named temp. As well, you are reading binary data as a string which will break because of embedded nulls.
You need to read as binary data and either change temp to be a pointer to str or read directly into temp using something like:
ret=fread(&temp,sizeof(temp),1,stream);
(look at the doco for fread - my C is too rusty to be sure of that). Make sure you check and use the return value.

C Program unable to create output text file

A friend of mine needs to use MATLAB for one of his classes, so he called me up (a Computer Science Major) and asked if I could teach him C. I am familiar with C++, so I am also familiar with the general syntax, but had to read up on the IO library for C.
I was creating some simple IO programs to show my friend, but my third program is causing me trouble. When I run the program on my machine using Eclipse (with the CDT) Eclipse's console produces a glitchy output where instead of prompting me for the data, it gets the input and then prints it all at once with FAILURE.
The program is supposed to get a filename from user, create the file, and write to it until the user enters a blank line.
When I compile/run it on my machine via console (g++ files2.c) I am prompted for the data properly, but FAILURE shows up, and there is no output file.
I think the error lies with how I am using the char arrays, since using scanf to get the filename will create a functional file (probably since it ignores whitespace), but not enter the while loop.
#include <stdio.h>
#define name_length 20
#define line_size 80
int main() {
FILE * write_file; // pointer to file you will write to
char filename[name_length]; // variable to hold the name of file
char string_buffer[line_size]; // buffer to hold your text
printf("Filename: "); // prompt for filename
fgets(filename, name_length, stdin); // get filename from user
if (filename[name_length-1] == '\n') // if last char in stream is newline,
{filename[name_length-1] = '\0';} // remove it
write_file = fopen(filename, "w"); // create/overwrite file user named
if (!write_file) {printf("FAILURE");} // failed to create FILE *
// inform user how to exit
printf("To exit, enter a blank line (no spaces)\n");
// while getting input, print to file
while (fgets(string_buffer, line_size, stdin) != NULL) {
fputs(string_buffer, write_file);
if (string_buffer[0] == '\n') {break;}
}
fclose(write_file);
return 0;
}
How should I go about fixing the program? I have found next to nothing on user-terminated input being written to file.
Now if you will excuse me, I have a couple of files to delete off of my University's UNIX server, and I cannot specify them by name since they were created with convoluted filenames...
EDIT------
Like I said, I was able to use
scanf("%s", filename);
to get a working filename (without the newline char). But regardless of if I use scanf or fgets for my while loop, if I use them in conjunction with scanf for the filename, I am not able to write anything to file, as it does not enter the while loop.
How should I restructure my writing to file and my while loop?
Your check for the newline is wrong; you're looking at the last character in filename but it may be before that if the user enters a filename that's shorter than the maximum. You're then trying to open a file that has a newline in it's name.
These lines seem to be incorrect:
if (filename[name_length-1] == '\n') // if last char in stream is newline,
{filename[name_length-1] = '\0';} // remove it
You verify the name_length - 1 character,, which is 19 in your case without any regard of the introduced filename's length. So if your file name's length is less then 18 you won't replace the '\n' character at the end of your string. Obviously the file name can't contain '\n' character.
You need to get the size of you file name first with strlen() as an example.
if (filename[strlen(filename) - 1] == '\n')
{
filename[strlen(filename) - 1] = '\0';
}
(Don't forget to include the string.h header)
I hope I was able to help with my weak english.

How can I edit a text file in C in the easy way?

I have the following code in C. I am trying to look for a line starting with a known word, in order to write back that line with new information. The problem I find is that the stream fp is already after the line I want to edit the moment I find it, so I need to go back to that line in order to write it. How could I do it? Thanks.
FILE *fp;
char line[256];
char description[128];
memset(description, 0, sizeof (description));
if (!(fp = (FILE*) fopen("/etc/samba/smb.conf", "r+"))) {
printf("Error opening smb.conf\n");
return -1;
}
while (fgets(line, sizeof line, fp)) {
if (!strncmp(line, "comment =", 9)) {
sscanf(line, "comment = %[^\t\n]", description);
printf("Old comment found: %s\n",description);
fprintf(fp, "comment = %s\n", "New Comment here");
}
}
fclose(fp);
Even if there was a way to "rewind to previous line", your approach would not work in general. It would only work if you're replacing with a line of the exact same length. (You could make it work for inserting lines shorter than the original with whitespace padding, but not if you want to insert more data than was originally there.)
Create a new file, copy everything there (modifying as you see fit), and replace the original once you've successfully completed.
Alternative, read the file in memory (modifying as you need along the way), and overwrite the original.
You can't "insert" something in the middle of a file without something like the above.
Assuming the file isn't tremendously huge, the easiest way is probably to read the entire file into an array of strings, modify whichever of those strings you want, then write them all back out to disk.
Edit: I do feel obliged to point out that unless you need quite a bit beyond what your question suggests, sed might be a better choice than writing your own program from the ground up.

Opening a file in C through a proccess

I am trying to create a a program that does the following actions:
Open a file and read one line.
Open another file and read another line.
Compare the two lines and print a message.
This is my code:
#include <stdio.h>
#include <string.h>
int findWord(char sizeLineInput2[512]);
int main()
{
FILE*cfPtr2,*cfPtr1;
int i;
char sizeLineInput1[512],sizeLineInput2[512];
cfPtr2=fopen("mike2.txt","r");
// I open the first file
while (fgets(sizeLineInput2, 512, cfPtr2)!=NULL)
// I read from the first 1 file one line
{
if (sizeLineInput2[strlen(sizeLineInput2)-1]=='\n')
sizeLineInput2[strlen(sizeLineInput2)-1]='\0';
printf("%s \n",sizeLineInput2);
i=findWord(sizeLineInput2);
//I call the procedure that compares the two lines
}
getchar();
return 0;
}
int findWord(char sizeLineInput2[512])
{
int x;
char sizeLineInput1[512];
File *cfPtr1;
cfPtr1=fopen("mike1.txt","r");
// here I open the second file
while (fgets(sizeLineInput1, 512,cfPtr1)!=NULL)
{
if (sizeLineInput1[strlen(sizeLineInput1)-1]=='\n')
sizeLineInput1[strlen(sizeLineInput1)-1]='\0';
if (strcmp(sizeLineInput1,sizeLineInput2)==0)
//Here, I compare the two lines
printf("the words %s and %s are equal!\n",sizeLineInput1,sizeLineInput2);
else
printf("the words %s and %s are not equal!\n",sizeLineInput1,sizeLineInput2);
}
fclose(cfPtr1);
return 0;
}
It seems to have some problem with file pointers handling. Could someone check it and tell me what corrections I have to do?
Deconstruction and Reconstruction
The current code structure is, to be polite about it, cock-eyed.
You should open the files in the same function - probably main(). There should be two parallel blocks of code. In fact, ideally, you'd do your opening and error handling in a function so that main() simply contains:
FILE *cfPtr1 = file_open("mike1.txt");
FILE *cfPtr2 = file_open("mike2.txt");
If control returns to main(), the files are open, ready for use.
You then need to read a line from each file - in main() again. If either file does not contain a line, then you can bail out with an appropriate error:
if (fgets(buffer1, sizeof(buffer1), cfPtr1) == 0)
...error: failed to read file1...
if (fgets(buffer2, sizeof(buffer2), cfPtr2) == 0)
...error: failed to read file2...
Then you call you comparison code with the two lines:
findWord(buffer1, buffer2);
You need to carefully segregate the I/O operations from the actual processing of data; if you interleave them as in your first attempt, it makes everything very messy. I/O tends to be messy, simply because you have error conditions to deal with - that's why I shunted the open operation into a separate function (doubly so since you need to do it twice).
You could decide to wrap the fgets() call and error handling up in a function, too:
const char *file1 = "mike1.txt";
const char *file2 = "mike2.txt";
read_line(cfPtr1, file1, buffer1, sizeof(buffer1));
read_line(cfPtr2, file2, buffer2, sizeof(buffer2));
That function can trim the newline off the end of the string and deal with anything else that you want it to do - and report an accurate error, including the file name, if anything goes wrong. Clearly, with the variables 'file1' and 'file2' on hand, you'd use those instead of literal strings in the file_open() calls. Note, too, that making them into variables means it is trivial to take the file names from the command line; you simply set 'file1' and 'file2' to point to the argument list instead of the hard-wired defaults. (I actually wrote: const char file1[] = "mike1.txt"; briefly - but then realized that if you handle the file names via the command line, then you need pointers, not arrays.)
Also, if you open a file, you should close the file too. Granted, if your program exits, the o/s cleans up behind you, but it is a good discipline to get into. One reason is that not every program exits (think of the daemons running services on your computer). Another is that you quite often use a resource (file, in the current discussion) briefly and do not need it again. You should not hold resources in your program for longer than you need them.
Philosophy
Polya, in his 1957 book "How To Solve It", has a dictum:
Try to treat symmetrically what is symmetrical, and do not destroy wantonly any natural symmetry.
That is as valid advice in programming as it is in mathematics. And in their classic 1978 book 'The Elements of Programming Style', Kernighan and Plauger make the telling statements:
[The] subroutine call permits us to summarize the irregularities in the argument list [...]
The subroutine itself summarizes the regularities of the code.
In more modern books such as 'The Pragmatic Programmer' by Hunt & Thomas (1999), the dictum is translated into a snappy TLA:
DRY - Don't Repeat Yourself.
If you find your code doing the 'same' lines of code repeated several times, write a subroutine to do it once and call the subroutine several times.
That is what my suggested rewrite is aiming at.
In both main() and findWord() you should not use strlen(sizeLineInputX) right after reading the file with fgets() - there may be no '\0' in sizeLineInput2 and you will have strlen() read beyond the 512 bytes you have.
Instead of using fgets use fgetc to read char by char and check for a newline character (and for EOF too).
UPD to your UPD: you compare each line of mike2.txt with each line of mike1.txt - i guess that's not what you want. Open both files one outside while loop in main(), use one loop for both files and check for newline and EOF on both of them in that loop.

Resources