I have a file that contain few different sections. All sections have a start section and end section lines to distinguish between sections.
How can I read lines from section-2?
>start Section-1
Some words are here.
>end Section-1
>start Section-2
Other words are also here.
>end Section-2
With my current code, all the file is printed (all sections except words separating sections). I understand the issue is that in my fgets I'm reading the file until #end Section-2 and I probably need another while loop to read lines from specific start section. But I'm not sure how can I change the code so it will only output words inside the section-2.
Expected output:
Other
words
are
also
here.
What I get now:
Some
words
are
here.
Other
words
are
also
here.
My code:
#define MAXSTR 1000
#define END ">end Section-2\n"
#define ENDWORD ">end"
#define STRWORD ">start"
#define SECTION "Section-2"
int main () {
FILE *file;
char lines[MAXSTR];
char delim[2] = " ";
char *words;
if ((file = fopen("sample.txt", "r")) == NULL) {
printf("File empty.\n");
return 0;
}
while (strcmp(fgets(lines, MAXSTR, file), END) != 0) {
words = strtok(lines, delim);
while (words != NULL && strcmp(words, STRWORD) != 0
&& strcmp(words, SECTION) != 0
&& strcmp(words, ENDWORD) != 0) {
printf("%s\n", words);
words = strtok(NULL, delim);
}
}
fclose(fileUrl);
return 0;
}
You are thinking along the correct lines. The key is to set a flag when you find the first "Section-X" to read and then while that flag is set, tokenize each line until the closing "Section-X" is found, at which time you exit your read-loop.
You can check for "Section-X" however you like, using the entire line, or just the "Section-X" identifier (which I chose below). To locate the "Section-X" text, just use strrchr() to find the last space in each line, and compare from the next character to the end of line for your section, e.g.
#include <stdio.h>
#include <string.h>
#define MAXC 1024
int main (int argc, char **argv) {
if (argc < 2) { /* validate 1 arg givent for filename */
fprintf (stderr, "usage: %s file [\"Section-X\" (default: 2)]\n", argv[0]);
return 1;
}
const char *section = argc > 2 ? argv[2] : "Section-2", /* set section */
*delim = " ";
char line[MAXC];
int found = 0; /* found flag, 0-false, 1-true */
FILE *fp = fopen (argv[1], "r"); /* open file */
if (!fp) { /* validate file open for reading */
perror ("fopen-fp");
return 1;
}
while (fgets (line, MAXC, fp)) { /* read each line */
line[strcspn (line, "\n")] = 0; /* trim \n from end */
char *p = strrchr(line, ' '); /* pointer to last space */
if (p && strcmp (p + 1, section) == 0) { /* compare "Section-X" */
if (found++) /* check/set found flag */
break; /* break loop if 2nd "Section-X" */
continue;
}
if (found) { /* if found set, tokenize each line */
for (p = strtok (line, delim); p; p = strtok (NULL, delim))
puts (p);
}
}
}
Example Use/Output
With your input stored in the file dat/sections.txt and reading default "Section-2":
$ ./bin/read_sections dat/sections.txt
Other
words
are
also
here.
Reading "Section-1":
$ ./bin/read_sections dat/sections.txt "Section-1"
Some
words
are
here.
Look things over and let me know if you have questions.
Related
I'm trying to parse .ini file using only STANDARD Libraries in C.
Input files look like:
[section1]
key1 = value1
key2 = value2
[section2]
key3 = vaule3
key4 = value4
key5 = value5
...
im running that with ./file inputfile.ini section2.key3 and i want to get value of key3 from section2
MY QUESTION is: How to easily store keys and values? - im a total beginner, so I need something simple and easy to implement - maybe struct but how to store all keys and values inside struct if i don't know quantity of keys?
I got stuck here, two strings section and current_section looks equally but in if(section == current_section) they don't pass True, what is the problem?
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
FILE * fPointer;
fPointer = fopen(argv[1], "r"); // read from file
char singleLine[30];
char section[30];
char key[30];
int right_section = 0;
char current_section[30];
sscanf(argv[2], "%[a-zA-Z0-9].%[a-zA-Z0-9]", section, key); //split of section.key
while(!feof(fPointer)){
fgets(singleLine, 30, fPointer); //read line by line
printf("%s", singleLine);
char current_key[30];
char current_value[30];
if(singleLine[0]=='['){
sscanf(singleLine, "[%127[^]]", current_section); //strip from []
printf("current section:%s%s", current_section, section); //both look equally
if(current_section == section){ // doesn't work here, current_section == section looks the same but if doesnt work
right_section = 1;
printf("yes, right");
}
}
}
fclose(fPointer);
return 0;
}```
You are working down the correct path, but there are a few things that you must approach differently if you want ensure things work correctly. If you take nothing else from this answer, learn that you cannot use any input or parsing function without checking the return (that applies to virtually every function you use, unless the operation of code that follows does not depend on the result -- like just printing values) Also, you never use while (!feof(fpointer)), e.g. see: Why is while ( !feof (file) ) always wrong?
Now, how to approach the problem. First, if you need a constant for your array size, then #define a constant or use a global enum. For example, for my sect, inisect, key, inikey and val buffers I would define SPLTC and then for my line buffer, I define MAXC, e.g.
#define SPLTC 128 /* if you need a constant, #define one (or more) */
#define MAXC 256
Depending on whether you need to be -ansi or c89/90 compatible, declare your variables before any operations, e.g.
int main (int argc, char **argv) {
char buf[MAXC], sect[SPLTC], inisect[SPLTC], key[SPLTC], inikey[SPLTC], val[SPLTC];
FILE *fp = NULL;
Then the first thing you will do is validate that sufficient arguments were provided on the command line:
if (argc < 3) { /* validate 2 arguments provided */
fprintf (stderr,
"error: insufficient number of arguments\n"
"usage: %s file.ini section.key\n", argv[0]);
return 1;
}
Next you will open your file and validate that it is open for reading:
/* open/validate file open for reading */
if ((fp = fopen (argv[1], "r")) == NULL) {
fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
perror ("fopen");
return 1;
}
Then split your section.key argument argv[2] into sect and key and validate the separation:
/* split section.key into sect & key */
if (sscanf (argv[2], " %127[^.]. %127s", sect, key) != 2) {
fputs ("error: invalid section.key\n", stderr);
return 1;
}
Now enter your read loop to find your section in the file (you always control the loop with the return of the read function itself):
while (fgets (buf, MAXC, fp)) { /* read each line */
if (buf[0] == '[') { /* is first char '[]'? */
if (sscanf (buf, " [%127[^]]", inisect) == 1) { /* parse section */
if (strcmp (sect, inisect) == 0) /* does it match 2nd arg? */
break; /* if so break loop */
}
}
}
How do you check that the section was found? You can keep a flag-variable as you have done with right_section, or... think about where you would be in the file if your section wasn't found? You would be at EOF. So now you can correctly check feof(fp), e.g.
if (feof (fp)) { /* if file stream at EOF, section not found */
fprintf (stderr, "error: EOF encountered before section '%s' found.\n",
sect);
return 1;
}
If you haven't exited due to not finding your section (meaning you got to this point in the code), just read each line validating a separation into inikey and val (if the validation fails -- you have read all the key/val pairs in that section without a match) If you find the key match during your read of the section success you have your inikey and val. If you complete the loop without a match you can check if you issue an error, and if you reach EOF without a match, you can again check feof(fp) after the loop, e.g.
while (fgets (buf, MAXC, fp)) { /* continue reading lines */
/* parse key & val from line */
if (sscanf (buf, " %127s = %127s", inikey, val) != 2) { /* if not key & val */
fprintf (stderr, "error: end of section '%s' reached "
"with no matching key found.\n", sect);
return 1;
}
if (strcmp (key, inikey) == 0) { /* does key match? */
printf ("section : %s\n key : %s\n val : %s\n", sect, key, val);
break;
}
}
if (feof (fp)) { /* if file stream at EOF, key not found */
fprintf (stderr, "error: EOF encountered before key '%s' found.\n",
argv[3]);
return 1;
}
That's basically it. If you put it altogether you have:
#include <stdio.h>
#include <string.h>
#define SPLTC 128 /* if you need a constant, #define one (or more) */
#define MAXC 256
int main (int argc, char **argv) {
char buf[MAXC], sect[SPLTC], inisect[SPLTC], key[SPLTC], inikey[SPLTC], val[SPLTC];
FILE *fp = NULL;
if (argc < 3) { /* validate 2 arguments provided */
fprintf (stderr,
"error: insufficient number of arguments\n"
"usage: %s file.ini section.key\n", argv[0]);
return 1;
}
/* open/validate file open for reading */
if ((fp = fopen (argv[1], "r")) == NULL) {
fprintf (stderr, "error: file open failed '%s'\n", argv[1]);
perror ("fopen");
return 1;
}
/* split section.key into sect & key */
if (sscanf (argv[2], " %127[^.]. %127s", sect, key) != 2) {
fputs ("error: invalid section.key\n", stderr);
return 1;
}
while (fgets (buf, MAXC, fp)) { /* read each line */
if (buf[0] == '[') { /* is first char '[]'? */
if (sscanf (buf, " [%127[^]]", inisect) == 1) { /* parse section */
if (strcmp (sect, inisect) == 0) /* does it match 2nd arg? */
break; /* if so break loop */
}
}
}
if (feof (fp)) { /* if file stream at EOF, section not found */
fprintf (stderr, "error: EOF encountered before section '%s' found.\n",
sect);
return 1;
}
while (fgets (buf, MAXC, fp)) { /* continue reading lines */
/* parse key & val from line */
if (sscanf (buf, " %127s = %127s", inikey, val) != 2) { /* if not key & val */
fprintf (stderr, "error: end of section '%s' reached "
"with no matching key found.\n", sect);
return 1;
}
if (strcmp (key, inikey) == 0) { /* does key match? */
printf ("section : %s\n key : %s\n val : %s\n", sect, key, val);
break;
}
}
if (feof (fp)) { /* if file stream at EOF, key not found */
fprintf (stderr, "error: EOF encountered before key '%s' found.\n",
argv[3]);
return 1;
}
}
Example Use/Output
Finding valid section/key combinations:
$ ./bin/readini dat/test.ini section2.key3
section : section2
key : key3
val : vaule3
$ /bin/readini dat/test.ini section2.key5
section : section2
key : key5
val : value5
$ ./bin/readini dat/test.ini section1.key2
section : section1
key : key2
val : value2
Attempts to find invalid section/key combinations.
$ ./bin/readini dat/test.ini section1.key3
error: end of section 'section1' reached with no matching key found.
$ ./bin/readini dat/test.ini section2.key8
error: EOF encountered before key 'key8' found.
Look things over and let me know if you have further questions.
As you pointed out, the issue is in the strings compare if statement, the thing is that a char array (and any array in C) variable in reality is a pointer to the first element of the array (this explanation is over-simplified).
So in your if statement you are really comparing the memory addresses of the first element of each of the two array variables instead of comparing the content of each other.
For doing a correct compare of strings there are several options, you could do it manually (iterating over each array and comparing element by element) or you could use some helpers from the standar library, like strcmp or memcmp.
For example you could re-write your if statement as below:
#include <string.h>
if (memcmp ( section, current_section, sizeof(section) ) == 0) {
// both arrays have the same content
}
I have a .txt file that contains data in this format:
xxxx: 0.9467,
yyyy: 0.9489,
zzzz: 0.78973,
hhhh: 0.8874,
yyyy: 0.64351,
xxxx: 0.8743,
and so on...
Let's say that my C program receives, as input, the string yyyy. The program should, simply, return all the instances of yyyy in the .txt file and the average of all their numerical values.
int main() {
FILE *filePTR;
char fileRow[100000];
if (fopen_s(&filePTR, "file.txt", "r") == 0) {
while (fgets(fileRow, sizeof fileRow, filePTR) != NULL) {
if (strstr(fileRow, "yyyy") != NULL) { // Input parameter
printf("%s", fileRow);
}
}
fclose(filePTR);
printf("\nEnd of the file.\n");
} else {
printf("ERROR! Impossible to read the file.");
}
return 0;
}
This is my code right now. I don't know how to:
Isolate the numerical values
actually convert them to double type
average them
I read something about the strtok function (just to start), but I would need some help...
You have started off on the right track and should be commended for using fgets() to read a complete line from the file on each iteration, but your choice of strstr does not ensure the prefix you are looking for is found at the beginning of the line.
Further, you want to avoid hardcoding your search string as well as the file to open. main() takes arguments through argc and argv that let you pass information into your program on startup. See: C11 Standard - ยง5.1.2.2.1 Program startup(p1). Using the parameters eliminates your need to hardcode values by letting you pass the filename to open and the prefix to search for as arguments to your program. (which also eliminates the need to recompile your code simply to read from another filename or search for another string)
For example, instead of hardcoding values, you can use the parameters to main() to open any file and search for any prefix simply using something similar to:
#include <stdio.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char buf[MAXC] = "", *str = NULL; /* buffer for line and ptr to search str */
size_t n = 0, len = 0; /* counter and search string length */
double sum = 0; /* sum of matching lines */
FILE *fp = NULL; /* file pointer */
if (argc < 3) { /* validate 2 arguments given - filename, search_string */
fprintf (stderr, "error: insufficient number of arguments\n"
"usage: %s filename search_string\n", argv[0]);
return 1;
}
if (!(fp = fopen (argv[1], "r"))) { /* open/validate file open for reading */
perror ("fopen-filename");
return 1;
}
str = argv[2]; /* set pointer to search string */
len = strlen (str); /* get length of search string */
...
At this point in your program, you have opened the file passed as the first argument and have validated that it is open for reading through the file-stream pointer fp. You have passed in the prefix to search for as the second argument, assigned it to the pointer str and have obtained the length of the prefix and have stored in in len.
Next you want to read each line from your file into buf, but instead of attempting to match the prefix with strstr(), you can use strncmp() with len to compare the beginning of the line read from your file. If the prefix is found, you can then use sscanf to parse the double value from the file and add it to sum and increment the number of values stored in n, e.g.
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
if (strncmp (buf, str, len) == 0) { /* if prefix matches */
double tmp; /* temporary double for parse */
/* parse with scanf, discarding prefix with assignment suppression */
if (sscanf (buf, "%*1023[^:]: %lf", &tmp) == 1) {
sum += tmp; /* add value to sum */
n++; /* increment count of values */
}
}
}
(note: above the assignment suppression operator for sscanf(), '*' allows you to read and discard the prefix and ':' without having to store the prefix in a second string)
All that remains is checking if values are contained in sum by checking your count n and if so, output the average for the prefix. Or, if n == 0 the prefix was not found in the file, e.g.:
if (n) /* if values found, output average */
printf ("prefix '%s' avg: %.4f\n", str, sum / n);
else /* output not found */
printf ("prefix '%s' -- not found in file.\n", str);
}
That is basically all you need. With it, you can read from any file you like and search for any prefix simply passing the filename and prefix as the first two arguments to your program. The complete example would be:
#include <stdio.h>
#include <string.h>
#define MAXC 1024 /* if you need a constant, #define one (or more) */
int main (int argc, char **argv) {
char buf[MAXC] = "", *str = NULL; /* buffer for line and ptr to search str */
size_t n = 0, len = 0; /* counter and search string length */
double sum = 0; /* sum of matching lines */
FILE *fp = NULL; /* file pointer */
if (argc < 3) { /* validate 2 arguments given - filename, search_string */
fprintf (stderr, "error: insufficient number of arguments\n"
"usage: %s filename search_string\n", argv[0]);
return 1;
}
if (!(fp = fopen (argv[1], "r"))) { /* open/validate file open for reading */
perror ("fopen-filename");
return 1;
}
str = argv[2]; /* set pointer to search string */
len = strlen (str); /* get length of search string */
while (fgets (buf, MAXC, fp)) { /* read each line into buf */
if (strncmp (buf, str, len) == 0) { /* if prefix matches */
double tmp; /* temporary double for parse */
/* parse with scanf, discarding prefix with assignment suppression */
if (sscanf (buf, "%*1023[^:]: %lf", &tmp) == 1) {
sum += tmp; /* add value to sum */
n++; /* increment count of values */
}
}
}
if (n) /* if values found, output average */
printf ("prefix '%s' avg: %.4f\n", str, sum / n);
else /* output not found */
printf ("prefix '%s' -- not found in file.\n", str);
}
Example Use/Output
Using your data file stored in dat/prefixdouble.txt, you can search for each prefix in the file and obtain the average, e.g.
$ ./bin/prefixaverage dat/prefixdouble.txt hhhh
prefix 'hhhh' avg: 0.8874
$ ./bin/prefixaverage dat/prefixdouble.txt xxxx
prefix 'xxxx' avg: 0.9105
$ ./bin/prefixaverage dat/prefixdouble.txt yyyy
prefix 'yyyy' avg: 0.7962
$ ./bin/prefixaverage dat/prefixdouble.txt zzzz
prefix 'zzzz' avg: 0.7897
$ ./bin/prefixaverage dat/prefixdouble.txt foo
prefix 'foo' -- not found in file.
Much easier than having to recompile each time you want to search for another prefix. Look things over and let me know if you have further questions.
Say I have the following text file -
name:asdfg
address:zcvxz
,
name:qwerwer
address:zxcvzxcvxz
,
And I wanna copy the name (without "name:") to a certain string variable, the address to another and so on.
How do I do so without corrupting memory?
Tried using (example) -
char buf[50];
while (fgets(buf, 50, file) != NULL) {
if (!strncmp(buf, "name", 4))
strncpy(somestring, buf + 5, 20)
//do the same for address, continue looping
but the text lines differ in length, so it seems to copy all sorts of crap from the buffer, as the strings arent null terminated so it copies "asdfgcrapcrapcrap".
You are to be commended for using fgets to handle your file I/O as it provides a much more flexible and robust way to read, validate and prepare to parse the lines of data you read. It is generally the recommended way to do line-oriented input (either from a file or from the user). However, this is one of those circumstances where treating multiple records as formatted input does have some advantages.
Let's start with an example reading your data file and capturing the name:.... and address:... data in a simple data structure to hold both the name and address data values in a 20-char array for each. Each line is read, the length is validated, the trailing '\n' is removed and then strchr is used to locate the ':' in the line. (we don't care about lines without ':'). The label before ':' is copied to tmp and then compare against "name" or "address" to determine which value to read. Once the address data is read, both name and addr values are printed to stdout,
#include <stdio.h>
#include <string.h>
enum { MAXC = 20, MAXS = 256 };
typedef struct {
char name[MAXC],
addr[MAXC];
} data;
int main (int argc, char **argv) {
char buf[MAXS] = "",
*name = "name", /* name/address literals for comparison */
*addr = "address";
data mydata = { .name = "" };
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, MAXS, fp)) { /* read each line */
char *p = buf, /* pointer to use with strchr */
tmp[MAXC] = ""; /* storage for labels */
size_t len = strlen (buf); /* get buf len */
if (len && buf[len - 1] == '\n') /* validate last char is '\n' */
buf[--len] = 0; /* overwrite with nul-character */
else if (len + 1 == MAXS) { /* handle string too long */
fprintf (stderr, "error: line too long or no '\n'\n");
return 1;
}
if ((p = strchr (buf, ':'))) { /* find ':' in buf */
size_t labellen = p - buf, /* get length of label */
datalen = strlen (p + 1); /* get length of data */
if (labellen + 1 > MAXC) { /* validate both lengths */
fprintf (stderr, "error: label exceeds '%d' chars.\n", MAXC);
return 1;
}
if (datalen + 1 > MAXC) {
fprintf (stderr, "error: data exceeds '%d' chars.\n", MAXC);
return 1;
}
strncpy (tmp, buf, labellen); /* copy label to temp */
tmp[labellen] = 0; /* nul-terminate */
if (strcmp (name, tmp) == 0) /* is the label "name" ? */
strcpy (mydata.name, p + 1);
else if (strcmp (addr, tmp) == 0) { /* is the label "address" ? */
strcpy (mydata.addr, p + 1);
/* record complete -- output results */
printf ("\nname : %s\naddr : %s\n", mydata.name, mydata.addr);
}
}
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
(note: there are many ways to structure this logic. The example above just represents a semi-standard method)
Example Use/Output
$./bin/nameaddr <dat/nameaddr.txt
name : asdfg
addr : zcvxz
name : qwerwer
addr : zxcvzxcvxz
Here is where I will have a tough time convincing you that fgets was the way to go for this problem. Why? Here we are essentially reading formatted input that is comprised of 3-lines of data. The format string for fscanf doesn't care how many lines are involved, and can easily be constructed to skip '\n' within the formatted input. This can provide (a more fragile), but attractive alternative for the right input files.
For example, the code above can be reduced to the following using fscanf for a formatted read:
#include <stdio.h>
#define MAXC 20
typedef struct {
char name[MAXC],
addr[MAXC];
} data;
int main (int argc, char **argv) {
data mydata = { .name = "" };
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;
}
/* read 3-lines at a time separating name and address at once */
while (fscanf (fp, " name:%19s address:%19s ,",
mydata.name, mydata.addr) == 2)
printf ("\nname : %s\naddr : %s\n", mydata.name, mydata.addr);
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
(the output is the same)
In the rare case, for the correct data file, fscanf can provide a viable alternative to a line-oriented read with fgets. However, your first choice should remain a line-oriented approach using either fgets or POSIX getline.
Look both over and let me know if you have further questions.
If the name is 20 characters or longer, strncpy() won't copy the null terminator to the destination string, so you need to add it yourself.
strncpy(somestring, buf + 5, 19);
somestring[19] = '\0';
I'm currently working on this assignment and I'm stuck. The objective is to read a file and find if these char values exist in the String from the file. I have to compare a String from a file to another String I put in as an argument. However, just as long as each char value is in the String from the file then it "matches".
Example (input and output):
./a.out file1 done
done is in bonehead
done is not in doggie
Example (file1):
bonehead
doggie
As you can see the order in which is compares Strings does not matter and the file also follows one word per line. I've put together a program that finds if the char value is present in the other String but that is only part of the problem. Any idea how to go about this?
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv){
FILE *f = fopen(argv[1], "r");
char *line = NULL;
size_t len = 0;
ssize_t read;
char *word = argv[2];
if(argc != 3){
printf("./a.out <file> <word>\n");
exit(EXIT_SUCCESS);
}
if(f == NULL){
printf("file empty\n");
exit(EXIT_SUCCESS);
}
// confused what this loop does too
while((read = getline(&line, &len, f)) != -1){
char *c = line;
while(*c){
if(strchr(word, *c))
printf("can't spell \"%s\" without \"%s\"!\n", line, word);
else
printf("no \"%s\" in \"%s\".\n", word, line);
c++;
}
}
fclose(f);
exit(EXIT_SUCCESS);
}
Another approach would simply keep a sum of each character matched in the line read from the file, adding one for each unique character in the word supplied to test, and if the sum is equal to the length of the string made up by the unique characters is the search term, then each of the unique characters in the search term are included in the line read from the file.
#include <stdio.h>
#include <string.h>
#define MAXC 256
int main (int argc, char **argv) {
if (argc < 3 ) { /* validate required arguments */
fprintf (stderr, "error: insufficient input, usage: %s file string\n",
argv[0]);
return 1;
}
FILE *fp = fopen (argv[1], "r");
char line[MAXC] = "";
char *s = argv[2]; /* string holding search string */
size_t slen = strlen(s), sum = 0, ulen;
char uniq[slen+1]; /* unique characters in s */
if (!fp) { /* validate file open */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
memset (uniq, 0, slen+1); /* zero the VLA */
/* fill uniq with unique characters from s */
for (; *s; s++) if (!strchr (uniq, *s)) uniq[sum++] = *s;
ulen = strlen (uniq);
s = argv[2]; /* reset s */
while (fgets (line, MAXC, fp)) { /* for each line in file */
if (strlen (line) - 1 < ulen) { /* short line, continue */
printf ("%s is not in %s", s, line);
continue;
}
char *up = uniq; /* ptr to uniq */
sum = 0; /* reset sum */
while (*up) if (strchr (line, *up++)) sum++; /* count chars */
if (sum < ulen) /* validate sum */
printf ("%s is not in %s", s, line);
else
printf ("%s is in %s", s, line);
}
fclose (fp); /* close file */
return 0;
}
Example Use/Output
$ ./bin/strallcinc dat/words.txt done
done is in bonehead
done is not in doggie
which would work equally well for duplicate characters in the search string. e.g.
$ ./bin/strallcinc dat/words.txt doneddd
doneddd is in bonehead
doneddd is not in doggie
You can decide if you would handle duplicate characters differently, but you should make some determination on how that contingency will be addressed.
Let me know if you have any questions.
confused what this loop does
The while (read ... line obviously reads in lines from your file, placing them in the line variable
*c is a pointer to the start of the variable line and this pointer is incremented by c++, so that each letter in the word from the file is accessed. The while loop will be terminated when *c points to the null terminator (0).
The if (strchr(word ... line is testing if the test word contains one of the letters from the word in the file.
This seems to be the reverse of what you are trying to do - finding if all the letters in the test word can be found in the word from the file.
The printf lines are not sensible because there is no either/or - you need one line to print 'yes' our letters are present and one line to print 'no' at least one letter is not present.
The printf statements should be outside the comparison loop, so that you don't get multiple lines of output for each word. Add a flag to show if any letter does not exist in the word. Set flag to 1 at start, and only change it to 0 when a letter is not present, then use the flag to print one of the two outcome statements.
This code snippet may help
/* set flag to 'letters all present' */
int flag = 1;
/* set pointer c to start of input line */
c = word;
/* test word from file for each letter in test word */
while(*c) {
if(strchr(line, *c) == NULL) {
/* set flag to letter not present */
flag = 0;
break;
}
c++;
}
I want to print last n lines of a file using a c program.
I have already used the method of fseek. Now, I want to try it by using array. I have written the code, but, it gives a segmentation fault (core dumped) error.
Please help to modify this code:
#include <stdio.h>
#include <stdlib.h>
char s[10][100];
int main(int argc, char *argv[])
{
FILE *in, *out;
int count = 0;
long pos;
char c;
if ((in = fopen("count.txt", "r")) == NULL)
{
perror("fopen");
exit(EXIT_FAILURE);
}
if ((out = fopen("output.txt", "w")) == NULL)
{
printf("error in opening file");
exit(EXIT_FAILURE);
}
if (argc < 2)
fprintf(stderr, "Arguments which is to be passed should be 2\n");
else if (argc > 2)
printf("too many argumnets are passed");
else
{
int n = atoi(argv[1]);
if(n >= 1 && n< 100)
{
int j,i;
for(i=0;i<=n;i++)
{
j=0;
c = fgetc(in);
for (; j != EOF; ++j)
{
s[i][j]=c;
fputc(c, out);
c = fgetc(in);
if(s[i][j]=='\n')
break;
}
if(s[i][j] != EOF && i== n)
i=0;
}
for (i = 0; i <= n; i++)
for (j = 0; s[i][j] != '\n'; j++)
printf("%c", s[i][j]);
}
else
printf("renter the value of n");
}
fclose(in);
fclose(out);
return 0;
}
Here are some problems you can look into:
char s[10][100]; See if the sizes match the way you're using s : s[i][j]=c;
for(i=0;i<=n;i++) Are you printing n or n+1 lines ?
while (j != EOF) Inside the loop you're not setting j from the file
Plus: the following assignment probably does more harm than good:
if(s[i][j] != EOF && i== n)
i=0;
While you are free to approach the reading of lines one-character-at-a-time using character-oriented input functions, you are making your job a bit more difficult than it needs to be. C offers several functions that provide line-oriented input (e.g. fgets and getline), that are much better suited for reading text one line-at-a-time.
For your purposes here, it seems you need to read/store all lines of input, then based upon a user given number (say 'n'), write/display the last 'n' lines of input to your given output file. While dynamically allocating storage to accommodate an unlimited number of lines or characters does not take that much more effort, below we will use a statically declared array with a MAXL maximum number of lines, each containing a MAXC maximum number of characters. MAXL and MAXC will be constants for the code specified using an anonymous enum. The alternative to using an enum is to #define both as constants.
Rather than hardcoding filenames and the number of lines to display, you can easily pass that information as arguments to your program, making use of getopt to pick off the number of end (or tail) lines to display. Any remaining arguments will be considered input/output filenames (using stdin and stdout as defaults if no additional filenames are provided as arguments).
Below, two helper functions xfopen and processopts are used simply to move the error checking for fopen and option processing with getopt into functions to keep the main body of the code readable.
Using line-oriented input greatly simplifies the reading and storing of lines in your array. The only twist with line-oriented input functions is that they read-and-include the trailing '\n' (newline) character as part of their input. Meaning your only additional task is to remove the trailing '\n' by simply overwriting it with a nul-terminating character '\0' (or simply 0). So when reading lines with fgets, you will generally see something similar to:
while (fgets (array[idx], MAXC, ifp)) /* read each line of input */
{
size_t len = strlen (array[idx]); /* get length - used below */
while (len && (array[idx][len-1] == '\r' || array[idx][len-1] == '\n'))
array[idx][--len] = 0; /* strip trailing '\r' & '\n' */
if (++idx == MAXL) { /* test if line limit reached */
fprintf (stderr, "warning: MAXL lines read.\n");
break;
}
}
Which simply reads a line of information into the buffer array[idx], including a maximum of MAXC characters (including the nul-terminating character) from the file stream ifp. The length of the string read is found with strlen and then working backwards using the length as an index each '\n' (or '\r\n' on windows) is overwritten with a nul-terminating character. Lastly the line index idx is incremented and tested against the constant MAXL to insure you don't write beyond the end of the array. (if array was dynamically allocated, you would realloc here)
That is literally all that is required to do line-oriented input. You can efficiently read as many or as few lines as needed simply by calling, or repeatedly calling, fgets once per line and then removing the line-endings (there are some addition checks you can do to insure a complete line was read, etc.. not relevant to this discussion - here any part of a line that exceeds MAXC chars will simply be read as the next line.)
Putting all the pieces together you could do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
enum { MAXL = 64, MAXC = 128 }; /* constants for max lines and max chars */
FILE *xfopen (const char *fn, const char *mode); /* helper for fopen */
void processopts (int argc, char **argv, size_t *nlines, FILE **ifp, FILE **ofp);
int main (int argc, char **argv) {
char array[MAXL][MAXC] = {{0}}; /* array to hold MAXL strings */
size_t i, idx = 0, nlines = 0; /* index, num lines to print */
FILE *ifp, *ofp;
processopts (argc, argv, &nlines, &ifp, &ofp); /* handle option */
while (fgets (array[idx], MAXC, ifp)) /* read each line of input */
{
size_t len = strlen (array[idx]); /* get length - used below */
while (len && (array[idx][len-1] == '\r' || array[idx][len-1] == '\n'))
array[idx][--len] = 0; /* strip trailing '\r' & '\n' */
if (++idx == MAXL) { /* test if line limit reached */
fprintf (stderr, "warning: MAXL lines read.\n");
break;
}
}
if (ifp != stdin) fclose (ifp); /* close inputfile */
if (nlines >= idx) nlines = 0; /* validate nlines */
if (nlines) { /* tail only nlines lines */
for (i = idx - nlines; i < idx; i++)
fprintf (ofp, "%s\n", array[i]);
}
else { /* print all lines */
for (i = 0; i < idx; i++)
fprintf (ofp, "%s\n", array[i]);
}
if (ofp != stdout) fclose (ofp); /* close output fp */
return 0;
}
/* fopen with error checking */
FILE *xfopen (const char *fn, const char *mode)
{
FILE *fp = fopen (fn, mode);
if (!fp) {
fprintf (stderr, "xfopen() error: file open failed '%s'.\n", fn);
// return NULL;
exit (EXIT_FAILURE);
}
return fp;
}
void processopts (int argc, char **argv, size_t *nlines, FILE **ifp, FILE **ofp)
{
int opt; /* used with getopt to parse */
/* process '-n X' option to set number of tail lines */
while ((opt = getopt (argc, argv, "n:")) != -1) {
switch (opt) {
case 'n':
*nlines = atoi (optarg);
break;
}
}
/* infile/outfile are remaining args (default: stdin/stdout) */
*ifp = argc > optind ? xfopen (argv[optind], "r") : stdin;
*ofp = argc > optind + 1 ? xfopen (argv[optind + 1], "w") : stdout;
}
Input File
$ cat dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
Reading/Writing all to stdin/stdout
$ ./bin/fgets_array_static_opt <dat/captnjack.txt
This is a tale
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
Tailing last 2 lines to stdout
$ ./bin/fgets_array_static_opt <dat/captnjack.txt -n2
A Pirate So Brave
On the Seven Seas.
Tailing last 3 lines to foo.txt
$ ./bin/fgets_array_static_opt dat/captnjack.txt -n 3 debug/foo.txt
$ cat debug/foo.txt
Of Captain Jack Sparrow
A Pirate So Brave
On the Seven Seas.
Let me know if you have any questions.