Reading a binary file for dates - c

So all it gives me is the hex editor print out, the first 4 bytes is the integer stating how many dates, and then it is followed by chars MMDDYY so 6 chars per date. How would I write a program that reads a .bin file takes the first 4 bytes (int) as the number of elements, and then reads in each dates, parsing out the dates with the year 14?
Something Like this?
int void main(void) {
FILE* infile = fopen("dates.bin", "rb");
int numOfDates;
fgets(numOfDates, sizeof(int), infile);
int i, totalDates;
char allDates[numOfDates * 6];
for(i = 0; i <= numOfDates; i++) {
char tempDate[6];
fgets(tempDate, sizeof(tempDate), infile);
if(tempDate[5] == '1' && tempDate[6] == '4') {
strcpy(allDates[totalDates * 6], tempDate);
totalDates++;
}
}
fclose(infile);
int j;
FILE* outfile = fopen("datesNew.bin", "wb");
fprintf(outfile, "%d", totalDates);
fprintf(outfile, "%s", allDates);
fclose(outfile);
}

The task you have is one that quite literally has to be taken step-by-step validating each input and output along the way. Only then can you have confidence in your input and output. One thing to note, and I suspect this is intentional on the part of your lesson, you have more dates in your input file than the number contained as the first integer in the file. (Number is 7, but file actually contains 8 dates)
Generally, but not always, you can structure your reading and writing as separate operations. Here, since you have to recreate your output file in the same format as the input file (with the number of 2014 dates contained in the output as the first integer value written to the file), you almost have to do it that way to efficiently make a single pass through your input file. (you can always read your input multiple times, but that is what you want to avoid from an efficiency standpoint -- disk I/O being the primary bottleneck)
As noted in my comment main() is type int, it takes arguments, use them to pass the filenames to your code. Being type int, main() therefore returns a value.
Your first task is to open your input file and read the number of dates you are expected to read from the file. That can begin with a simple:
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#define DTSZ 6
int main (int argc, char **argv) {
FILE *fp = NULL; /* file pointer to use for both read/write */
int32_t n = 0; /* number of dates to read from input */
if (argc < 3) { /* validate input/output filenames arguments given */
fprintf (stderr, "error: insufficient input.\n"
"usage: %s infile.bin outfile.bin\n", argv[0]);
return 1;
}
if (!(fp = fopen (argv[1], "rb"))) { /* input file open for reading */
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
if (fread (&n, sizeof n, 1, fp) != 1) { /* read number of dates */
fprintf (stderr, "error: read of n-dates failed.\n");
return 1;
}
Since you now know the number of dates you will read, you can use a variable length array (a VLA) to hold the dates. Thinking forward, you will also need to track which dates are 2014 dates so creating a check chk array of length n and initializing to all zeros will allow you to increment the index in chk that corresponds to a 2014 so you have a mechanism to use to write the 2014 date out to your output file once you have determined how many there are. So after you create your VLAs, you can loop over the number of dates in your input storing them in an array, closing the file when input in done, e.g.
char buf[n][DTSZ + 1]; /* +1 to allow use as string */
int32_t chk[n], n14 = 0; /* array for 2014 indexes, number */
memset (chk, 0, sizeof chk); /* zero the chk array */
for (int i = 0; i < n; i++) { /* read/validate each date */
if (fread (buf[i], 1, DTSZ, fp) != DTSZ) {
fprintf (stderr, "error: failed to read date '%d'.\n", i);
return 1;
}
buf[i][DTSZ] = 0; /* nul-terminate for string use */
printf ("read : %s\n", buf[i]); /* output date found */
if (buf[i][4] == '1' && buf[i][5] == '4') /* 2014 ? */
n14++, chk[i]++; /* increment count, index */
}
fclose (fp); /* close input file */
Now, knowing the number of 2014 dates and the index for those dates within your date buffer, you can open your output file, loop over the indexes in chk outputting the dates in buf that correspond to the set indexes in chk, e.g
if (!(fp = fopen (argv[2], "wb"))) { /* output file open */
fprintf (stderr, "error: file open failed '%s'.\n", argv[2]);
return 1;
}
if (fwrite (&n14, sizeof n14, 1, fp) != 1) { /* output n 2014 */
fprintf (stderr, "error: write of n14-dates failed.\n");
return 1;
}
for (int i = 0; i < n; i++) /* for each date read */
if (chk[i]) { /* check 2014 index */
printf ("write : '%s' to output.\n", buf[i]);
if (fwrite (buf[i], 1, DTSZ, fp) != DTSZ) { /* write/validate */
fprintf (stderr, "error: write of '%s' failed.\n", buf[i]);
return 1;
}
}
if (fclose (fp) == EOF) { /* close output file */
/* handle error */
}
return 0;
}
note: you should also check the return of fclose after any write operation. fclose returns 0 on success or EOF on error and sets errno to contain specifics about the error encountered. A check of if (fclose (fp) == EOF) {/*... handle error ...*/} is sufficient. The concern are that a prior write operation may have encountered an error that is not reported until the stream is closed, the close itself was interrupted, or an IO error occurred. Also note that fclose does not cause data to be immediately written to disk in all cases (the kernel may buffer output, etc..). To force an immediate write disk, a call fsync to insure the buffer is flushed and data is written.
Putting those pieces together, you could expect the following:
Example Use/Output
$ ./bin/freaddates dat/bindates.bin dat/newdates.bin
read : 050514
read : 032313
read : 012514
read : 081612
read : 073114
read : 020612
read : 112315
write : '050514' to output.
write : '012514' to output.
write : '073114' to output.
Verify Written File
$ hexdump -C dat/newdates.bin
00000000 03 00 00 00 30 35 30 35 31 34 30 31 32 35 31 34 |....050514012514|
00000010 30 37 33 31 31 34 |073114|
00000016
Look things over and let me know if you have any further questions.

Related

Check if all letters of the alphabet appear in a file with no repeats

I'm writing a program that needs to read a CSV file and check that all letters in the alphabet appear one time on each side of the comma. The file would look something like this:
a,x
b,j
c,g
d,l
e,s
f,r
g,u
h,z
i,w
j,c
k,e
l,a
m,v
but there would be 26 lines total. What would be the most efficient way to check that each side has all 26 letters with no repeats?
While it is unclear from your question and follow-up comments where exactly you are stuck, or whether you have thrown in the towel and given up, let's take it from the beginning.
Open Your File (or reading stdin)
Before you can do anything with the content of your file, you need to open you file for reading. For reading formatted-input you will generally use the functions that read and write from a file stream using a FILE * stream pointer (as opposed to the low-level file-descriptor file interface). To open your file, you will call fopen and check the return to validate the open succeeded.
Do not hard-code filenames or numbers in your program. Your program takes arguments, either pass the filename to open as an argument, or prompt for entry of the filename. You can increase the flexibility of your program by taking the filename to read as an argument, or read from stdin by default if no argument is provided (as most Linux utilities do). Since stdin is a file stream, you can simply assign it to your FILE* pointer if you are not opening a filename provided as an argument. For example:
FILE *fp = NULL;
if (argc > 1) /* if one argument provided */
fopen (argv[1], "r"); /* open file with name from argument */
else
fp = stdin; /* set fp to stdin */
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
which can be shortened using the ternary operator, e.g.:
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
Reading Your Data
With a file-stream open and validated, you can now read your data from your file. While you could read with fscanf, you are limited in the information it provides in the event less that two values are read. Additionally, reading with the scanf family of functions is full of pitfalls due to what characters remain in your input file-stream depending on the conversion specifiers used and on whether the conversion succeeded or failed. Nonetheless, a simple approach that validates two conversions took place according to your format-string will allow you to read your file, e.g.
char c1, c2; /* characters from each line */
int freq1[MAXC] = {0}, freq2[MAXC] = {0}; /* frequency arrays */
...
while (fscanf (fp, " %c, %c", &c1, &c2) == 2) /* read all chars */
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment element in each */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
(the downside is any variation in format on one line can leave you with unwanted characters stored, and if less than two conversions take place, your read loop stops despite valid data remaining unread)
A better approach is to read a line-at-a-time with a line-oriented input function such as fgets or POSIX getline. With this approach, you are consuming a line of data at a time, and then parsing the needed information from the stored line. The benefits are significant. You have an independent validation of the read itself, and then whether you find the needed values in the line. If your format varies an you parse less than the needed values from the line, you have the option of simply skipping that line and continuing with the next. Further, what remains in your input file stream does not depend on the conversion specifiers used.
An example with fgets and sscanf doing the same thing would be:
char c1, c2, /* characters from each line */
buf[MAXC] = ""; /* buffer to hold each line */
...
while (fgets (buf, MAXC, fp)) /* read all chars */
if (sscanf (buf, " %c, %c", &c1, &c2) == 2) { /* parse values */
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment element in each */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
}
else
fputs ("error: in line format.\n", stderr);
Handling the Frequency of Characters
If you have been paying attention to the read of the data from the file, you will note that a pair of frequency arrays have been incremented on each read of the characters freq1 and freq2. As mentioned in my comments above, you start with an adequately sized array of int to hold the ASCII character set. The arrays are initialized to zero. When you read a character from each column, you simply increment the value at:
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment each element */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
For example the ASCII value for 'a' is 97 (see ASCII Table and Description). So if you read an 'a' and increment
freq1['a']++;
that is the same as incrementing:
freq1[97]++;
When you are done with your read loop, you simply need to iterate over your frequency arrays from 'a' to 'z' and the number of times the corresponding character appeared in your file will be captured in your array. Then you can use the data however you like.
Outputting The Results
The simplest way to output your column1/column2 results is simply to output the number of occurrences for each character. For example:
for (int i = 'a'; i <= 'z'; i++) /* loop over 'a' to 'z' */
printf (" %c: %d, %d\n", i, freq1[i], freq2[i]);
Which will produce output similar to:
$ ./bin/freq_dual_col2 <dat/char2col.txt
lowercase occurrence:
a: 1, 1
b: 1, 0
c: 1, 1
d: 1, 0
e: 1, 1
f: 1, 0
...
If you wanted to get a little more verbose and note whether the characters appears "none", or 1 or whether the character was duplicated "dupe", you could employ a few additional checks, e.g.
for (int i = 'a'; i <= 'z'; i++) { /* loop over 'a' to 'z' */
if (freq1[i] == 1) /* check col 1 chars */
printf (" %c , ", i);
else if (!freq1[i])
fputs ("none, ", stdout);
else
fputs ("dupe, ", stdout);
if (freq2[i] == 1) /* check col 2 chars */
printf (" %c\n", i);
else if (!freq2[i])
fputs ("none\n", stdout);
else
fputs ("dupe\n", stdout);
}
Which would produce output as:
$ ./bin/freq_single_dual_col <dat/char2col.txt
lowercase single occurrence, none or dupe:
a , a
b , none
c , c
d , none
e , e
f , none
...
Putting it altogether, your minimal example using fscanf for your read could be similar to:
#include <stdio.h>
#include <limits.h>
#define MAXC UCHAR_MAX+1
int main (int argc, char **argv) {
char c1, c2; /* characters from each line */
int freq1[MAXC] = {0}, freq2[MAXC] = {0}; /* frequency arrays */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fscanf (fp, " %c,%c", &c1, &c2) == 2) /* read all chars */
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment each element */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
if (fp != stdin) fclose (fp); /* close file if not stdin */
puts ("lowercase occurrence:\n");
for (int i = 'a'; i <= 'z'; i++) /* loop over 'a' to 'z' */
printf (" %c: %d, %d\n", i, freq1[i], freq2[i]);
return 0;
}
The example using fgets and sscanf would be similar to:
#include <stdio.h>
#include <limits.h>
#define MAXC UCHAR_MAX+1
int main (int argc, char **argv) {
char c1, c2, /* characters from each line */
buf[MAXC] = ""; /* buffer to hold each line */
int freq1[MAXC] = {0}, freq2[MAXC] = {0}; /* frequency arrays */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
while (fgets (buf, MAXC, fp)) /* read each line */
if (sscanf (buf, " %c, %c", &c1, &c2) == 2) { /* parse values */
if (c1 > 0 || c2 > 0) /* validate ASCII values */
/* increment each element */
freq1[(unsigned char)c1]++, freq2[(unsigned char)c2]++;
}
else
fputs ("error: in line format.\n", stderr);
if (fp != stdin) fclose (fp); /* close file if not stdin */
puts ("lowercase occurrence:\n");
for (int i = 'a'; i <= 'z'; i++) /* loop over 'a' to 'z' */
printf (" %c: %d, %d\n", i, freq1[i], freq2[i]);
return 0;
}
And if you wanted the more verbose output, then I leave it to you to incorporate it in the code above.
Look things over and let me know if your have further questions.
Add all columns to Sets and check if the Sets are same size of file lines.
remember that Sets ignore duplicates

