Justifying text in C and general array - c

I'm attempting to fully justify (left and right columns line-up) input from files and this is what I came up with. The input files have embedded commands so from my pseudo output below I start justifying at the company's line and end at telephone As you can see it randomly joins two of the lines read together. Can someone please tell me why it's doing this? My input files definitely have newline characters in them since I double checked they were entered.
Also how do I do the following: Check if my read line will fit into my output array (of 40 char)? If it doesn't I want to move the overflowed string(s) into the next line or char(s) if it's easier. This one isn't as necessary as my first question but I would really like to make the output as nice as possible and I don't know how to restrict and carry overflow from read lines into the next output array.
Since it began to escape from AT&T's Bell Laboratories in
the early 1970's, the success of the UNIX
operating system has led to many different
versions: recipients of the (at that time free) UNIX system
code all began developing their own different
versions in their own different ways for use and sale.
Universities, research
institutes, government bodies and computer
companies all began using the powerful
UNIX system to develop many of the
technologies which today are part of a
UNIX system. Computer aided design,
manufacturing control systems,laboratorysimulations,even the Internet itself,
all began life with and because of UNIX
Today, without UNIX systems, the Internewould come to a screeching halt.
Most telephone calls could not be made,
electronic commerce would grind to a halt and
there would have never been "Jurassic Park"!
Below is my justify function that's passed the read file line using fgets in another function. The printf lines are just for debugging.
void justify(char strin[]){
int i = 0; //strin iterator
int j = 0; //out iterator
int endSpaces = LINE + 1 - strlen(strin);
int voids = countwords(strin) - 1;
printf("Voids: %d\n", voids);
printf("Input: %s", strin);
//No words in line, exit
if (voids <= 0)
return;
//How many to add between words
int addEvenly = endSpaces/voids;
int addUnevenly = endSpaces % voids;
printf("space to distribute: %d evenly: %d unevenly: %d\n", endSpaces, addEvenly, addUnevenly);
//Copy space left of array to output
while (strin[i] == ' '){
outLine[j++] = ' ';
i++;
}
//One word at a time
while (endSpaces > 0 || addUnevenly > 0){
//Copy letters into out
while (strin[i] != ' '){
outLine[j] = strin[i];
i++;
j++;
}
//Add the necessary spaces between words
if (addEvenly > 0){
for (int k = 0; k < addEvenly; k++){
outLine[j++] = ' ';
}
}
//Distribute to the left
if (addUnevenly > 0){
outLine[j++] = ' ';
endSpaces--;
addUnevenly--;
}
printf("Output: %s\n\n", outLine);
endSpaces = endSpaces - addEvenly;
//Finish copying rest of input to output when no more spaces to add
if (endSpaces == 0 && addUnevenly == 0){
while (strin[i] != '\0')
outLine[j++] = strin[i++];
printf("Output 2: %s\n", outLine);
}
}
fprintf(out, "%s", outLine);
}

