0

I have a file storing data of students in the following order:

id (space) name (space) address

Below is the content of the file:

10 john manchester

11 sam springfield

12 samuel glasgow

Each data is stored in a newline.

I want to search the student with id 10 and display his/her details using the lseek command, however I'm not to complete the task. Any help is appreciated.

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void main() {

char line[50] = "";

char id[2] = "";

ssize_t fd = open("file.dat", O_RDONLY);

while(read(fd,line,sizeof(line))>0){

if (id[0] == '1' && id[1] == '0'){
    printf("%s\n",line);
}
lseek(fd, 1 ,SEEK_CUR);
}
close(fd);
IrAM
  • 1,720
  • 5
  • 18
  • 1
    I don't see how `lseek` would be of help here. You already have to know the position in the file, so how do you know it? Are the records of fixed size and in known sorted order? `lseek` starts with 0, not 1. – Devolus Feb 15 '21 at 12:08
  • 2
    Maybe you're supposed to read the file, create an index of the positions of all the id numbers, and then use that to seek to the right spot when given an id number to look up? – Shawn Feb 15 '21 at 12:09
  • The only way I see here, is to read the file line by line using `fgets` and `ftell`, storing the indizies in an array, and then you can use `lseek` to position to the given record. – Devolus Feb 15 '21 at 12:12
  • You should probably first understand how to do this with `fseek`. Once you can do it with the higher level abstraction, then move on to `lseek`. – William Pursell Feb 15 '21 at 13:26
  • In your code, how do you expect `id` to be modified? If you are thinking of `id` as a string, it is only large enough to hold a string of length 1 so `10` will not fit. If you are not thinking of it as a string, why are you confusing your reader by initializing it with `""`? – William Pursell Feb 15 '21 at 13:30
  • If the input is ordered, a reasonable approach would be to `lseek` to the middle of the file and start scanning for a newline. When you find a newline, look at the index. If it is correct, you're done. Otherwise, seek to halfway between the current position and the closest previously read location in the correct direction, doing a binary search on the file. The details are a challenge to get right, and there are several pitfalls. It is a good exercise. – William Pursell Feb 15 '21 at 13:55
  • @WilliamPursell Note: `lseek()` is UB for general seeking (as suggested) of text files. – chux - Reinstate Monica Feb 16 '21 at 02:51

2 Answers2

1

Use the right tools for the task. Hammer for nails, Screwdriver for screws.

lseek is not the right tool here, since lseek is for repositioning the file offset (which you do not have yet, you are looking for a specific position, when found, then you don't have a need for repositioning the file offset, since you are already there).

Ask yourself,

What is your task:

  • search for a specific id
  • print the line if match

What do you have:

  • a dataset (textfile) with a fixed format (id <space> name <space> address <newline>)

Your dataset is separated by a newline, and the id is the first field of that row. The keywords here are 'newline' and 'first field'.

The right procedure here would be:

  • read a whole line (fgets)
  • compare the first field (start of line) with the desired id (strcmp)

Example:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main() {
    //return value of main
    int ret = EXIT_FAILURE;

    //open filestream in read mode
    FILE *f = fopen("file.dat", "r");

    //string buffer
    #define MAX_LEN 50
    const char line[MAX_LEN];
    char field[MAX_LEN];

    //the id to search for
    const char *id = "10";

    //for each line
    while (fgets(line, MAX_LEN, f)) {
        //extract the first field ('%s' matches a sequence of non-white-space characters)
        sscanf(line, "%s", field);
        //compare the field with the desired id
        if (strcmp(field, id) == 0) {
            //if found print line
            printf("%s", str);
            //set result to success
            ret = EXIT_SUCCESS;
            //and exit
            break;
        }
    }

    //cleanup
    fclose(f);

    //return the result
    return ret;
}
Erdal Küçük
  • 4,810
  • 1
  • 6
  • 11
0

Your file has a first line that has 18 characters, a second line with the same number of characters, and a third one with one less (17) number of characters.

In case you have a four line in which the name for example makes the number of characters different, they should be appended to the file without any other structure.

Lines are delimited by \n characters, that can appear at any point, so second line starts as soon as just behind the first appearance of the \n char.

For this reason, you don't know the precise position where each line begins, and so you cannot know the exact position where each line begins, as the position of each line is (n + 1) bytes forward from twhere the previous line started, where n is the number of characters you put in the previous line, plus one (for the new line character).

You need an index, which is a file that allows you to get, on a fixed length record, to store the starting positions of each line in the data file. In this way, to read line i, you access the index at position (record_index_size * i), and get the position of the starting point of line i. Then you go to the data file, and position your file pointer to the value obtained from the las calculation, and read that with, for example fgets(3).

To build the index, you need to call ftell() right before each call to fgets(), because the call to fgets() will move the pointer, and so the position obtained will not be correct. Try to write the position in a fixed length format, e.g. binary form, with:

    write(ix_fd, position, sizeof position);

so the position of line i, can be calculated by reading from index at position i * sizeof position.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31