Solved

[C] How to read line number in a text file

Posted on 2008-06-21
15
2,299 Views
Last Modified: 2013-11-15
Hi!

I'd like to let a client to receive the content stored in a text file. In order to do this, i load the content of my text file in a linked list and then i let the server to send back the data to the client.
And this works fine.

My text file is something like this:

15 nick
25 marcus
18 sarah

where each line is composed by an integer (the age) and a string (the username)

I'd like also to let client to ask the server for a value stored at the n line in the file.

i have to use it with a Client/Server application in C, where i'm using TCP concurrence for the server.

My txt file can change and can be updated with new records...

The requests sended by the client are for example:

FIND1230

where 0 means that the command is termined and when the server receive this command it should have to send to the client: (server split the first 4 chars which are the command, and then it has to read each number 1, 2 ,3 and 0)

15 25 18

and then to stop.

Or, if it receive:

FIND2130

it have to send back: 25 15 18

(each number sended by the client is in net short format)

I use a function which load each integer and each string in a linked list, like this:


void load_file_in_list(){
 
 llist = (struct NODE *)malloc(sizeof(struct NODE));
 llist->code = 0;
 llist->next = NULL;
 llist->size = 0;
   
   FILE * pFile;
   char buffer [100];
   int code;
   char name[1000];
     
   pFile = fopen (text_file , "r");
   if (pFile == NULL) perror ("Error opening file");
   else
   {
     while ( fgets (buffer , 100 , pFile) != NULL )
     {
       
         sscanf(buffer,"%d %s \n", &code, name);
         append_node(llist, name, code); /* insert values in the list */
         size++;
        }
         fclose (pFile);
      }

}


and then, if the client sends me a string, i'm able to send back to it the value associated to that string with this function:

int search_value(struct NODE *llist, char *name, int fd) {
 int retval = -1;
 int i = 1;
 int net;
     
  while(llist != NULL) {
  if(strcmp((char*)llist->name, (char*)name) == 0){ /* if there is a result for the request */
      net = htons(llist->code);
      Writeline(fd, &net, 2);             /* send the value to the client */
      llist = llist->next;
    return i;
      }      
 
  else  i++;
  llist = llist->next;
 
 }
 if (i != 1)             /* else htons(0) si returned */
 net = htons(0);
 Writeline(fd, &net, 2);
 return retval;
}

but, there is a way to use a function like the previous which can send back to the server the values associated to the line number stored in the txt file?

something like:
int search_value(struct NODE *llist, int text_line, int fd)

where the text_line is the line number where is stored the value which i want.
Because i'm able to find a string or a value when there are in the linked list, but what i have to do in order to use line number?

Thanks for all!
void load_file_in_list(){

  

 llist = (struct NODE *)malloc(sizeof(struct NODE));

 llist->code = 0;

 llist->next = NULL;

 llist->size = 0;

   

   FILE * pFile;

   char buffer [100];

   int code;

   char name[1000];

      

   pFile = fopen (text_file , "r");

   if (pFile == NULL) perror ("Error opening file");

   else

   {

     while ( fgets (buffer , 100 , pFile) != NULL )

     {

       

	   sscanf(buffer,"%d %s \n", &code, name);

	   append_node(llist, name, code); /* insert values in the list */

	   size++;

	  }

	   fclose (pFile);

      }
 

}
 
 

int search_value(struct NODE *llist, char *name, int fd) {

 int retval = -1;

 int i = 1;

 int net;

     

  while(llist != NULL) {

  if(strcmp((char*)llist->name, (char*)name) == 0){ /* if there is a result for the request */

	net = htons(llist->code);

	Writeline(fd, &net, 2);             /* send the value to the client */

	llist = llist->next;

    return i;

	}	

  

  else  i++;

  llist = llist->next;

 

 }

 if (i != 1)             /* else htons(0) si returned */

 net = htons(0);

 Writeline(fd, &net, 2);

 return retval;

}

Open in new window

0
Comment
Question by:roccogalati
  • 7
  • 6
  • 2
15 Comments
 
LVL 45

Expert Comment

by:sunnycoder
ID: 21837439
There are multiple ways of doing this ...
The easiest and most inefficient would be to read to nth line each time ...
e.g. assuming that your line numbers are stored in an array int lineno[] whose last entry is 0. res[] is the corresponding result array

int lineno[];
int res[];
...
FILE * temp = fopen (myfile, "r");
char buffer[MAX_LENGTH] = { 0 };

