Adding exam part 1/4

This commit is contained in:
Christopher Sanden
2025-11-03 21:30:23 +01:00
parent d6e494cf1c
commit a8006be05f
95 changed files with 12245 additions and 25 deletions

View File

@@ -0,0 +1,32 @@
# --- Step 1: Create the Library ---
# Define a library target named "SharedLib".
# We use STATIC because we are using cpp and header files.
add_library(SharedLib STATIC)
# --- Step 2: Add Header Files to the Library ---
# This command explicitly lists the header files that belong to the library.
# This helps Visual Studio display them nicely in the Solution Explorer.
target_sources(SharedLib
PUBLIC
# You can add more functionalty to SharedLib.h just by adding more definitions in SharedLib.h.
SharedLib.h
# Or add other shared files here
PRIVATE
ReadNames.cpp
ReadGraph.cpp
ReadSongs.cpp
FileReaderUtils.cpp
)
# --- Step 3: Make Headers "Findable" ---
# This is the most important command here.
# It tells any other project that links to "SharedLib" to add this
# directory (CMAKE_CURRENT_SOURCE_DIR) to its list of include paths.
# This is what allows you to write #include "list.hpp" in your main.cpp.
# Note: CMAKE_CURRENT_SOURCE_DIR is a built-in variable that points to the directory
target_include_directories(SharedLib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -0,0 +1,32 @@
#include "FileReaderUtils.h"
#include "FileReaderUtils.h"
int GetRecordCount(const std::string& aHeaderLine)
{
size_t recordPos = aHeaderLine.find("records:=");
if (recordPos == std::string::npos)
{
return 0; // No record count found
}
size_t countStart = recordPos + 9; // Length of "records:="
// Find the end bracket ']' or a potential semicolon ';'
size_t countEnd = aHeaderLine.find_first_of("];", countStart);
if (countEnd == std::string::npos)
{
return 0; // Malformed header
}
std::string countStr = aHeaderLine.substr(countStart, countEnd - countStart);
try
{
// stoi = string to integer
return std::stoi(countStr);
}
catch (const std::exception&)
{
return 0; // Malformed number
}
}

View File

@@ -0,0 +1,13 @@
// FileReaderUtils.h
#pragma once
#if !defined(FILEREADERUTILS_H)
#define FILEREADERUTILS_H
#include <string>
/**
* @brief [Internal] Safely parses the "records:=N" part of a header line.
* @param aHeaderLine The line, e.g., "[NODES;records:=11]"
* @return The number of records, or 0 if not found.
*/
int GetRecordCount(const std::string& aHeaderLine);
#endif // FILEREADERUTILS_H

View File

@@ -0,0 +1,109 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include "SharedLib.h"
#include "FileReaderUtils.h"
// --- Enum for the parser's state ---
enum class EParseState
{
NONE,
NODES,
EDGES
};
void readGraphFromFile(const std::string& aFilename, FNodeRead aOnNodeRead, FEdgeRead aOnEdgeRead)
{
if (aFilename.empty()) return;
std::ifstream file(aFilename);
if (!file.is_open())
{
// Optional: print an error
// std::cerr << "Error: Could not open file " << aFilename << std::endl;
return;
}
std::string line;
EParseState currentState = EParseState::NONE;
int totalCount = 0;
int currentIndex = 0;
bool keepReading = true;
while (keepReading && std::getline(file, line))
{
if (line.empty()) continue;
if (line[0] == '[')
{
// --- 2. USE THE SHARED FUNCTION ---
totalCount = GetRecordCount(line);
currentIndex = 0;
if (line.find("[NODES") != std::string::npos)
{
currentState = EParseState::NODES;
continue;
}
else if (line.find("[EDGES") != std::string::npos)
{
currentState = EParseState::EDGES;
continue;
}
// If it's a comment or other header, reset state and count
currentState = EParseState::NONE;
totalCount = 0;
continue;
}
// Process data based on the current state
switch (currentState)
{
case EParseState::NODES:
if (aOnNodeRead)
{
if (!aOnNodeRead(currentIndex, totalCount, line))
{
keepReading = false;
}
currentIndex++;
}
break;
case EParseState::EDGES:
{
std::istringstream edgeStream(line);
std::string fromNode, toNode, weightStr;
if (std::getline(edgeStream, fromNode, ';') &&
std::getline(edgeStream, toNode, ';') &&
std::getline(edgeStream, weightStr))
{
try
{
// Use std::stof (string to float) for weight
float weight = std::stof(weightStr);
if (aOnEdgeRead)
{
if (!aOnEdgeRead(currentIndex, totalCount, fromNode, toNode, weight))
{
keepReading = false;
}
currentIndex++;
}
}
catch (const std::exception&)
{
// Failed to parse float, skip this line
}
}
break;
}
case EParseState::NONE:
default:
break;
}
}
file.close();
}

View File

@@ -0,0 +1,55 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include "SharedLib.h"
#include "FileReaderUtils.h"
void readNamesFromFile(const std::string& aFilename, FNameRead aOnNameRead)
{
if (aFilename.empty()) return;
std::ifstream file(aFilename);
if (!file.is_open())
{
std::cerr << "Error: Could not open file " << aFilename << std::endl;
return;
}
std::string line;
int totalCount = 0;
int currentIndex = 0;
bool keepReading = true;
// --- 1. Read the header line ---
if (std::getline(file, line))
{
// Use our shared helper to get the count
totalCount = GetRecordCount(line);
}
// --- 2. Loop through the rest of the file ---
while (keepReading && std::getline(file, line))
{
if (line.empty()) continue;
std::istringstream nameStream(line);
std::string firstName, lastName;
// Parse "FirstName LastName"
if (nameStream >> firstName >> lastName)
{
if (aOnNameRead)
{
// Call the callback with all parameters
if (!aOnNameRead(currentIndex, totalCount, firstName, lastName))
{
keepReading = false;
}
currentIndex++;
}
}
}
file.close();
}

View File

@@ -0,0 +1,60 @@
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include "SharedLib.h"
#include "FileReaderUtils.h" // Include the shared utility
void ReadSongsFromFile(const std::string& aFilename, FSongRead aOnSongRead)
{
if (aFilename.empty()) return;
std::ifstream file(aFilename);
if (!file.is_open())
{
// std::cerr << "Error: Could not open file " << aFilename << std::endl;
return;
}
std::string line;
int totalCount = 0;
int currentIndex = 0;
bool keepReading = true;
// --- 1. Read the header line ---
if (std::getline(file, line))
{
// Use our shared helper to get the count
totalCount = GetRecordCount(line);
}
// --- 2. Loop through the rest of the file (the data lines) ---
while (keepReading && std::getline(file, line))
{
if (line.empty()) continue; // Skip empty lines
std::istringstream lineStream(line);
std::string artist, title, year, genre, source;
// Parse the five semicolon-separated fields
// Artist;Title;Year;Genre;Source
if (std::getline(lineStream, artist, ';') &&
std::getline(lineStream, title, ';') &&
std::getline(lineStream, year, ';') &&
std::getline(lineStream, genre, ';') &&
std::getline(lineStream, source)) // Last one reads to end of line
{
if (aOnSongRead)
{
// Call the callback with all parameters
if (!aOnSongRead(currentIndex, totalCount, artist, title, year, genre, source))
{
keepReading = false;
}
currentIndex++;
}
}
}
file.close();
}

100
Exam/SharedLib/SharedLib.h Normal file
View File

@@ -0,0 +1,100 @@
#pragma once
#ifndef SHARED_LIB_H
#define SHARED_LIB_H
#include <string>
#include <functional>
/// <summary>
/// Delegate type for processing a name read from a file.
/// </summary>
/// <param name="aIndex">The index of the name (0-based).</param>
/// <param name="aTotalCount">The total number of names.</param>
/// <param name="aFirstName">The first name read from the file.</param>
/// <param name="aLastName">The last name read from the file.</param>
/// <returns>Returns true to continue reading, false to stop.</returns>
typedef bool (*FNameRead)(
const int aIndex,
const int aTotalCount,
const std::string& aFirstName,
const std::string& aLastName
);
/// <summary>
/// Use this function to read names from a specified file and process them using a callback function.
/// </summary>
/// <function>readNamesFromFile</function>
/// <description>Reads names from a specified file and invokes a callback for each name read.</description>
/// <param name="aFilename">The path to the file containing names.</param>
/// <param name="aOnNameRead">A callback function that is called for each name read. It takes two parameters: firstName and lastName. If the callback returns false, the reading process stops.</param>
/// <param name="firstName">The first name read from the file.</param>
/// <param name="lastName">The last name read from the file.</param>
/// <returns>None.</returns>
void readNamesFromFile(const std::string& aFilename, FNameRead aOnNameRead);
/// <summary>
/// Delegate type for processing a node read from the file.
/// </summary>
/// <description>Function pointer type for a callback that processes nodes read from a file.</description>
/// <param name="aIndex">The index of the node (0-based).</param>
/// <param name="aTotalCount">The total number of nodes.</param>
/// <param name="aNode">The node std::string.</param>
/// <returns>Returns true to continue reading, false to stop.</returns>
typedef bool (*FNodeRead)(const int aIndex, const int aTotalCount, const std::string& aNode);
/// <summary>
/// Delegate type for processing an edge read from the file.
/// </summary>
/// <description>Function pointer type for a callback that processes edges read from a file.</description>
/// <param name="aIndex">The index of the edge (0-based).</param>
/// <param name="aTotalCount">The total number of edges.</param>
/// <param name="aFromNode">The from node std::string.</param>
/// <param name="aToNode">The to node std::string.</param>
/// <param name="aWeight">The weight of the edge.</param>
/// <returns>Returns true to continue reading, false to stop.</returns>
typedef bool (*FEdgeRead)(const int aIndex, const int aTotalCount, const std::string& aFromNode, const std::string& aToNode, float aWeight);
/// </summary>
/// Use this function to read a graph from a specified file and process its nodes and edges using callback functions.
/// </summary>
/// <function>readGraphFromFile</function>
/// <description>
/// Reads a graph from a specified file and invokes callbacks for each node and edge read.
/// All nodes are read first, followed by edges.
/// </description>
/// <param name="aFilename">The path to the file containing the graph data.</param>
/// <param name="aOnNodeRead">A callback function that is called for each node read. It takes one parameter: the node std::string. If the callback returns false, the reading process stops.</param>
/// <param name="aOnEdgeRead">A callback function that is called for each edge read. It takes three parameters: the fromNode std::string, the toNode std::string, and the weight float. If the callback returns false, the reading process stops.</param>
void readGraphFromFile(const std::string& aFilename, FNodeRead aOnNodeRead, FEdgeRead aOnEdgeRead);
/// <summary>
/// Delegate type for processing a song read from the file.
/// </summary>
/// <param name="aIndex">The index of the song (0-based).</param>
/// <param name="aTotalCount">The total number of songs.</param>
/// <param name="aArtist">The artist.</param>
/// <param name="aTitle">The title.</param>
/// <param name="aYear">The release year (as a std::string).</param>
/// <param name="aGenre">The genre.</param>
/// <param name="aSource">The source.</param>
/// <returns>Returns true to continue reading, false to stop.</returns>
typedef bool (*FSongRead)(
const int aIndex,
const int aTotalCount,
const std::string& aArtist,
const std::string& aTitle,
const std::string& aYear,
const std::string& aGenre,
const std::string& aSource
);
/// <summary>
/// Reads song data from a file and processes them using a callback.
/// This function automatically skips the "records:=" header.
/// </summary>
/// <param name="aFilename">The path to the file (e.g., "songs.txt").</param>
/// <param name="aOnSongRead">The callback function called for each song.</param>
void ReadSongsFromFile(const std::string& aFilename, FSongRead aOnSongRead);
#endif // SHARED_LIB_H