On sunday I created a function (justifyline()) able to justify and indent a line you give it as input. It outputs a buffer containing the justified (formatted) text and any eventual text-remainder; such a remainder may be used as input to the function justifyline().
After this step I've used the file below (text.txt) to test the behaviour of such a function. That test demonstrates me the need to use also word wrapping between lines. Then I've written the function formatLineByLine(). The function formatLineByLine() doesn't care of void lines.
Text file (text.txt): (I used the text in your question trying to correct it, but not all I've corrected, then the input file suffers of this fact!)
Since it began to escape from AT&T's
Bell Laboratories in the early 1970's,
the success of the UNIX operating system
has led to many different versions:
recipients of the (at that time free)
UNIX system code all began developing
their own different versions in their
own different ways for use and sale.
Universities, research institutes,
government bodies and computer companies
all began using the powerful UNIX system
to develop many of the technologies which
today are part of a UNIX system.
Computer aided design, manufacturing
control systems, laboratory simulations,
even the Internet itself, all began life
with and because of UNIX Today, without
UNIX systems, the Internet would come to a
screeching halt. Most telephone calls
could not be made, electronic commerce
would grind to a halt and there would
have never been "Jurassic Park"!
The output of the function formatLineByLine()
ABCDE12345678901234567890123456789012345
Since it began to escape from
AT&T's Bell Laboratories in the
early 1970's, the success of the
UNIX operating system has led to
many different versions: recipients
of the (at that time free) UNIX
system code all began developing
their own different versions in
their own different ways for use
and sale. Universities, research
institutes, government bodies and
computer companies all began using
the powerful UNIX system to develop
many of the technologies which
today are part of a UNIX system.
Computer aided design,
manufacturing control systems,
laboratory simulations, even the
Internet itself, all began life
with and because of UNIX Today,
without UNIX systems, the Internet
would come to a screeching halt.
Most telephone calls could not be
made, electronic commerce would
grind to a halt and there would
have never been "Jurassic Park"!
Another step is the idea to use a paragraph per paragraph justifycation. Then I've written the function justifyParagraph(). The function formatInParagraphs() reads the file text.txt and prints it justified using the function justifyParagraph().
The output of the function formatInParagraphs()
ABCDE12345678901234567890123456789012345
Since it began to escape from
AT&T's Bell Laboratories in the
early 1970's, the success of the
UNIX operating system has led to
many different versions: recipients
of the (at that time free) UNIX
system code all began developing
their own different versions in
their own different ways for use
and sale.
Universities, research
institutes, government bodies and
computer companies all began using
the powerful UNIX system to develop
many of the technologies which
today are part of a UNIX system.
Computer aided design,
manufacturing control systems,
laboratory simulations, even the
Internet itself, all began life
with and because of UNIX Today,
without UNIX systems, the Internet
would come to a screeching halt.
Most telephone calls could not be
made, electronic commerce would
grind to a halt and there would
have never been "Jurassic Park"!
The function justifyline() is able to create a justified buffer with indentation (parameter size_t indent) and to use also a single space between the words (parameter int nospacing sent as 1).
The function justifyParagraph() is able to create a justified buffer with line indentation (parameter: size_t indent) and 1st line indentation (parameter: size_t indentstart). The formatted output may be directly printed when a NULL output buffer is sent to the function (parameter char **outbuf sent as NULL). The last line the function generates may be justified or not (parameter: int notFrmtLast sent as 1).
Both justification functions, when the parameter char **outbuf points a NULL pointer ( *outbuf == NULL ), allocate memory using malloc() . In this case you have to free the buffer after its use. If this parameter is passed as NULL to the function justifyParagraph(), the function prints the elaborated output, if outbuf is passed as NULL to the function justifyline(), the function returns an error.
The code is below. An issue of this code is that, in some cases, the length of the string should be computed using a function different from strlen(). To avoid this problem you may use these functions with lines that have a single space between the words. Such a problem affects the functions justifyParagraph() and formatLineByLine().
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int justifyLine(char *inbuf, char **outbuf, size_t linelen, char ** endptr, size_t indent, int nospacing);
int justifyParagraph(char *inbuf,char **outbuf,size_t linelen,size_t indentstart,size_t indent,int notFmtLast);
int formatLineByLine(FILE *f, size_t linelen,size_t indent, int notFrmtLast);
int formatInParagraphs(FILE *f, size_t linelen,size_t indentstart,size_t indent, int notFrmtLast);
int justifyParagraph(char *inbuf,char **outbuf,size_t linelen,size_t indentstart,size_t indent,int notFmtLast)
{
char *optr=NULL,*endp=NULL;
size_t len,s;
int retval,nf;
for(;;) { //Error control loop
if (inbuf==NULL) {
retval=0x10;break;
}
if (indent+indentstart>linelen) {
retval=0x20;break;
}
if (outbuf!=NULL) {
if (*outbuf==NULL) {
if ( (*outbuf=malloc(linelen+1))==NULL ){
retval=0x30;break;
}
}
optr=*outbuf;
}
endp=inbuf;
indent+=indentstart;
len=linelen-indent;
s=indentstart;nf=0;
while( *endp!=0) {
if (notFmtLast && strlen(endp)<linelen-indent)
nf=1;
if ( (retval=justifyLine(endp,&optr,linelen,&endp,
indent,nf)) ) {
retval|=0x40;break;
}
if (outbuf!=NULL) {
optr+=strlen(optr);
*optr++='\n';
*optr=0;
} else {
puts(optr);
}
indent-=s;
len+=s;
s=0;
}
break; //Close error ctrl loop!
}
if (outbuf==NULL && optr!=NULL)
free(optr);
return retval;
}
int justifyLine(char *inbuf,char **outbuf,size_t linelen, char ** endptr,size_t indent,int nospacing)
{
size_t textlen,tmp;
size_t spctoadd,spcodd,spcin;
size_t timetoodd;
size_t ibidx,obidx,k,wc;
char * endp;
char * outb=NULL;
int retval=0;
for(;;) { //Error control loop
endp=inbuf;
if (inbuf==NULL) {
retval=1;break;
}
if (indent>linelen) {
retval=2;break;
}
if (outbuf==NULL) {
retval=3;break;
}
if (*outbuf==NULL) {
if ( (*outbuf=malloc(linelen+1))==NULL ){
retval=4;break;
}
}
outb=*outbuf;
//Leave right spaces
while(*inbuf==' ')
inbuf++;
if (*inbuf==0) {
endp=inbuf;
*outb=0;
break; //exit from error loop without error!
}
linelen-=indent;
//Count words and the minimum number of characters
ibidx=0;
wc=0;textlen=0;k=1;endp=NULL;
while ( *(inbuf+ibidx)!=0 ) {
if (*(inbuf+ibidx)==' ') {
ibidx++;continue;
}
//There's a char!
k=ibidx; //last word start
tmp=textlen;
wc++;textlen++; //add the space after the words
//textlen<linelen because textlen contains also the space after the word
// while(textlen<=linelen && *(inbuf+ibidx)!=' ' && *(inbuf+ibidx) ) {
while(*(inbuf+ibidx)!=' ' && *(inbuf+ibidx) ) {
textlen++;ibidx++;
}
if (textlen>linelen+1) {
endp=inbuf+k;
textlen=tmp;
wc--;
break;
}
}
textlen=textlen-wc;
if (endp==NULL) {
endp=inbuf+ibidx;
}
if (textlen<2) {
*outb=0;
break; //exit from error loop without error!
}
//Prepare outbuf
memset(outb,' ',linelen+indent);
*(outb+linelen+indent)=0;
ibidx=0;
obidx=indent;
if (wc>1) {
if (!nospacing) {
//The odds are max in number == wc-2
spctoadd=linelen-textlen;
} else {
spctoadd=wc-1;
}
spcin=spctoadd/(wc-1);
spcodd=spctoadd % (wc-1);
if (spcodd)
timetoodd=(wc-1)/spcodd;
k=timetoodd;
while(spctoadd) {
while(*(inbuf+ibidx)!=' ') {
*(outb+obidx++)=*(inbuf+ibidx++);
}
obidx+=spcin;spctoadd-=spcin;
if (spcodd && !(--k)) {
k=timetoodd;
spcodd--;
spctoadd--;
obidx++;
}
while(*(inbuf+ ++ibidx)==' ');
}
}
while(*(outb+obidx) && *(inbuf+ibidx) && *(inbuf+ibidx)!=' ')
*(outb+obidx++)=*(inbuf+ibidx++);
//There're words longer then the line!!!
if (*(inbuf+ibidx) && *(inbuf+ibidx)!=' ')
endp=inbuf+ibidx;
break; //Terminate error ctrl loop.
}
if (endptr!=NULL)
*endptr=endp;
return retval;
}
int formatLineByLine(FILE *f, size_t linelen,size_t indent, int notFrmtLast)
{
char text[250],*app;
//justifyLine allocates memory for the line if the outbuf (optr) value is NULL
char * optr=NULL;
size_t j,k;
//print a ruler
for(j=0;j<indent;j++)
printf("%c",'A'+(char)j);
for(j=1;j<=linelen-indent;j++)
printf("%c",'0'+(char)(j%10));
printf("\n");
//starts printing
fseek(f,0,SEEK_SET);
j=0;
while(fgets(text+j,sizeof(text)-j,f)) {
if ( (app=strrchr(text+j,'\n')) ) {
*app=0;
}
k=strlen(text);
if (strlen(text)<linelen-indent) {
if (!*(text+k) && *(text+k-1)!=' ') {
*(text+k++)=' ';
*(text+k)=0;
}
j=k;
continue;
}
app=text;
do {
//justifyLine allocates memory for the line if the outbuf (optr) value is NULL
if ( justifyLine(app,&optr,linelen,&app,indent,0) ) {
if (optr!=NULL)
free(optr);
return 1;
}
printf("%s\n",optr);
j=(*app!=0)?strlen(app):0;
} while(j>linelen-indent);
if (j) {
strcpy(text,app);
*(text+j++)=' ';
*(text+j)=0;
}
}
if (*text!=0 && j) {
if ( justifyLine(text,&optr,linelen,NULL,indent,notFrmtLast) )
{
if (optr!=NULL)
free(optr);
return 2;
}
printf("%s\n",optr);
}
//justifyLine allocates memory for the line if the outbuf value is NULL
if (optr!=NULL)
free(optr);
return 0;
}
int formatInParagraphs(FILE *f, size_t linelen,size_t indentstart,size_t indent, int notFrmtLast)
{
char text[1024], *app;
//To uncomment when you use the commented justifyParagraph line.
//see below
//char *outbuf=NULL;
size_t j;
//print a ruler
for(j=0;j<indent;j++)
printf("%c",'A'+(char)j);
for(j=1;j<=linelen-indent;j++)
printf("%c",'0'+(char)(j%10));
printf("\n");
//starts printing
fseek(f,0,SEEK_SET);
j=0;
while(fgets(text+j,sizeof(text),f)) {
if ( (app=strrchr(text+j,'\n')) ) {
*app++=' ';*app=0;
}
if ( *(text+j)==' ' && !*(text+j+1) ) {
//The following commented line allocates memory creating a paragraph buffer!
//doesn't print the formatted line.
//justifyParagraph(text,&outbuf,linelen,indentstart,indent,notFrmtLast);
//This line directly print the buffer allocating and de-allocating
//only a line buffer. It prints the formatted line.
justifyParagraph(text,NULL,linelen,indentstart,indent,notFrmtLast);
j=0;
//To uncomment when you use the commented justifyParagraph line.
// printf("%s\n\n",outbuf);
puts("");
} else {
j+=strlen(text+j);
}
}
return 0;
}
int main(void)
{
FILE * file;
file=fopen("text.txt","r");
formatLineByLine(file,40,5,1);
puts("");
formatInParagraphs(file,40,5,5,1);
fclose(file);
return 0;
}

You were incredibly close – but you forgot one thing!
After copying a word into outLine, you insert the correct number of additional spaces, and continue with 'the next word'. However, at that point the input pointer i still is at the end of the previously copied word (so it points to the first space immediately after that). The test while (strin[i] != ' ') then immediately fails and you insert the additional spaces at that point again. This continues until you run out of spaces to add, and at the very end you add what was not processed, which is "the entire rest of the string".
The fix is simple: after copying your word into outLine, copy the original space(s) as well, so the i iterator gets updated to point to the next word.
//One word at a time
while (endSpaces > 0 || addUnevenly > 0)
{
//Copy letters into out
while (strin[i] != ' ')
{
outLine[j] = strin[i];
i++;
j++;
}
//Copy original spaces into out <-- FIX!
while (strin[i] == ' ')
{
outLine[j] = strin[i];
i++;
j++;
}
With this, your code works entirely as you intended. Output:
|Since it began to escape from AT&T's Bell Laboratories in|
|the early 1970's, the success of the UNIX|
|operating system has led to many different|
|versions: recipients of the (at that time free) UNIX system|
|code all began developing their own different|
|versions in their own different ways for use and sale.|
| Universities, research|
|institutes, government bodies and computer|
|companies all began using the powerful |
|UNIX system to develop many of the |
|technologies which today are part of a |
|UNIX system. Computer aided design, |
|manufacturing control systems,laboratorysimulations,even the Internet itself, |
|all began life with and because of UNIX |
|Today, without UNIX systems, the Internewould come to a screeching halt.|
|Most telephone calls could not be made,|
|electronic commerce would grind to a halt and|
|there would have never been "Jurassic Park"! |
Possible improvements
Justified lines should never begin with whitespace (your Copy space left of array to output part). Just increment the pointer there:
//Copy space left of array to output
while (strin[i] == ' ')
{
// outLine[j++] = ' ';
i++;
endSpaces++;
}
(and move the calculation for How many to add between words below this, because it changes endSpaces).
The same goes for spaces at the end. You can adjust endSpaces at the start
int l = strlen(strin);
while (l > 0 && strin[l-1] == ' ')
{
l--;
endSpaces++;
}
and suppress copying the trailing spaces into outLn at the bottom. (That needs some additional tinkering, I couldn't get it right first time.)
It is much neater to ignore multiple spaces inside the input string as well, but that takes a bit more code.
With these three implemented, you get a slightly neater output:
|Since it began to escape from AT&T's Bell Laboratories in|
|the early 1970's, the success of the UNIX|
|operating system has led to many different|
|versions: recipients of the (at that time free) UNIX system|
|code all began developing their own different|
|versions in their own different ways for use and sale.|
|Universities, research|
|institutes, government bodies and computer|
|companies all began using the powerful|
|UNIX system to develop many of the|
|technologies which today are part of a|
|UNIX system. Computer aided design,|
|manufacturing control systems,laboratorysimulations,even the Internet itself,|
|all began life with and because of UNIX|
|Today, without UNIX systems, the Internewould come to a screeching halt.|
|Most telephone calls could not be made,|
|electronic commerce would grind to a halt and|
|there would have never been "Jurassic Park"!|
A drawback of this one-line-at-a-time method is that it cannot easily be rewritten to gather input until a line overflows. To do so, you need:
a routine that skips all spaces and return a pointer to the next word.
a routine that reads words until a line is 'overfull' – that is, the number of words plus (the number of words - 1) for spaces is larger than your LINE value. This uses routine #1 and outputs exactly one justified line.
You need to pass on the location and number of strings from your main to both these routines, and in both check if you are at the end of either a single input line or the entire input array.

I've written this main that contains two simple methods to center a text in a line. The first method only prints the text variable without modifying it, the second method modifies the text variable and then prints it. (Here method is not intended as function, the code contains two examples which you may translate easily in simple functions)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char text[81],fmt[10];
int linelen=80,tlen;
int spacetocenter=0;
printf("Insert text to center [max %lu char]:\n",sizeof(text)-1);
if (scanf("%[^\n]",text)<1) {
perror("scanf");
return -1;
}
getchar(); //Leaves return from the buffer
tlen=strlen(text);
spacetocenter=(linelen-tlen)/2;
if (spacetocenter<0)
spacetocenter=0;
//Method one (this doesn't modify text)
//This method directly prints the contents of text centered.
//----------------------------------------------------------
snprintf(fmt,sizeof(fmt),"%%%+ds\n",spacetocenter+tlen);
//printf("%s\n",fmt); // prints the used format
printf(fmt,text);
//Method two (this modifies text)
//This method modifies the contents of the variable text
//----------------------------------------------------------
memmove(text+spacetocenter,text,tlen+1);
memset(text,' ',spacetocenter);
printf("%s\n",text);
return 0;
}
Note:
After the second method is applied tlen no longer contains the length of text!
The program consider the line of 80 chars, if you need shorter/longer lines you have to modify the value of the variable linelen.

Related

Strategy for cycling trough preexisting set of variables in c

I’m trying to program a HMI console to read a file from an USB pen drive and display its data on the screen. This is a csv file and the objective is to store the interpreted data to HMI console memory, which the HMI console later interprets. The macros on these consoles run in C (not C++).
I have no issue with both reading and interpreting the file, the issue that the existing function (not accessible to me, shown below) to write in the console memory only interprets char.
int WriteLocal( const char *type, int addr, int nRegs, void *buf , int flag );
Parameter: type is the string of "LW","LB" etc;
address is the Operation address ;
nRegs is the length of read or write ;
buf is the buffer which store the reading or writing data
flag is 0,then codetype is BIN,is 1 then codetype is BCD;
return value : 1 , Operation success
0 , Operation fail.
As my luck would have it I need to write integer values. What are available to me are the variables for each memory position. These are preexisting and are named individually such as:
int WR_LW200;
int WR_LW202;
int WR_LW204;
...
int WR_LW20n;
Ideally we could have a vector with all the names of the variables but unfortunately this is not possible. I could manually write every single variable but I need to do 300 of these…
must be a better way, right?
Just to give you a look on how it ended up looking:
int* arr[50][5] = { {&WR_LW200, &WR_LW400, &WR_LW600, &WR_LW800, &WR_LW1000},
{&WR_LW202, &WR_LW402, &WR_LW602, &WR_LW802, &WR_LW1002},
{&WR_LW204, &WR_LW404, &WR_LW604, &WR_LW804, &WR_LW1004},
{&WR_LW206, &WR_LW406, &WR_LW606, &WR_LW806, &WR_LW1006},
{&WR_LW208, &WR_LW408, &WR_LW608, &WR_LW808, &WR_LW1008},
{&WR_LW210, &WR_LW410, &WR_LW610, &WR_LW810, &WR_LW1010},
{&WR_LW212, &WR_LW412, &WR_LW612, &WR_LW812, &WR_LW1012},
{&WR_LW214, &WR_LW414, &WR_LW614, &WR_LW814, &WR_LW1014},
{&WR_LW216, &WR_LW416, &WR_LW616, &WR_LW816, &WR_LW1016},
{&WR_LW218, &WR_LW418, &WR_LW618, &WR_LW818, &WR_LW1018},
{&WR_LW220, &WR_LW420, &WR_LW620, &WR_LW820, &WR_LW1020},
{&WR_LW222, &WR_LW422, &WR_LW622, &WR_LW822, &WR_LW1022},
{&WR_LW224, &WR_LW424, &WR_LW624, &WR_LW824, &WR_LW1024},
{&WR_LW226, &WR_LW426, &WR_LW626, &WR_LW826, &WR_LW1026},
{&WR_LW228, &WR_LW428, &WR_LW628, &WR_LW828, &WR_LW1028},
{&WR_LW230, &WR_LW430, &WR_LW630, &WR_LW830, &WR_LW1030},
{&WR_LW232, &WR_LW432, &WR_LW632, &WR_LW832, &WR_LW1032},
{&WR_LW234, &WR_LW434, &WR_LW634, &WR_LW834, &WR_LW1034},
{&WR_LW236, &WR_LW436, &WR_LW636, &WR_LW836, &WR_LW1036},
{&WR_LW238, &WR_LW438, &WR_LW638, &WR_LW838, &WR_LW1038},
{&WR_LW240, &WR_LW440, &WR_LW640, &WR_LW840, &WR_LW1040},
{&WR_LW242, &WR_LW442, &WR_LW642, &WR_LW842, &WR_LW1042},
{&WR_LW244, &WR_LW444, &WR_LW644, &WR_LW844, &WR_LW1044},
{&WR_LW246, &WR_LW446, &WR_LW646, &WR_LW846, &WR_LW1046},
{&WR_LW248, &WR_LW448, &WR_LW648, &WR_LW848, &WR_LW1048},
{&WR_LW250, &WR_LW450, &WR_LW650, &WR_LW850, &WR_LW1050},
{&WR_LW252, &WR_LW452, &WR_LW652, &WR_LW852, &WR_LW1052},
{&WR_LW254, &WR_LW454, &WR_LW654, &WR_LW854, &WR_LW1054},
{&WR_LW256, &WR_LW456, &WR_LW656, &WR_LW856, &WR_LW1056},
{&WR_LW258, &WR_LW458, &WR_LW658, &WR_LW858, &WR_LW1058},
{&WR_LW260, &WR_LW460, &WR_LW660, &WR_LW860, &WR_LW1060},
{&WR_LW262, &WR_LW462, &WR_LW662, &WR_LW862, &WR_LW1062},
{&WR_LW264, &WR_LW464, &WR_LW664, &WR_LW864, &WR_LW1064},
{&WR_LW266, &WR_LW466, &WR_LW666, &WR_LW866, &WR_LW1066},
{&WR_LW268, &WR_LW468, &WR_LW668, &WR_LW868, &WR_LW1068},
{&WR_LW270, &WR_LW470, &WR_LW670, &WR_LW870, &WR_LW1070},
{&WR_LW272, &WR_LW472, &WR_LW672, &WR_LW872, &WR_LW1072},
{&WR_LW274, &WR_LW474, &WR_LW674, &WR_LW874, &WR_LW1074},
{&WR_LW276, &WR_LW476, &WR_LW676, &WR_LW876, &WR_LW1076},
{&WR_LW278, &WR_LW478, &WR_LW678, &WR_LW878, &WR_LW1078},
{&WR_LW280, &WR_LW480, &WR_LW680, &WR_LW880, &WR_LW1080},
{&WR_LW282, &WR_LW482, &WR_LW682, &WR_LW882, &WR_LW1082},
{&WR_LW284, &WR_LW484, &WR_LW684, &WR_LW884, &WR_LW1084},
{&WR_LW286, &WR_LW486, &WR_LW686, &WR_LW886, &WR_LW1086},
{&WR_LW288, &WR_LW488, &WR_LW688, &WR_LW888, &WR_LW1088},
{&WR_LW290, &WR_LW490, &WR_LW690, &WR_LW890, &WR_LW1090},
{&WR_LW292, &WR_LW492, &WR_LW692, &WR_LW892, &WR_LW1092},
{&WR_LW294, &WR_LW494, &WR_LW694, &WR_LW894, &WR_LW1094},
{&WR_LW296, &WR_LW496, &WR_LW696, &WR_LW896, &WR_LW1096},
{&WR_LW298, &WR_LW498, &WR_LW698, &WR_LW898, &WR_LW1098} };
Big right? I had consurns that this HMI would have issues with such an approach but it did the job. The code below runs trough a string that comes from the csv file. This code runs inside another while cycle to cycle trough the multi dimensional array.
it's a little crude but works.
while (i<=5)
{
memset(lineTemp, 0, sizeof lineTemp); // clear lineTemp array
while (lineFromFile[index] != delimiter)
{
if (lineFromFile[index] != delimiter && lineFromFile[index] != '\0') { lineTemp[j] = lineFromFile[index]; index++; j++; }
if (lineFromFile[index] == '\0') { i = 5; break; }
}
index++;
lineTemp[j] = '\0'; // NULL TERMINATION
j = 0;
if (i == -1) { WriteLocal("LW",temp,3,lineTemp,0); }
if (i >= 0 && i<=5) { *(arr[x][i]) = atoi(lineTemp); }
i++;
}
Thanks again for the tip.
Cheers

Output problems in c

I have a problem with output of the make[] array is giving me. So first I tokenize the file called Carss.txt, only grabbing the third & fifth column, and store the result in make[] array and year[] array respectively.
I tested out the make[] array to see if it would output the results I wanted. It did inside the while(fgets) loop, but when I tested it on the outside of while(fgets), it's repeating.
I don't understand what's the problem. My program and the text file is provided below.
Output of make array inside while(fgets) loop: (what I wanted)
FORD
FORD
JAYCO
Output of make array outside while(fgets) loop:(Not what I wanted)
JAYCO
JAYCO
JAYCO
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
FILE * fp;
char * filename = "Carss.txt";
char lines[100000], blank[100000];
char * token, * del = " ";
int i = 0, c, d = 0, j;
char * make[10000];
char * year[10000];
if ((fp = fopen(filename, "r")) == NULL) /* Opens the file */ {
printf("can't open %s\n", filename);
exit(1);
}
c = 0;
while (fgets(lines, sizeof(lines), fp) != NULL) {
token = strtok(lines, "\t");
i = 1;
while (i < 5) {
token = strtok(NULL, "\t");
if (i == 2) {
make[c] = token;
}
if (i == 4) {
year[c] = token;
}
i++;
}
printf("%s\n", make[c]); /* OUTPUT OF THE ARRAY HERE IS WHAT I WANTED */
c++;
}
c = 0;
while (c < 3) {
printf("%d\n", c);
printf("%s\n", make[c]);
c++;
}
}
The Text File I tokenize: Carss.txt
1 02V288000 FORD FOCUS 2001 02S41 ELECTRICAL SYSTEM:BATTERY:CABLES FORD MOTOR COMPANY 19990719 20010531 V 291854 20030210 ODI Ford Motor Company 20021106 20021106 CERTAIN PASSENGER VEHICLES EQUIPPED WITH ZETEC ENGINES, LOOSE OR BROKEN ATTACHMENTS AND MISROUTED BATTERY CABLES COULD LEAD TO CABLE INSULATION DAMAGE. THIS, IN TURN, COULD CAUSE THE BATTERY CABLES TO SHORT RESULTING IN HEAT DAMAGE TO THE CABLES. BESIDES HEAT DAMAGE, THE "CHECK ENGINE" LIGHT MAY ILLUMINATE, THE VEHICLE MAY FAIL TO START, OR SMOKE, MELTING, OR FIRE COULD ALSO OCCUR. DEALERS WILL INSPECT THE BATTERY CABLES FOR THE CONDITION OF THE CABLE INSULATION AND PROPER TIGHTENING OF THE TERMINAL ENDS. AS NECESSARY, CABLES WILL BE REROUTED, RETAINING CLIPS INSTALLED, AND DAMAGED BATTERY CABLES REPLACED. OWNER NOTIFICATION BEGAN FEBRUARY 10, 2003. OWNERS WHO DO NOT RECEIVE THE FREE REMEDY WITHIN A REASONABLE TIME SHOULD CONTACT FORD AT 1-866-436-7332. ALSO CONTACT THE NATIONAL HIGHWAY TRAFFIC SAFETY ADMINISTRATION'S AUTO SAFETY HOTLINE AT 1-888-DASH-2-DOT (1-888-327-4236). 000015339000215022000000202
2 02V288000 FORD FOCUS 2000 02S41 ELECTRICAL SYSTEM:BATTERY:CABLES FORD MOTOR COMPANY 19990719 20010531 V 291854 20030210 ODI Ford Motor Company 20021106 20021106 CERTAIN PASSENGER VEHICLES EQUIPPED WITH ZETEC ENGINES, LOOSE OR BROKEN ATTACHMENTS AND MISROUTED BATTERY CABLES COULD LEAD TO CABLE INSULATION DAMAGE. THIS, IN TURN, COULD CAUSE THE BATTERY CABLES TO SHORT RESULTING IN HEAT DAMAGE TO THE CABLES. BESIDES HEAT DAMAGE, THE "CHECK ENGINE" LIGHT MAY ILLUMINATE, THE VEHICLE MAY FAIL TO START, OR SMOKE, MELTING, OR FIRE COULD ALSO OCCUR. DEALERS WILL INSPECT THE BATTERY CABLES FOR THE CONDITION OF THE CABLE INSULATION AND PROPER TIGHTENING OF THE TERMINAL ENDS. AS NECESSARY, CABLES WILL BE REROUTED, RETAINING CLIPS INSTALLED, AND DAMAGED BATTERY CABLES REPLACED. OWNER NOTIFICATION BEGAN FEBRUARY 10, 2003. OWNERS WHO DO NOT RECEIVE THE FREE REMEDY WITHIN A REASONABLE TIME SHOULD CONTACT FORD AT 1-866-436-7332. ALSO CONTACT THE NATIONAL HIGHWAY TRAFFIC SAFETY ADMINISTRATION'S AUTO SAFETY HOTLINE AT 1-888-DASH-2-DOT (1-888-327-4236). 000015339000215021000000202
3 02V236000 JAYCO FT EAGLE 10 SG 2003 EQUIPMENT:OTHER:LABELS JAYCO, INC. 20020730 20020813 V 86 20020923 MFR Jayco, Inc. 20020904 20020912 ON CERTAIN FOLDING TENT CAMPERS, THE FEDERAL CERTIFICATION (AND RVIA) LABELS HAVE THE INCORRECT GROSS VEHICLE WEIGHT RATING, TIRE SIZE, AND INFLATION PRESSURE LISTED. IF THE TIRES WERE INFLATED TO 80 PSI, THEY COULD BLOW RESULTING IN A POSSIBLE CRASH. OWNERS WILL BE MAILED CORRECT LABELS FOR INSTALLATION ON THEIR VEHICLES. OWNER NOTIFICATION BEGAN SEPTEMBER 23, 2002. OWNERS SHOULD CONTACT JAYCO AT 1-877-825-4782. ALSO, CUSTOMERS CAN CONTACT THE NATIONAL HIGHWAY TRAFFIC SAFETY ADMINISTRATION'S AUTO SAFETY HOTLINE AT 1-888-DASH-2-DOT (1-888-327-4236). 000015210000106403000000349
When you do this:
token = strtok(NULL, "\t");
if(i==2)
{
make[c] = token;
}
You do not take a copy of the string, only a pointer reference to it. Then the next time you call fgets the string is overwritten, and all the references point to the new string. So, when you get to the end of the program all your pointers point to the last thing that was written to that memory.
Try this:
make[c] = strdup(token);
That will allocate new memory, duplicate the string, and return the new pointer. The copy will not get overwritten. strdup calls malloc, internally, so you should free the memory when you are done with it.
Inside the loop, make[c] = token records an address inside char lines[]. Since that buffer is overwritten on the next iteration, you need to copy your tokens somewhere else to keep them safe.
Just use make[c] = strdup(token) (and the same for year) to do this - but remember in any real program, you should now free all your make and year strings when you're done with them.
You are using the wrong data structure to store your results.
The make array is an array of pointers. This means that all you store there is a memory address, in your case, the memory address of the pointer being used during the strtok operation. This is not what you want, because you don't know what is going to happen with that memory afterwards; in this case, it appears that during each successive call to the function it gets overwritten.
You need to either use a multidimensional array or to use malloc() or strdup() to create a copy of the char * value as returned by strtok().
strtok operates on a static buffer
when you write
strtok(lines, "\t");
the lines buffer is copied to an internal static buffer - and strtok has only one static buffer - so every time your while loop iterates, new content is placed intothe static buffer invalidating any previous pointers i.e. make[] and year[].
in order to preserve the tokens you need to allocate separate memory for them which is not destroyed at the end of the loop. this can be done in several ways like using strdup on each string like mentioned by other posters although there would be a lot of bookkeeping then freeing the meory. you could also use another data structure, e.g. a linked list to store the values instead of having lots of pointers:
typedef struct record
{
char make[32]; // lets say the max length of make is 32
char year[10];
struct record* next;
} record;
whenever you read a new line from the file create a new record:
record* first = NULL;
record* last = NULL;
while (fgets(lines, sizeof(lines), fp) != NULL)
{
// first create the record
record* newRecord = malloc(sizeof(record));
newRecord->next = NULL;
// parse the line
int column = 0;
for (char* token = strtok(lines,"\t"); token != NULL; token = strtok(NULL, "\t"), ++column)
{
switch(column)
{
case 2:
strncpy(newRecord->make, token, sizeof(newRecord.make));
break;
case 4:
strncpy(newRecord->year, token, sizeof(newRecord.year));
break;
default: // ignore
break;
}
}
// put in the list
if (first == NULL)
{
first = last = newRecord;
}
else
{
last->next = newRecord;
last = newRecord;
}
}
now you have the parts you are interested in a list. you can print out the list with
for (record* rec = first; rec != NULL; rec = rec->next)
{
printf( "%s, %s\n", rec->make, rec->year );
}
// disclaimer, haven't compiled this.

Bus Error on void function return

I'm learning to use libcurl in C. To start, I'm using a randomized list of accession names to search for protein sequence files that may be found hosted here. These follow a set format where the first line is a variable length (but which contains no information I'm trying to query) then a series of capitalized letters with a new line every sixty (60) characters (what I want to pull down, but reformat to eighty (80) characters per line).
I have the call itself in a single function:
//finds and saves the fastas for each protein (assuming on exists)
void pullFasta (proteinEntry *entry, char matchType, FILE *outFile) {
//Local variables
URL_FILE *handle;
char buffer[2] = "", url[32] = "http://www.uniprot.org/uniprot/", sequence[2] = "";
//Build full URL
/*printf ("u:%s\nt:%s\n", url, entry->title); /*This line was used for debugging.*/
strcat (url, entry->title);
strcat (url, ".fasta");
//Open URL
/*printf ("u:%s\n", url); /*This line was used for debugging.*/
handle = url_fopen (url, "r");
//If there is data there
if (handle != NULL) {
//Skip the first line as it's got useless info
do {
url_fread(buffer, 1, 1, handle);
} while (buffer[0] != '\n');
//Grab the fasta data, skipping newline characters
while (!url_feof (handle)) {
url_fread(buffer, 1, 1, handle);
if (buffer[0] != '\n') {
strcat (sequence, buffer);
}
}
//Print it
printFastaEntry (entry->title, sequence, matchType, outFile);
}
url_fclose (handle);
return;
}
With proteinEntry being defined as:
//Entry for fasta formatable data
typedef struct proteinEntry {
char title[7];
struct proteinEntry *next;
} proteinEntry;
And the url_fopen, url_fclose, url_feof, url_read, and URL_FILE code found here, they mimic the file functions for which they are named.
As you can see I've been doing some debugging with the URL generator (uniprot URLs follow the same format for different proteins), I got it working properly and can pull down the data from the site and save it to file in the proper format that I want. I set the read buffer to 1 because I wanted to get a program that was very simplistic but functional (if inelegant) before I start playing with things, so I would have a base to return to as I learned.
I've tested the url_<function> calls and they are giving no errors. So I added incremental printf calls after each line to identify exactly where the bus error is occurring and it is happening at return;.
My understanding of bus errors is that it's a memory access issue wherein I'm trying to get at memory that my program doesn't have control over. My confusion comes from the fact that this is happening at the return of a void function. There's nothing being read, written, or passed to trigger the memory error (as far as I understand it, at least).
Can anyone point me in the right direction to fix my mistake please?
EDIT: As #BLUEPIXY pointed out I had a potential url_fclose (NULL). As #deltheil pointed out I had sequence as a static array. This also made me notice I'm repeating my bad memory allocation for url, so I updated it and it now works. Thanks for your help!
If we look at e.g http://www.uniprot.org/uniprot/Q6GZX1.fasta and skip the first line (as you do) we have:
MNAKYDTDQGVGRMLFLGTIGLAVVVGGLMAYGYYYDGKTPSSGTSFHTASPSFSSRYRY
Which is a 60 characters string.
When you try to read this sequence with:
//Grab the fasta data, skipping newline characters
while (!url_feof (handle)) {
url_fread(buffer, 1, 1, handle);
if (buffer[0] != '\n') {
strcat (sequence, buffer);
}
}
The problem is sequence is not expandable and not large enough (it is a fixed length array of size 2).
So make sure to choose a large enough size to hold any sequence, or implement the ability to expand it on-the-fly.

Filter text from huge .csv files, in C

I have the raw and unfiltered records in a csv file (more than 1000000 records), and I am suppose to filter out those records from a list of files (each weighing more than 282MB; approx. more than 2000000 records). I tried using strstr in C. This is my code:
while (!feof(rawfh)) //loop to read records from raw file
{
j=0; //counter
while( (c = fgetc(rawfh))!='\n' && !feof(rawfh)) //read a line from raw file
{
line[j] = c; line[j+1] = '\0'; j++;
}
//function to extract the element in the specified column, in the CSV
extractcol(line, relcolraw, entry);
printf("\nWorking on : %s", entry);
found=0;
//read a set of 4000 bytes; this is the target file
while( fgets(buffer, 4000, dncfh)!=NULL && !found )
{
if( strstr(buffer, entry) !=NULL) //compare it
found++;
}
rewind(dncfh); //put the file pointer back to the start
// if the record was not found in the target list, write it into another file
if(!found)
{
fprintf(out, "%s,\n", entry); printf(" *** written to filtered ***");
}
else
{
found=0; printf(" *** Found ***");
}
//I hope this is the right way to null out a string
entry[0] = '\0'; line[0] ='\0';
//just to display a # on the screen, to let the user know that the program
//is still alive and running.
rawreccntr++;
if(rawreccntr>=10)
{
printf("#"); rawreccntr=0;
}
}
This program takes approximately 7 to 10 seconds, on an average, to search one entry in the target file (282 MB). So, 10*1000000 = 10000000 seconds :( God knows how much is that going to take if I decide to search in 25 files.
I was thinking of writing a program, and not going to spoon fed solutions (grep, sed etc.). OH, sorry, but I am using Windows 8 (64 bit, 4 GB RAM, AMD processor Radeon 2 core - 1000Mhz). I used DevC++ (gcc) to compile this.
Please enlighten me with your ideas.
Thanks in advance, and sorry if I sound stupid.
Update by Ali, the key information extracted from a comment:
I have a raw CSV file with details for customer's phone number and address. I have the target file(s) in CSV format; the Do Not Call list. I am suppose to write a program to filter out phone number that are not present in the Do No Call List. The phone numbers (for both files) are in the 2nd column. I, however, don't know of any other method. I searched for Boyer-Moore algorithm, however, could not implement that in C. Any suggestions about how should I go about searching for records?
EDITED
I would recommend you have a try with the readymade tools in any Unix/Linux system, grep and awk. You'll probably find they are just as fast and much more easily maintained. I haven't seen your data format, but you say the phone numbers are in the second column, so you can get the phone numbers on their own like this:
awk '{print $2}' DontCallFile.csv
If your phone numbers are in double quotes, you can remove those like this:
awk '{print $2}' DontCallFile.csv | tr -d '"'
Then you can use fgrep with the -f option, to search whether strings listed in one file are present in a second file, like this:
fgrep -f file1.csv file2.csv
or you can invert the search and search for strings NOT present in another file, by adding the -v switch to fgrep.
So, your final command would probably end up like this:
fgrep -v -f <(awk '{print $2}' DontCallFile.csv | tr -d '"') file2.csv
That says... search, in file2.csv for all strings not present (-v option) in column 2 of file "DontCallFile.csv". If you want to understand the bit in <() it is called process substitution and it basically makes a pseudo-file out of the result of running the command inside the brackets. And we need a pseudo-file because fgrep -f expects a file.
ORIGINAL ANSWER
Why are you using fgetc() anyway. Surely you would use getline() like this:
while(getline(myfile,line ))
{
...
}
Are you really reading the whole "target" file from the start for every single line in your main file? That will kill you! And why are you doing it in chunks of 4,000 bytes? And what if one of your strings straddles the 4,000 bytes you compare it with - i.e. the first 8 bytes are in one 4k chunk and the last however many bytes are in the nect 4k chunk?
I think you will get better help on here if you take the time to explain properly what you are trying to do - and maybe do it with awk or grep (at least figuratively) so we can see what you are actually trying to achieve. Your decription doesn't mention the "target" file you use in the code, for example.
You can do this with awk, like this:
awk -F, '
FNR==NR {gsub(/"/,"",$2);dcn[$2]++;next}
{gsub(/ /,"",$2);if(!dcn[$2])print}
' DontCallFile.csv x.csv
That says... the field separator is a comma (-F,). Now read the first file (DontCallFile.csv) and process according to the part in curly braces after FNR==NR. Remove the double quotes from around the phone number in field 2, using gsub (global substitution). Then increment the element in the associative array (i.e. hash) as indexed by unquoted field 2 and then move to next record. So basically, after file "DontCallFile.csv" is processed, the array dcn[] will hold a hash of all the numbers not to call (dcn=dontcallnumbers). Then, the code in the second set of curly braces is executed for each line of the second file ("x.csv"). That says... remove all spaces from around the phone number in field 2. Then, if that phone number is not present in the array dcn[] that we built earlier, print the line.
Here is one idea for improvement...
In the code below, what's the point in setting line[j+1] = '\0' at every iteration?
while( (c = fgetc(rawfh))!='\n' && !feof(rawfh))
{
line[j] = c; line[j+1] = '\0'; j++;
}
You might as well do it outside the loop:
while( (c = fgetc(rawfh))!='\n' && !feof(rawfh))
line[j++] = c;
line[j] = '\0';
My advice is the following.
Put all don't call phone numbers into an array.
Sort this array.
Use binary search to check if a given phone number is among the sorted
don't call numbers.
In the code below, I just hard-coded the numbers. In your application, you will have to replace that with the corresponding code.
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int compare(const void* a, const void* b) {
return (strcmp(*(char **)a, *(char **)b));
}
int binary_search(const char** first, const char** last, const char* val) {
ptrdiff_t len = last - first;
while (len > 0) {
ptrdiff_t half = len >> 1;
const char** middle = first;
middle += half;
if (compare(&*middle, &val)) {
first = middle;
++first;
len = len - half - 1;
}
else
len = half;
}
return first != last && !compare(&val,&*first);
}
int main(int argc, char** argv) {
size_t i;
/* Read _all_ of your don't call phone numbers into an array. */
/* For the sake of the example, I just hard-coded it. */
char* dont_call[] = { "908-444-555", "800-200-400", "987-654-321" };
/* in your program, change length to the number of dont_call numbers actually read. */
size_t length = sizeof dont_call / sizeof dont_call[0];
qsort(dont_call, length, sizeof(char *), compare);
printf("The don\'t call numbers sorted\n");
for (i=0; i<length; ++i)
printf("%lu %s\n", i, dont_call[i]);
/* For each phone number, check if it is in the sorted dont_call list. */
/* Use binary search to check it. */
char* numbers[] = { "999-000-111", "333-444-555", "987-654-321" };
size_t n = sizeof numbers / sizeof numbers[0];
printf("Now checking if we should call a given number\n");
for (i=0; i<n; ++i) {
int should_call = binary_search((const char **)dont_call, (const char **)dont_call+length, numbers[i]);
char* as_text = should_call ? "no" : "yes";
printf("Should we call %s? %s\n",numbers[i], as_text);
}
return 0;
}
This prints:
The don't call numbers sorted
0 800-200-400
1 908-444-555
2 987-654-321
Now checking if we should call a given number
Should we call 999-000-111? yes
Should we call 333-444-555? yes
Should we call 987-654-321? no
The code is definitely not perfect but it is sufficient to get you started.
The problem with your algorithm is complexity. You approach is O(n*m) where n is number of customers and m is number of do_not_call records (or size of file in your case). You need reduce this complexity. (And Boyer-Moore algorithm would not help there which suggested by Ali. It would not improve asymptotic complexity but only constant.) Even binary search as Ali suggest in his answer is not best. It would be O((n+m)*log m). We can do better. Nice solutions are using fgrep and awk as suggested by Mark Setchell in his answers. (I would chose one using fgrep which should perform better I guess but it is only guess.) I can provide one similar solution in Perl which will provide more robust CSV parsing and should handle your data sizes in easy on decent HW. This type of solutions has complexity O(n+m).
#!/usr/bin/env perl
use strict;
use warnings;
use autodie;
use Text::CSV_XS;
use constant PHN_COL_DNC => 1;
use constant PHN_COL_CUSTOMERS => 1;
die "Usage: $0 dnc_file [customers]" unless #ARGV>0;
my $dncfile = shift #ARGV;
my $csv = Text::CSV_XS->new({eol=>"\n", allow_whitespace=>1, binary=>1});
my %dnc;
open my $dnc, '<', $dncfile;
while(my $row = $csv->getline($dnc)){
$dnc{$row->[PHN_COL_DNC]} = undef;
}
close $dnc;
while(my $row = $csv->getline(*ARGV)){
$csv->print(*STDOUT, $row) unless exists $dnc{$row->[PHN_COL_CUSTOMERS]};
}
If it would not meet our performance expectation you can go down to C road but I would definitely recommend use some good csv parsing and hashmap libraries. I would try libcsv and khash.h

Why does my program read an extra structure?

I'm making a small console-based rpg, to brush up on my programming skills.
I am using structures to store character data. Things like their HP, Strength, perhaps Inventory down the road. One of the key things I need to be able to do is load and save characters. Which means reading and saving structures.
Right now I'm just saving and loading a structure with first name and last name, and attempting to read it properly.
Here is my code for creating a character:
void createCharacter()
{
char namebuf[20];
printf("First Name:");
if (NULL != fgets(namebuf, 20, stdin))
{
char *nlptr = strchr(namebuf, '\n');
if (nlptr) *nlptr = '\0';
}
strcpy(party[nMember].fname,namebuf);
printf("Last Name:");
if (NULL != fgets(namebuf, 20, stdin))
{
char *nlptr = strchr(namebuf, '\n');
if (nlptr) *nlptr = '\0';
}
strcpy(party[nMember].lname,namebuf);
/*Character created, now save */
saveCharacter(party[nMember]);
printf("\n\n");
loadCharacter();
}
And here is the saveCharacter function:
void saveCharacter(character party)
{
FILE *fp;
fp = fopen("data","a");
fwrite(&party,sizeof(party),1,fp);
fclose(fp);
}
and the loadCharacter function
void loadCharacter()
{
FILE *fp;
character tempParty[50];
int loop = 0;
int count = 1;
int read = 2;
fp= fopen("data","r");
while(read != 0)
{
read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
printf("%d. %s %s\n",count,tempParty[loop].fname,tempParty[loop].lname);
loop++;
count++;
}
fclose(fp);
}
So the expected result of the program is that I input a name and last name such as 'John Doe', and it gets appended to the data file. Then it is read in, maybe something like
1. Jane Doe
2. John Doe
and the program ends.
However, my output seems to add one more blank structure to the end.
1. Jane Doe
2. John Doe
3.
I'd like to know why this is. Keep in mind I'm reading the file until fread returns a 0 to signify it's hit the EOF.
Thanks :)
Change your loop:
while( fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp) )
{
// other stuff
}
Whenever you write file reading code ask yourself this question - "what happens if I read an empty file?"
You have an algorithmic problem in your loop, change it to:
read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
while(read != 0)
{
//read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
printf("%d. %s %s\n",count,tempParty[loop].fname,tempParty[loop].lname);
loop++;
count++;
read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
}
There are ways to ged rid of the double fread but first get it working and make sure you understand the flow.
Here:
read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
printf("%d. %s %s\n",count,tempParty[loop].fname,tempParty[loop].lname);
You are not checking whether the read was successful (the return value of fread()).
while( 1==fread(&tempParty[loop],sizeof*tempParty,1,fp) )
{
/* do anything */
}
is the correct way.
use fopen("data","rb")
instead of fopen("data","r") which is equivalent to fopen("data","rt")
You've got the answer to your immediate question but it's worth pointing out that blindly writing and reading whole structures is not a good plan.
Structure layouts can and do change depending on the compiler you use, the version of that compiler and even with the exact compiler flags used. Any change here will break your ability to read files saved with a different version.
If you have ambitions of supporting multiple platforms issues like endianness also come into play.
And then there's what happens if you add elements to your structure in later versions ...
For robustness you need to think about defining your file format independently of your code and having your save and load functions handle serialising and de-serialising to and from this format.

Resources