for (int i=0; lineno[i]; i++)
{
       for (int j=0; j<i; j++)
             fgets (buffer, MAX_LENGTH, temp);
       res[i] = atoi(buffer); //use strtol for robustness
       fseek(temp, 0, SEEK_SET);
}
fclose (temp);

lineno[] and res[] can be dynamically allocated and resized using malloc/realloc ... Usual error checking code needs to be written. If your file can be altered at any time and you absolutely need the latest info, then you will have to read from file each time ... However, it is possible to optimize the reads by sorting the requests and reading them in increasing order ... Results can be reordered later to match the original order specified in the command.

Next approach is to load your file into an array instead of linked list ... this will allow you to randomly access any line.
llist = (struct NODE *)malloc(sizeof(struct NODE) * capacity);
you would need to maintain a count of total capacity and number of locations used

add( ... )
{
       if (used == capacity)
       {
                 realloc (...); //increase capacity
                 capacity = ...;
       }
        llist[used] = new_element;
        used++;
}

Finally if you are intent on using a linked list,
make a copy of requested lineno array
sort this copy
int init = 0;
for (int i=0; lineno[i]; i++)
{
      //traverse (lineno[i] - init) nodes in linked list
      //store this value in result[]
      //init = lineno[i]
}
0
 
LVL 45

Expert Comment

by:sunnycoder
ID: 21837444
posted a tad bit too soon ... in the last approach, as a final step you need to again reorder res[] as per the original request order in lineno[]
0
 

Author Comment

by:roccogalati
ID: 21837667
i was using a function which i wrote some time ago, which can search for a string in a linked list.
I modified it by adding the text_line information like this:

llist = (struct NODE *)malloc(sizeof(struct NODE));
 llist->code = 0;
 llist->next = NULL;
 llist->size = 0;
 llist->line = 0;  
   
   FILE * pFile;
   char buffer [100];
   int code;
   char name[1000];
 
   
   pFile = fopen (text_file , "r");
   if (pFile == NULL) perror ("Error opening file");
   else
   {
     while ( fgets (buffer , 100 , pFile) != NULL )
     {
       
         sscanf(buffer,"%d %s \n", &code, name);
         line++;
         append_node(llist, name, code, line); /* insert values in the list */
         size++;
        }
         fclose (pFile);
      }


now, for each couple of (integer, string) i have associated a line.
So, if a call line = 2, now it gives me the couple (25, marcus) and so i can find random values, too...

i think it's great...

but i have a problem...

client have to send something like:

FIND123

so i have to read each number and call the search function for each number... but how?

can i use strtok in some way?

because there are no delimiters, i have  just a sequence of numbers after the first 4 chars in the string and i don't know how to read it... mmh...

because i can find a match with the first 4 chars, but then, i dont know how to split the string..

if ( strncmp(comando, "FIND", 4) == 0){

???
while(){
search_value(llist, line_text, fd);
}

}  



my modified search function:
 

int search_value(struct NODE *llist, int text_line, int fd) {

 int retval = -1;

 int i = 1;

 int net;

     

  while(llist != NULL) {

  if(llist->line == text_line){ /* if there is a result for the request */

	net = htons(llist->code);

	Writeline(fd, &net, 2);             /* send the value to the client */

	llist = llist->next;

    return i;

	}	

  

  else  i++;

  llist = llist->next;

 

 }

 if (i != 1)             /* else htons(0) si returned */

 net = htons(0);

 Writeline(fd, &net, 2);

 return retval;

}

Open in new window

0
 
LVL 45

Expert Comment

by:sunnycoder
ID: 21837673
there has to be a delimiter between individual numbers ... With just 123 it is impossible to tell whether it is 1,2,3 or 12,3 or 1,23 or 123. You would have to adapt the command format.
0
 

Author Comment

by:roccogalati
ID: 21837748
but, at the reality, where the server receives FIND followed by some numbers... all these numbers are in network byte order on 16 bit.

So, i think it has to read for each time 16 bit at a time, convert the number and go on until it find a 0 which is the end of command. (Also 0 is in network byte order)

is it possible to read a 16 bit number each time? for example with strtok?

in this way, by reading each time 16bit, there are no problems, and all the number will be read...


0
 
LVL 45

Accepted Solution

by:
sunnycoder earned 500 total points
ID: 21837789
If each of these is a 16 bit number then you can use memcpy to copy 16 bits at a time.

char * temp = buffer;
char arr[4] = { 0 };
memcpy(arr, temp , 2);
//process the 2 bytes copied to arr

In your case this would translate to


char * temp = buffer;

char arr[4] = { 0 };

short val;
 

do 

{

    memcpy(arr, temp, 2);

    temp = temp + 2;

    val = //convert from arr to val

    //save val to your request list/array

}while (val);

