Laboratory Overview
What is new in this laboratory.
Laboratory 11 introduced basic file operations and command-line arguments. In this laboratory, you will move from reading a file character by character to treating a text file as a dynamic array of lines.
file โ count lines โ allocate char ** โ load lines โ process lines โ save lines
This is the direct preparation for sorting text files in Laboratory 13. Once the file is represented as an array of strings, you can reuse the sorting knowledge from Laboratories 9 and 10.
Learning Outcomes
- count lines in a text file and use the result for allocation,
- allocate a dynamic array of string pointers,
- allocate memory for each line separately,
- load a text file into memory line by line,
- save an array of strings to a text file,
- free a dynamically allocated array of strings correctly,
- design
text.handtext.c, - build a reusable static library
libtext.a, - prepare reusable code for sorting files in Laboratory 13.
How this laboratory continues Lab 11
The previous lab read files; this lab loads them into memory in a reusable form.
| Laboratory 11 | Laboratory 12 |
|---|---|
| open a file | open, count, load and save a file |
| read characters | read complete lines |
| print or count immediately | store lines in dynamic memory |
| single-purpose programs | reusable text-file library |
ex42 as an optional extra task.
This laboratory continues the main sequence with ex43โex47.
Task 0 โ Reminder and preparation
Step 1 โ Log in to the AGH UNIX server
ssh your_login@student.agh.edu.pl
Step 2 โ Go to the course directory
cd ~/I2PL
Step 3 โ Create the directory for Laboratory 12
mkdir lab12
cd lab12
Step 4 โ Prepare a sample file
cat > countries.txt << EOF
Poland
Germany
Japan
Italy
United Kingdom
United States of America
Portugal
Ukraine
EOF
free.
If allocation fails in the middle of loading a file, release everything that was already allocated.
Task 1 โ Count lines as a reusable function
Step 1 โ Create the file
nano ex43_count_lines_function.c
Step 2 โ Function declaration
int f_lines(const char *name);
Step 3 โ Expected behaviour
- Open the file with the given name.
- Count text lines.
- Count the last non-empty line even if it does not end with
\n. - Return the number of lines.
- Return
-1if the file cannot be opened.
Hint: two useful variables
Use lines to count newline characters and last to remember the last character read.
If the file is not empty and last is not '\n' or '\r', add one more line.
Task 2 โ Load all lines into a dynamic array
Step 1 โ Create the file
nano ex44_load_lines.c
Step 2 โ Function declaration
char **f_load(const char *name, int *size);
Step 3 โ Expected behaviour
- Count the number of lines using
f_lines. - Allocate an array of
char *. - Read each line from the file.
- Allocate exactly enough memory for every line.
- Copy the line into its own allocated block.
- Store the number of loaded lines in
*size. - Return the array, or
NULLon error.
Step 4 โ Suggested reading buffer
char buffer[1024];
while (fgets(buffer, sizeof(buffer), in) != NULL) {
/* allocate memory for this line */
}
buffer directly in the array.
Allocate memory and copy its content.
Hint: allocation for one line
tab[i] = malloc(strlen(buffer) + 1);
if (tab[i] == NULL) {
/* release previously allocated lines */
}
Task 3 โ Save an array of lines to a file
Step 1 โ Create the file
nano ex45_save_lines.c
Step 2 โ Function declaration
int f_save(const char *name, int size, char *tab[]);
Step 3 โ Expected behaviour
- Open output file for writing.
- Write each line from
tab. - Close the file.
- Return the number of successfully written lines.
- Return
-1if the file cannot be opened.
Step 4 โ Basic writing pattern
for (int i = 0; i < size; i++) {
fputs(tab[i], out);
}
f_save writes exactly the strings stored in the array.
If the lines were loaded with fgets, the newline character is usually already part
of each string. In that case, do not add an extra '\n' when saving the file.
Task 4 โ Free an array of lines
Step 1 โ Function declaration
void f_free(int size, char *tab[]);
Step 2 โ Function skeleton
void f_free(int size, char *tab[]) {
if (tab == NULL) {
return;
}
/* free each line */
/* free the array itself */
}
Hint
First release tab[i] for every valid line, then release tab.
Task 5 โ Build text.h, text.c and libtext.a
Step 1 โ Create the header file
nano text.h
Step 2 โ Header file content
#ifndef TEXT_H
#define TEXT_H
int f_lines(const char *name);
char **f_load(const char *name, int *size);
int f_save(const char *name, int size, char *tab[]);
void f_free(int size, char *tab[]);
#endif
Step 3 โ Create implementation file
nano text.c
Move implementations of f_lines, f_load, f_save and f_free into this file.
Step 4 โ Compile and build the library
gcc -Wall -c text.c
ar rcs libtext.a text.o
Task 6 โ Test the text library
Step 1 โ Create the test program
nano ex46_test_text_library.c
Step 2 โ Required usage
./ex46_test_text_library input.txt output.txt
Invalid usage should display:
Usage: ex46_test_text_library input output
Step 3 โ Test workflow
int size = 0;
char **lines = f_load(argv[1], &size);
if (lines == NULL) {
return 1;
}
f_save(argv[2], size, lines);
f_free(size, lines);
Step 4 โ Compile with the library
gcc -Wall ex46_test_text_library.c -L. -ltext -o ex46_test_text_library
Task 7 โ Display loaded lines with line numbers
Step 1 โ Create the file
nano ex47_numbered_lines.c
Step 2 โ Required usage
./ex47_numbered_lines file.txt
Step 3 โ Output example
1: Poland
2: Germany
3: Japan
f_load and f_free. Do not duplicate file-loading code here.
Test Questions โ Review before the next class
Try to answer these questions before opening the answers.
1. Why do we count lines before loading the file?
We need to know how many pointers to allocate in the dynamic array of strings.
2. Why can't we store the address of a local buffer in the array?
The buffer is reused for every line and exists only in the current function scope. Each loaded line must have its own allocated memory.
3. What does char ** represent in this laboratory?
It represents a dynamic array of pointers to strings. Each element points to one loaded line.
4. Why do we need f_free?
The file is loaded into multiple allocated memory blocks: one block for the array of pointers and one block for each line. All of them must be released.
5. Why is this library useful for sorting files?
Sorting needs random access to lines, and an array of strings gives exactly that.
Once the file is loaded into char **, sorting lines becomes similar to sorting strings.
Checklist before submission
ex43_count_lines_function.cimplementsf_lines,ex44_load_lines.cloads a file intochar **,ex45_save_lines.csaves an array of strings into a file,f_freereleases all memory allocated byf_load,text.hcontains include guards and declarations,text.ccontains definitions of reusable functions,libtext.ahas been created,ex46_test_text_library.ctests load/save/free workflow,ex47_numbered_lines.cdisplays loaded lines with line numbers,- all opened files are closed,
- all allocated memory is released correctly.