Reading a file into a dynamically allocated array

How can write this file
5
Llewellyn Mark
1 15 19 26 33 46
Young Brian
17 19 33 34 46 47
Cazalas Jonathan
1 4 9 16 25 36
Siu Max
7 19 34 46 47 48
Balci Murat
5 10 17 19 34 47`
into a dynamically allocated array
typedef struct KnightsBallLottoPlayer{
char firstName[20];
char lastName[20];
int numbers[6]
} KBLottoPlayer;
the first row of the file indicates the number of individual names and number sets.
I have tried to use the below code to dynamically allocate the memory needed for n people using the struct but I am lost on how to read the information in, following the first read to get the n amount of users.
int n;
FILE *fin;
fin = fopen("text.in","r");
if (fin == NULL){
printf("Error: No File!");
}
fscanf(fin, "%d", &n);
printf("%d",n); //reads file correctly
struct KnightsBallLottoPlayer *p = calloc(sizeof(KBLottoPlayer), n);
While the basic premise of the question has been answered many times, each implementation varies just enough based on the data file format, it makes it difficult to find an exact duplicate.
Continuing from the comment, the approach is the same, but the details of how you handle the read will vary based on your input file format and struct members. When you are given a first line specifying how many of what comes next you will need to read, the easiest way to handle storage for your array of stuct is simply to allocate storage for that many struct with malloc (you can use calloc if you wish to zero all bytes at the time of allocation). While you can use a VLA (Variable Length Array), it is just as easy to allocate for 'n' struct.
The read in your case becomes the challenge. You are needing to read data from two separate lines into a single struct. While I would generally suggest fgets to read each line and then call sscanf to parse the data (which you could still do here), fscanf with its format string will simplify the process by allowing you to read both lines of data in a single call if, and only if you already know the number of elements in your member array (your numbers). In your case it is fixed at 6, so as long as that is the case, you can take the simple route.
When reading with any input function (and especially the scanf family of functions), you must validate the return of the function to insure there was a successful conversion for each conversion specifier in your format string. The scanf family of functions return the number of successful number of conversions that took place (or EOF if end-of-file was encountered before a conversion took place). Since fscanf will ignore the intervening '\n', you can read both lines of your data for each struct with the following:
int rtn = fscanf (fp, "%19s %19s %d %d %d %d %d %d",
players[i].firstName, players[i].lastName,
&players[i].numbers[0], &players[i].numbers[1],
&players[i].numbers[2], &players[i].numbers[3],
&players[i].numbers[4], &players[i].numbers[5]);
You would validate that a successful read occurred before you increment i with something like the following:
if (rtn == EOF) /* validate fscanf return not EOF */
break;
else if (rtn != 8) { /* check for matching or input failure */
fputs ("error: matching or input failure occurred.\n", stderr);
break;
}
else /* all struct values read, increment counter */
i++;
You place that in a loop and loop until your index reaches the number of elements you were told to read (or EOF occurs) and you are essentially done (don't forget to close the file you are reading from and free the memory you allocate). A short example putting it altogether could be:
#include <stdio.h>
#include <stdlib.h>
typedef struct KnightsBallLottoPlayer {
char firstName[20];
char lastName[20];
int numbers[6];
} KBLottoPlayer;
int main (int argc, char **argv) {
size_t i = 0, n = 0;
KBLottoPlayer *players = NULL; /* pointer to players */
/* use filename provided as 1st argument (stdin by default) */
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed");
return 1;
}
if (fscanf (fp, "%zu", &n) != 1) { /* read first value into size_t */
fputs ("error: invalid format - 'n'\n", stderr);
return 1;
}
/* allocate/validate array of 'n' KBLottoPlayer */
if ((players = malloc (n * sizeof *players)) == NULL) {
perror ("malloc-players");
return 1;
}
while (i < n) { /* read until 'n' struct worth of data read */
int rtn = fscanf (fp, "%19s %19s %d %d %d %d %d %d",
players[i].firstName, players[i].lastName,
&players[i].numbers[0], &players[i].numbers[1],
&players[i].numbers[2], &players[i].numbers[3],
&players[i].numbers[4], &players[i].numbers[5]);
if (rtn == EOF) /* validate fscanf return not EOF */
break;
else if (rtn != 8) { /* check for matching or input failure */
fputs ("error: matching or input failure occurred.\n", stderr);
break;
}
else /* all struct values read, increment counter */
i++;
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
if (i < n) { /* validate 'n' value read or reduce 'n' */
fputs ("error: less than all data read.\n", stderr);
n = i;
}
for (i = 0; i < n; i++) /* output results */
printf ("%s %s\n%d %d %d %d %d %d\n",
players[i].firstName, players[i].lastName,
players[i].numbers[0], players[i].numbers[1],
players[i].numbers[2], players[i].numbers[3],
players[i].numbers[4], players[i].numbers[5]);
free (players); /* don't forget to free the memory you allocate */
return 0;
}
(note: the %19s in fscanf prevents reading more than 19-characters (plus the nul-terminating character for both the firstName, lastName variables to prevent writing beyond their array bounds)
Example Use/Output
$ ./bin/players_struct <dat/playerdata.txt
Llewellyn Mark
1 15 19 26 33 46
Young Brian
17 19 33 34 46 47
Cazalas Jonathan
1 4 9 16 25 36
Siu Max
7 19 34 46 47 48
Balci Murat
5 10 17 19 34 47
Look things over and let me know if you have further questions. There are many different ways to do this and using a VLA would avoid the allocation, but this is probably on par with any of the simple approaches. There are always additional validations you can use, but the above covers the salient ones.

C program to copy .csv of integers copies one less element unless element size is set to +1

I'm new to learning the C language and I wanted to write a simple program that would copy an array integers from one .csv file to a new .csv file. My code works as intended, however when my array size for fread/fwrite is set to the exact number of elements in the .csv array (10 in this case), it only copies nine of the elements.
When the array size is set to +1, it copies all the elements.
#include <stdio.h>
#include <stdlib.h>
#define LISTSIZE 11
//program that copies an array of integers from one .csv to another .csv
int main(int argc, char * argv[])
{
if (argc != 2)
{
fprintf(stderr, "Usage ./file_sort file.csv\n");
return 1;
}
char * csvfile = argv[1];
FILE * input_csvile = fopen(csvfile, "r"); //open .csv file and create file pointer input_csvile
if(input_csvile == NULL)
{
fprintf(stderr, "Error, Could not open\n");
return 2;
}
unsigned int giving_total[LISTSIZE];
if(input_csvile != NULL) //after file opens, read array from .csv input file
{
fread(giving_total, sizeof(int), LISTSIZE, input_csvile);
}
else
fprintf(stderr, "Error\n");
FILE * printed_file = fopen("school_currentfy1.csv", "w");
if (printed_file != NULL)
{
fwrite(giving_total, sizeof(int), LISTSIZE, printed_file); //copy array of LISTSIZE integers to new file
}
else
fprintf(stderr, "Error\n");
fclose(printed_file);
fclose(input_csvile);
return 0;
}
Does this have something to do with the array being 0-indexed and the .csv file being 1-indexed? I also had an output with the LISTSIZE of 11 which had the last (10) element being displayed incorrectly; 480 instead of 4800.
http://imgur.com/lLOozrc Output/input with LISTSIZE of 10
http://imgur.com/IZPGwsA Input/Output with LISTSIZE of 11
Note: as noted in the comment, fread and fwrite are for reading and writing binary data, not text. If you are dealing with a .csv (comma separated values -- e.g. as exported from MS Excel or Open/LibreOffice calc) You will need to use fgets (or any other character/string oriented function) followed by sscanf (or strtol, strtoul) to read the values as text and perform the conversion to int values. To write the values to your output file, use fprintf. (fscanf is also available for input text processing and conversion, but you lose flexibility in handling variations in input format)
However, if your goal was to read binary data for 10 integers (e.g. 40-bytes of data), then fread and fwrite are fine, but as with all input/output routines, you need to validate the number of bytes read and written to insure you are dealing with valid data within your code. (and that you have a valid output data file when you are done)
There are many ways to read a .csv file, depending on the format. One generic way is to simply read each line of text with fgets and then repeatedly call sscanf to convert each value. (this has a number of advantages in handling different spacing around the ',' compared to fscanf) You simply read each line, assign a pointer to the beginning of the buffer read by fgets, and then call sscanf (with %n to return the number of character processed by each call) and then advance the pointer by that number and scan forward in the buffer until your next '-' (for negative values) or a digit is encountered. (using %n and scanning forward can allow fscanf to be used in a similar manner) For example:
/* read each line until LISTSIZE integers read or EOF */
while (numread < LISTSIZE && fgets (buf, MAXC, fp)) {
int nchars = 0; /* number of characters processed by sscanf */
char *p = buf; /* pointer to line */
/* (you should check a whole line is read here) */
/* while chars remain in buf, less than LISTSIZE ints read
* and a valid conversion to int perfomed by sscanf, update p
* to point to start of next number.
*/
while (*p && numread < LISTSIZE &&
sscanf (p, "%d%n", &giving_total[numread], &nchars) == 1) {
numread++; /* increment the number read */
p += nchars; /* move p nchars forward in buf */
/* find next digit in buf */
while (*p && *p != '-' && (*p < '0' || *p > '9'))
p++;
}
}
Now to create your output file, you simply write numread values back out in comma separated value format. (you can adjust how many your write per line as required)
for (i = 0; i < numread; i++) /* write in csv format */
fprintf (fp, i ? ",%d" : "%d", giving_total[i]);
fputc ('\n', fp); /* tidy up -- make sure file ends with '\n' */
Then it is just a matter of closing your output file and checking for any stream errors (always check on close when writing values to a file)
if (fclose (fp)) /* always validate close after write to */
perror("error"); /* validate no stream errors occurred */
Putting it altogether, you could do something similar to the following:
#include <stdio.h>
#include <stdlib.h>
#define LISTSIZE 10
#define MAXC 256
int main(int argc, char *argv[])
{
if (argc < 3) {
fprintf(stderr, "Usage ./file_sort file.csv [outfile]\n");
return 1;
}
int giving_total[LISTSIZE]; /* change to int to handle negative values */
size_t i, numread = 0; /* generic i and number of integers read */
char *csvfile = argv[1],
buf[MAXC] = ""; /* buffer to hold MAXC chars of text */
FILE *fp = fopen (csvfile, "r");
if (fp == NULL) { /* validate csvfile open for reading */
fprintf(stderr, "Error, Could not open input file.\n");
return 2;
}
/* read each line until LISTSIZE integers read or EOF */
while (numread < LISTSIZE && fgets (buf, MAXC, fp)) {
int nchars = 0; /* number of characters processed by sscanf */
char *p = buf; /* pointer to line */
/* (you should check a whole line is read here) */
/* while chars remain in buf, less than LISTSIZE ints read
* and a valid conversion to int perfomed by sscanf, update p
* to point to start of next number.
*/
while (*p && numread < LISTSIZE &&
sscanf (p, "%d%n", &giving_total[numread], &nchars) == 1) {
numread++; /* increment the number read */
p += nchars; /* move p nchars forward in buf */
/* find next digit in buf */
while (*p && *p != '-' && (*p < '0' || *p > '9'))
p++;
}
}
if (numread < LISTSIZE) /* warn if less than LISTSIZE integers read */
fprintf (stderr, "Warning: only '%zu' integers read from file", numread);
fclose (fp); /* close input file */
fp = fopen (argc > 2 ? argv[2] : "outfile.csv", "w"); /* open output file */
if (fp == NULL) { /* validate output file open for writing */
fprintf(stderr, "Error, Could not open output file.\n");
return 3;
}
for (i = 0; i < numread; i++) /* write in csv format */
fprintf (fp, i ? ",%d" : "%d", giving_total[i]);
fputc ('\n', fp); /* tidy up -- make sure file ends with '\n' */
if (fclose (fp)) /* always validate close after write to */
perror("error"); /* validate no stream errors occurred */
return 0;
}
Like I said, there are many, many ways to approach this. The idea is to build in as much flexibility to your read as possible so it can handle any variations in the input format without choking. Another very robust way to approach the read is using strtol (or strtoul for unsigned values). Both allow will advance a pointer for you to the next character following the integer converted so you can start your scan for the next digit from there.
An example of the read flexibility provide in either of these approaches is shown below. Reading a file of any number of lines, with values separate by any separator and converting each integer encountered to a value in your array, e.g.
Example Input
$ cat ../dat/10int.csv
8572, -2213, 6434, 16330, 3034
12346, 4855, 16985, 11250, 1495
Example Program Use
$ ./bin/fgetscsv ../dat/10int.csv dat/outfile.csv
Example Output File
$ cat dat/outfile.csv
8572,-2213,6434,16330,3034,12346,4855,16985,11250,1495
Look things over and let me know if you have questions. If your intent was to read 40-bytes in binary form, just let me know and I'm happy to help with an example there.
If you want a truly generic read of values in a file, you can tweak the code that finds the number in the input file to scan forward in the file and validate that any '-' is followed by a digit. This allows reading any format and simply picking the integers from the file. For example with the following minor change:
while (*p && numread < LISTSIZE) {
if (sscanf (p, "%d%n", &giving_total[numread], &nchars) == 1)
numread++; /* increment the number read */
p += nchars; /* move p nchars forward in buf */
/* find next number in buf */
for (; *p; p++) {
if (*p >= '0' && *p <= '9') /* positive value */
break;
if (*p == '-' && *(p+1) >= '0' && *(p+1) <= '9') /* negative */
break;
}
}
You can easily process the following file and obtain the same results:
$ cat ../dat/10intmess.txt
8572,;a -2213,;--a 6434,;
a- 16330,;a
- The Quick
Brown%3034 Fox
12346Jumps Over
A
4855,;*;Lazy 16985/,;a
Dog.
11250
1495
Example Program Use
$ ./bin/fgetscsv ../dat/10intmess.txt dat/outfile2.csv
Example Output File
$ cat dat/outfile2.csv
8572,-2213,6434,16330,3034,12346,4855,16985,11250,1495

How to show lines from a text file matching the first five character taking as input?

I am trying to read text file and compare first five character and print the line if the first five characters match. I have text file like this:
03 09 Add this text to file once
03 09 Add this text to file once
12 29 Add this text to file once
So far, I am able to print the content of text file using following code:
while ((c = getc(f)) != EOF) { putchar(c); }
I have input argument as 03 09 as date if the first five characters match then I have to print the whole line.
How to do this?
There are a number of approaches you can take. One of the most straight forward is to read the first line in the file, validate it contains at least 5 character, and save it to a reference buffer to compare each subsequent line against. The strncmp function will allow you to compare the first 'x' number of characters in any two strings. Calling strncmp on the saved buffer and each new line read and comparing the first 5 characters will tell you if the first five chars of each string are the same.
#include <stdio.h>
#include <string.h>
enum { MAXC = 64 }; /* constant for max characters per read */
int main (int argc, char **argv) {
char s1[MAXC] = "";
char buf[MAXC] = "";
size_t idx = 0; /* open given file (default: stdin) */
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, MAXC, fp)) { /* read each line */
if (!idx++) { /* if 1st store s1 */
strcpy (s1, buf);
printf ("%s", s1);
if (strlen (s1) < 5) { /* check 5 chars */
fprintf (stderr, "error: invalid first line.\n");
return 1;
}
else continue; /* read next line */
}
if (strncmp (s1, buf, 5) == 0) /* commpare 1st 5 chars in each line */
printf ("%s", s1);
}
if (fp != stdin) fclose (fp); /* close file */
return 0;
}
Output
$ ./bin/first5 <../dat/first5.txt
03 09 Add this text to file once
03 09 Add this text to file once
note: you can add verifications that each line does not exceed the array size by checking that the strlen of each line is less than your array size (-1 to account for the nul-terminating character) and that last character in each line is a newline. If your line length equals the array size -1 and the last char is not a newline, then additional characters remain in that line that should be read and discarded before attempting to check the next line. That is left to you.
Look over the example and let me know if you have any questions.
Try this:(I am using the fgets() from the previous reply. Do check if that is correct)
while(fgets(line,sizeof(line),file))
{
if((line[0]=='0')&&(line[1]=='3')&&(line[2]==' ')&&(line[3]=='0')&&(line[4]=='9')){
print("%s\n",line);
}
}
This code is pretty basic. It does not care how many chars you have in the line but just checks the first 5 to see if they match. If there are any formatting issues then it will not detect it.
What about this? It takes text from file named input.txt and pattern from standard input. I assumed the maximum length of a line in the file would be 99. You can change it, if you need.
#include <stdio.h>
#include <string.h>
int main()
{
char inp[100],line[100], temp[6];
FILE *file = fopen("input.txt", "r");
fgets(inp,sizeof(inp),stdin);
inp[5]=0;
while(fgets(line,sizeof(line),file))
{
if(strlen(line)<5) continue;
strncpy(temp, line, 5);
temp[5] = 0;
if(!strcmp(inp,temp)) printf("%s", line);
}
fclose(file);
return 0;
}

IO results. Tweak the code [duplicate]

This question already has answers here:
reading a text file into an array
(2 answers)
Closed 8 years ago.
I need some help with an assignment. This is what I have so far:
#include <stdio.h>
#include <stdlib.h>
#define MAXN 100
int main(){
int ch = 0;
FILE *fi = NULL;
FILE *fo = NULL;
int numo = 0;
int numi = 0;
int nump = 0;
fo = fopen("OutputFile.txt", "w+");
if (!fo) {
perror ("Error while opening the file.\n");
exit (EXIT_FAILURE);
}
fi = fopen("InputFile.txt","r");
if(!fi){
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
printf("\n The contents of %s file are :\n", "InputFile.txt");
while( ( ch = fgetc(fi) ) != EOF )
printf("%c",ch);
numi = ch;
numo = numi + 8;
fprintf (fo, " %d\n", numo);
if (fo) fclose (fo);
return 0;
}
Edit 3. Scrapped the array idea since it's causing me more trouble then success. I reduced the list to just one line in the inputfile.txt. The math is simple so I can see what I'm doing and where its going wrong. I've got everything working for the most part, it's just a bit glitched.
First off, The program will read the file just fine and display it in the program. The problem comes after that point and the point the results are saved into OutputFile.txt. Depending on which % I use (%s, %i, %d, %c) The result in OutputFile.txt is either -1, a character, or a longer list of numbers.
How do I get the number from InputFile.txt and save the number to numi?
This is what it looks like
The contents of InputFile.txt are: 10010110
numo=ch+8
Result (Numo) save to OutputFile.txt
When I open the OutputFile.txt the line read 7. So for some reason CH = -1 (Which I want it to equal 10010110) And I'm not sure where the -1 is coming from.
There are many ways to put the pieces of the puzzle together. Finding the tool for the job is 1/2 the battle. In this case strtol will covert base 2 to decimal. The key is to recognize, there is no reason to do character input and you can simplify your code using line-oriented input which will provide your data in a format ready for conversion.
Below are the pieces of the puzzle. They are left somewhat out of order so that you can work to rearrange them to produce a final output file with both the string and the decimal value contained within it. You will probably want to open your output file before you read the text file so that both file steams are available during your read loop.
Take a look and let me know if you have any questions. Note: this is just one of many, many ways to approach this problem:
#include <stdio.h>
#include <stdlib.h>
#define MAXN 100
int main () {
char file_input[25] = { 0 }; /* always initialize all variables */
char file_output[25] = { 0 };
FILE *fi = NULL;
FILE *fo = NULL;
int integers[MAXN] = { 0 };
int i = 0;
int num = 0;
printf ("\n Please enter the input filename: ");
while (scanf ("%[^\n]%*c", file_input) != 1)
fprintf (stderr, "error: read failed for 'file_input', try again\n filename: ");
fi = fopen (file_input, "r"); /* open input file and validate */
if (!fi) {
perror ("Error while opening the file.\n");
exit (EXIT_FAILURE);
}
printf ("\n The contents of file '%s' are :\n\n", file_input);
char *line = NULL; /* NULL forces getline to allocate */
size_t n = 0; /* max chars to read (0 - no limit */
ssize_t nchr = 0; /* number of chars actually read */
while ((nchr = getline (&line, &n, fi)) != -1) {
if (line[nchr - 1] == '\n')
line[--nchr] = 0; /* strip newline from end of line */
integers[i] = strtol (line, NULL, 2); /* convert to decimal */
printf (" %s -> %d\n", line, integers[i]);
if (i == MAXN - 1) { /* check MAXN limit not exceeded */
fprintf (stderr, "error: input lines exceed %d\n", MAXN);
exit (EXIT_FAILURE);
}
i++;
}
if (line) free(line); /* free memory allocated by getline */
if (fi) fclose (fi); /* close file stream when done */
num = i; /* save number of elements in array */
printf ("\n Conversion complete, output filename: ");
while (scanf ("%[^\n]%*c", file_output) != 1)
fprintf (stderr, "error: read failed for 'file_output', try again\n filename: ");
fo = fopen (file_output, "w+"); /* open output file & validate */
if (!fo) {
perror ("Error while opening the file.\n");
exit (EXIT_FAILURE);
}
for (i = 0; i < num; i++) /* write integers to output file */
fprintf (fo, " %d\n", integers[i]);
if (fo) fclose (fo);
return 0;
}
Use/output:
$ ./bin/arrayhelp
Please enter the input filename: dat/binin.txt
The contents of file 'dat/binin.txt' are :
01000101 -> 69
11010110 -> 214
11101110 -> 238
Conversion complete, output filename: dat/binout.txt
$ cat dat/binout.txt
69
214
238
Reading Character-by-character
While this isn't the easiest way to handle reading the file, there is nothing wrong with it. However, you have logic problems. Specifically, you read (and assign as integer) numi = ch; and then assign numo = numi + 8; to write to your output file. This results in adding 8 to the ASCII value of '0' (48) or '1' (49). If you add 8 to that, well, you can do the math. When you read from a file as text, you are reading the ASCII value, NOT the numeric value 1 or 0.
In order to accomplish what you appear to be attempting, you must save all characters in a line to a buffer (a string, a character array, I don't care what you call it). That is the only way, (short of doing a character-by-character conversion to a numeric 1 or 0 and then preforming the binary addition), you have to convert the string of '0's and '1's to a decimal value.
Here is an example using the character-by-character read from fi. Read though it and understand why it needs to be done this way. If you have questions, drop another comment.
#include <stdio.h>
#include <stdlib.h>
#define MAXN 100
int main () {
int ch = 0;
FILE *fi = NULL;
FILE *fo = NULL;
// int numo = 0;
// int numi = 0;
// int nump = 0;
char buffer[MAXN] = { 0 }; /* buffer to hold each line */
int idx = 0; /* index for buffer */
fo = fopen ("OutputFile.txt", "w+"); /* open output file & validate */
if (!fo) {
perror ("Error while opening the file.\n");
exit (EXIT_FAILURE);
}
fi = fopen ("InputFile.txt", "r"); /* open input file & validate */
if (!fi) {
perror ("Error while opening the file.\n");
exit (EXIT_FAILURE);
}
printf ("\n The contents of %s file are :\n\n", "InputFile.txt");
fprintf (fo, " binary decimal\n"); /* header for output file */
while (1) /* loop and test for both '\n' and EOF (-1) to parse file */
{
// printf ("%c", ch); /* we will store each ch in line in buffer */
if ((ch = fgetc (fi)) == '\n' || ch == EOF)
{
if (ch == EOF && idx == 0) /* if EOF (-1) & buffer empty exit loop */
break;
buffer[idx] = 0; /* null-terminate buffer (same as '\0' ) */
idx = 0; /* reset index for next line & continue */
/* write original value & conversion to fo */
fprintf (fo, " %s => %ld\n", buffer, strtol (buffer, NULL, 2));
/* write fi contents to stdout (indented) */
printf (" %s\n", buffer);
}
else
{
buffer[idx++] = ch; /* assign ch to buffer, then increment idx */
}
/* This makes no sense. You are reading a character '0' or '1' from fi,
the unsigned integer value is either the ASCII value '0', which is
decimal 48 (or hex 0x30), or the ASCII value '1', decimal 49/0x31.
If you add 8 and write to 'fo' with '%d' you will get a 16-digit
string of a combination of '56' & '57', e.g. 56575756....
numi = ch;
numo = numi + 8;
*/
}
if (fi) /* close both input and output file streams */
fclose (fi);
if (fo)
fclose (fo);
return 0;
}
output to stdout:
$ ./bin/arrayhelp2
The contents of InputFile.txt file are :
01000101
11010110
11101110
OutputFile.txt:
$ cat OutputFile.txt
binary decimal
01000101 => 69
11010110 => 214
11101110 => 238

Resources