
Get Next Line #42 #1337
mrmo7ox
April 30, 2025
Introduction The get-next-line repository is a project from the renowned 42 curriculum. It is designed to implement a function that reads and returns a single line from a file descriptor. The project showcases the importance of memory management, algorithmic efficiency, and modular design in C programming.
Purpose and Goals
The get-next-line
function addresses the challenge of reading files line by line in a resource-efficient manner. Unlike reading the entire file into memory, this approach ensures scalability and suitability for large files or systems with limited resources.
Key Features:
Line-by-Line Reading: Reads one line at a time from the file, ensuring low memory consumption.
Multiple File Descriptor Support: Simultaneously handles multiple file descriptors, enabling parallel operations.
Efficiency: Utilizes a static variable to maintain state across function calls.
Code Structure
├── get_next_line.c # Core function implementation ├── get_next_line.h # Header file containing function declarations ├── get_next_line_utils.c # Utility functions supporting the main logic ├── get_next_line_bonus.c # Extended functionality for bonus tasks ├── get_next_line_bonus.h # Corresponding header file for bonus features
Core Function: get_next_line.h
#ifndef GET_NEXT_LINE_H # define GET_NEXT_LINE_H # include <stdlib.h> # include <limits.h> # include <unistd.h> # ifndef BUFFER_SIZE # define BUFFER_SIZE 42 # endif size_t ft_strlen(const char *s); char *ft_strjoin(char const *s1, char const *s2); void *ft_memcpy(void *dst, const void *src, size_t n); char *ft_strchr(const char *s, int c); char *ft_strdup(const char *source); void *ft_calloc(size_t number, size_t size); char *ft_substr(char const *s, unsigned int start, size_t len); char *get_next_line(int fd); #endif
as u can see i imported all the libs that i need and one more new thing i defined a const variable and i protract it using the #ifndef NAME
# ifndef BUFFER_SIZE # define BUFFER_SIZE 42 # endif
is help me if someone define the BUFFER_SIZE
on the compilation will not overwrite it
gcc -Wall -Wextra -Werror -D BUFFER_SIZE=42 -o my_program get_next_line.c get_next_line_utils.c main.c
by the -D BUFFER_SIZE=42
u can define a const into to the compilation
char *get_next_line(int fd) { static char *checkpoint; char *line; int len; char *tmp; if (fd < 0 || BUFFER_SIZE <= 0) return (NULL); if (!ft_strchr(checkpoint, '\n')) checkpoint = ft_read_until_newline(fd, checkpoint); if (!checkpoint) return (NULL); len = 0; while (checkpoint[len] != '\n' && checkpoint[len] != '\0') len++; if (checkpoint[len] == '\n') len++; line = ft_substr(checkpoint, 0, len); if (!line) return (free(checkpoint), checkpoint = NULL, NULL); tmp = checkpoint; checkpoint = ft_substr(checkpoint, len, (ft_strlen(checkpoint) - len)); free(tmp); return (line); }
let`s now explain how we can start:
first we will need to protect the function from a negative number like -1 but why exactly -1
when we open a file using the open function if it failed it will return a -1 as u can see in the open
description.
RETURN VALUE On success, open(), openat(), and creat() return the new file descriptor (a nonnegative integer). On error, -1 is returned and errno is set to indicate the error.
but in the case of success it return a file descriptor
?
what a file descriptor:
A file descriptor is a small, non-negative integer (like 0
, 1
, 2
, etc.) that your operating system (OS) uses to identify and manage open files or resources. Think of it as a “ticket number” for each file or resource your program interacts with.
Example:
When you open a file in a program, the OS gives you a file descriptor (e.g., 3
), which you use to refer to that file
Similarly, standard input (stdin
), standard output (stdout
),
and standard error (stderr
) have file descriptors 0
, 1
, and 2
, respectively.
Where is a File Descriptor Stored?
File descriptors are stored in a file descriptor table, which is part of the process’s memory space in the operating system.
How it works:
Each process (running program) has its own file descriptor table.
The table maps file descriptors (like 3
, 4
, etc.) to the actual open files or resources in the OS.
What is an Inode?
An inode (short for “index node”) is a data structure used by the file system to store information about a file.
What it contains:
File metadata: Size, permissions, timestamps, etc.
Pointers to the file’s data blocks on the disk.
Important: The inode does not store the file’s name—it only stores the file’s location and attributes.
Why it matters:
When a file is opened, the OS looks up the file’s inode to find its data on the disk. T
he file descriptor is then created to reference that open file.
if (fd < 0 || BUFFER_SIZE <= 0) return (NULL); if (!ft_strchr(checkpoint, '\n')) checkpoint = ft_read_until_newline(fd, checkpoint);
now u know way i protect from these so if there is no new line the current data we will get some data using the functions ft_read_until_newline(fd, checkpoint)
static char *ft_read_until_newline(int fd, char *checkpoint) { char *next; ssize_t read_bytes; next = NULL; while (!ft_strchr(checkpoint, '\n')) { next = malloc((size_t)BUFFER_SIZE + 1); if (!next) return (free(checkpoint), checkpoint = NULL, NULL); read_bytes = read(fd, next, BUFFER_SIZE); if (read_bytes <= 0) { free (next); if (read_bytes < 0) return (free(checkpoint), checkpoint = NULL, NULL); break ; } checkpoint = ft_join(checkpoint, next, read_bytes); if (checkpoint == NULL) return (free(next), NULL); free(next); next = NULL; } return (checkpoint); }
OK now let us see how read so first as u know we need to send the file descriptor to the reading function and static variable that we will add data to it that is checkpoint
to read i will make to variables that i will need trow my while first the next that will be temp holder for the reading data and a ssize_t read_bytes that will hold my total length of the read data
for the ssize_t is the a type that has the range of size_t and -1 so all the positive numbers but will -1 included
Loop:
-
-
-
-
- first i allocate a next buffer with the predefined size and i check if the allocation is correct, if not the will free and set the checkpoint variable to be null
- now i read using the read function that will need the buffer to past the output in , fd of the file that will be reading from and length that will will read with, if the operation is a success the read function will return how much it read from the file
- that way we catch the return to check if there something in left on the file and if there is a problem with read or the file descriptor because if it return -1 there a problem so i need to free the next and free, set the variable next to null and break from the loop
- if every thing is right we will call the ft_join() that joins the two strings together
-
static char *ft_join(char *checkpoint, char *next, int read_bytes) { char *tmp; next[read_bytes] = '\0'; tmp = checkpoint; checkpoint = ft_strjoin(checkpoint, next); free(tmp); return (checkpoint); }
as u can see the variable read_bytes is a really important one because we will need it again to make the end of the string of the next
- i create a tmp variable that will help me not lose the address of the checkpoint that i need to free as u can in the figure
- we join the data and we free the tmp
- if we go back to the code u can see that i check the checkpoint variable if null to return that will me that the checkpoint has nothing and we will need to free next and return to the older function to deliver the result
- but if we have a success loop we will free the next to loop again until we find \n or the read function return 0 or -1
-
-
-
GO BACK
if (!checkpoint) return (NULL); len = 0; while (checkpoint[len] != '\n' && checkpoint[len] != '\0') len++; if (checkpoint[len] == '\n') len++; line = ft_substr(checkpoint, 0, len); if (!line) return (free(checkpoint), checkpoint = NULL, NULL); tmp = checkpoint; checkpoint = ft_substr(checkpoint, len, (ft_strlen(checkpoint) - len)); free(tmp); return (line);
-
-
-
-
- now to after the reading part first we check if the result is there if yes to go if not to return a null
- after that we will need to find the \n in the data that we read by looping on the string and compering each character on the string to ‘\n’
- then will will have a variable that as the length of the string from the beginning ‘0’ to the ‘\n’ character
- and i add a check of the \n if it is the current character on the string to add +1 to the length variable
- all this for the substr function that need the start and end of the string that u are tying to get, first i will copy from 0 to the length that i found as a result to be return
- and a after that i will need to modify the new checkpoint to start form the length to the rest of the string using substr again
- i will do the same method again of hold the previous address and free it using a tmp variable
-
-
-
BONUS
char *get_next_line(int fd) { static char *checkpoint[OPEN_MAX]; char *line; int len; char *tmp; if (fd < 0 || BUFFER_SIZE <= 0) return (NULL); if (!ft_strchr(checkpoint[fd], '\n')) checkpoint[fd] = ft_read_until_newline(fd, checkpoint[fd]); if (!checkpoint[fd]) return (NULL); len = 0; while (checkpoint[fd][len] != '\n' && checkpoint[fd][len] != '\0') len++; if (checkpoint[fd][len] == '\n') len++; line = ft_substr(checkpoint[fd], 0, len); if (!line) return (free(checkpoint[fd]), checkpoint[fd] = NULL, NULL); tmp = checkpoint[fd]; checkpoint[fd] = ft_substr(checkpoint[fd], len, (ft_strlen(checkpoint[fd]) - len)); free(tmp); return (line); }
-
-
- the only thing that u will change is adding a [open_max] that will make the char * to be an array of the strings like the figure
- so this is how a char ** or *char[] work just a table that u can store string in row based by there index like the figure
- and you need to apply the changes on the checkpoints variables and ur u set to test
-