Open in new window

0
 

Author Comment

by:roccogalati
ID: 21838032
I thought to do something like what i posted below, but it doesn't work very well.


it doesn't work because it says that arr is a string and val in an integer, and when i run it, it never exit from the while cicle.

I used comando+4 in order to point to the string without FIND... i hope this is correct...

If i write: FIND1

it displays:

test: 27897
test: 63852

and it never exit from the while cicle...

mmh...
 
p.s. why char arr[4] = { 0 }; // what does it means?
 else if(strncmp(comando, "FIND", 4) == 0){    

   if (llist->next == NULL ){

     Writeline(fd, noob, strlen(noob));

   }

   

   else	if (llist ->next != NULL ) {   

  

   

   char * temp = comando+4;

   char arr[4] = { 0 }; // what does it means?

   int val=0;

 

 do 

{

    memcpy(arr, temp, 2);

    temp = temp + 2;   

    val = ntohs(arr);

    printf("test: %u\n", val);

    printf("test: %u\n", htons(val));

   // search_value(llist, val, fd);

} while (val);

Open in new window

0
Maximize Your Threat Intelligence Reporting

Reporting is one of the most important and least talked about aspects of a world-class threat intelligence program. Here’s how to do it right.

 

Author Comment

by:roccogalati
ID: 21838047
may be i found out where is the problem...

i'm testing it...
0
 

Author Comment

by:roccogalati
ID: 21838070
mmmh, no, it doesn't work... :(
0
 

Author Comment

by:roccogalati
ID: 21838106
Actually it works fine!

it convert correctly the values and it is able to find the correct value when a text line is given!

That's great!

thanks a lot!!!

there is just a problem... when i try to compile it by using gcc, it gives me these errors:

Macintosh:server roccogalati$ make
gcc -O -Wall -Werror  -s   server.c   -o server
cc1: warnings being treated as errors
server.c: In function listen_client_requests:
server.c:433: warning: cast from pointer to integer of different size
server.c:433: warning: cast from pointer to integer of different size
server.c:433: warning: passing argument 1 of _OSSwapInt16 makes integer from pointer without a cast
make: *** [server] Error 1

where the line with the problem is:

  val = ntohs(arr);

because arr is a string and val is an unsigned...

but memcpy takes strings as arguments... so i don't know hoe to solve the casting...



else if(strncmp(comando, "COST", 4) == 0){    

   if (auth==0){

   Writeline(fd, noauth, strlen(noauth));

   }

   else {

   if (llist->next == NULL ){

     Writeline(fd, noob, strlen(noob));

   }

   

   else	if (llist ->next != NULL ) {   

  

   

   char * temp = comando+4;

   char arr[4] = { 0 };

   int val = 0;

 

 do 

{

    memcpy(arr, temp, 2);

    temp = temp + 2;

    val = ntohs(arr);  <-- HERE THERE IS THE PROBLEM

    search_value(llist, val, fd); 

} while (val);

Open in new window

0
 
LVL 45

Expert Comment

by:sunnycoder
ID: 21838118
memcpy actually takes a void * as argument ... you can as well pass a pointer to an int or a pointer to a short

short arr;
memcpy (&arr, temp, 2);
0
 
LVL 84

Expert Comment

by:ozo
ID: 21838208
Either declare val as uint16_t
or cast
val = (int) ntohs(arr);
0
 
LVL 84

Expert Comment

by:ozo
ID: 21838220
sorry
arr is  char arr[4]
but ntohs takes uint16_t
did you mean
uint16_t arr[4];
ntohs(arr[0]);
0
 
LVL 45

Expert Comment

by:sunnycoder
ID: 21838225
Using casting, it would probably be
val =  ntohs( *((short *)arr));
0
 

Author Closing Comment

by:roccogalati
ID: 31469404
Thanks, you are great!
thanks for all!
0

Featured Post

Free Gift Card with Acronis Backup Purchase!

Backup any data in any location: local and remote systems, physical and virtual servers, private and public clouds, Macs and PCs, tablets and mobile devices, & more! For limited time only, buy any Acronis backup products and get a FREE Amazon/Best Buy gift card worth up to $200!

Join & Write a Comment

Suggested Solutions

Today companies are subjected to more-and-more data, and it won't stop any time soon.  But there are obvious opportunities for reducing data, particularly data duplicated among companies.
A list of useful business intelligence software.
Video by: Grant
The goal of this video is to provide viewers with basic examples to understand and use nested-loops in the C programming language.
Viewers will learn how to use the Hootsuite Dashboard.

746 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now