lab 4 complete

This commit is contained in:
dDogge 2024-12-11 15:54:56 +01:00
parent 2769a3a0ad
commit 69af73e315
12 changed files with 476 additions and 79 deletions

59
lab4/.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,59 @@
{
"files.associations": {
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"*.tcc": "cpp",
"bitset": "cpp",
"cctype": "cpp",
"charconv": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"compare": "cpp",
"concepts": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"cwctype": "cpp",
"deque": "cpp",
"string": "cpp",
"unordered_map": "cpp",
"vector": "cpp",
"exception": "cpp",
"algorithm": "cpp",
"functional": "cpp",
"iterator": "cpp",
"memory": "cpp",
"memory_resource": "cpp",
"numeric": "cpp",
"optional": "cpp",
"random": "cpp",
"regex": "cpp",
"string_view": "cpp",
"system_error": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"utility": "cpp",
"format": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"limits": "cpp",
"new": "cpp",
"numbers": "cpp",
"ostream": "cpp",
"span": "cpp",
"sstream": "cpp",
"stdexcept": "cpp",
"streambuf": "cpp",
"text_encoding": "cpp",
"typeinfo": "cpp",
"variant": "cpp"
}
}

74
lab4/Sieve.cc Normal file
View file

@ -0,0 +1,74 @@
#include <iostream>
#include <string>
#include <vector>
class Sieve {
private:
std::string sieve;
public:
// Constructor to initialize sieve with 'P' (prime assumption)
Sieve(size_t limit) : sieve(limit + 1, 'P') {
// 0 and 1 are not primes
sieve[0] = 'C';
if (limit > 0) sieve[1] = 'C';
// Sieve of Eratosthenes
for (size_t i = 2; i * i <= limit; ++i) {
if (sieve[i] == 'P') {
for (size_t j = i * i; j <= limit; j += i) {
sieve[j] = 'C';
}
}
}
}
// Get primes as a vector of integers
std::vector<int> getPrimes() const {
std::vector<int> primes;
for (size_t i = 2; i < sieve.size(); ++i) {
if (sieve[i] == 'P') {
primes.push_back(i);
}
}
return primes;
}
// Print all primes in the range
void printPrimesInRange(int start, int end) const {
for (int i = start; i <= end; ++i) {
if (sieve[i] == 'P') {
std::cout << i << " ";
}
}
std::cout << std::endl;
}
// Get the largest prime below a limit
int largestPrimeBelow(int limit) const {
for (int i = limit; i >= 2; --i) {
if (sieve[i] == 'P') {
return i;
}
}
return -1; // If no prime is found
}
};
// Main function for testing
int main() {
const int limit = 100000;
// Create a Sieve instance for the range 0 to limit
Sieve sieve(limit);
// Print primes between 1 and 200
std::cout << "Primes between 1 and 200:" << std::endl;
sieve.printPrimesInRange(1, 200);
// Find and print the largest prime below 100,000
int largestPrime = sieve.largestPrimeBelow(limit);
std::cout << "Largest prime below " << limit << ": " << largestPrime << std::endl;
return 0;
}

32
lab4/TagRemover.cc Normal file
View file

@ -0,0 +1,32 @@
#include <iostream>
#include <regex>
#include <string>
class TagRemover {
private:
std::string content;
public:
TagRemover(std::istream& input) {
std::string line;
while (std::getline(input, line)) {
content += line + '\n';
}
}
void print(std::ostream& output) const {
std::string result = std::regex_replace(content, std::regex("<[^>]*>"), "");
result = std::regex_replace(result, std::regex("&lt;"), "<");
result = std::regex_replace(result, std::regex("&gt;"), ">");
result = std::regex_replace(result, std::regex("&nbsp;"), " ");
result = std::regex_replace(result, std::regex("&amp;"), "&");
output << result;
}
};
int main() {
TagRemover tr(std::cin);
tr.print(std::cout);
return 0;
}

View file

@ -1,30 +1,86 @@
#include <ctime> // time and localtime #include <ctime> // för tid och localtime
#include <iomanip> // för setw och setfill
#include <sstream> // för inputhantering
#include "date.h" #include "date.h"
int Date::daysPerMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int Date::daysPerMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
// Konstruktor: dagens datum
Date::Date() { Date::Date() {
time_t timer = time(0); // time in seconds since 1970-01-01 time_t timer = time(0); // tid i sekunder sedan 1970-01-01
tm* locTime = localtime(&timer); // broken-down time tm* locTime = localtime(&timer); // lokal tid
year = 1900 + locTime->tm_year; year = 1900 + locTime->tm_year;
month = 1 + locTime->tm_mon; month = 1 + locTime->tm_mon;
day = locTime->tm_mday; day = locTime->tm_mday;
} }
Date::Date(int y, int m, int d) {} // Konstruktor: specifikt datum
Date::Date(int y, int m, int d) : year(y), month(m), day(d) {}
// Get-funktioner
int Date::getYear() const { int Date::getYear() const {
return 0; return year;
} }
int Date::getMonth() const { int Date::getMonth() const {
return 0; return month;
} }
int Date::getDay() const { int Date::getDay() const {
return 0; return day;
} }
// Gå till nästa dag
void Date::next() { void Date::next() {
day++;
if (day > daysPerMonth[month - 1] + (month == 2 && isLeapYear(year))) {
day = 1;
month++;
if (month > 12) {
month = 1;
year++;
}
}
} }
// Kontrollera om ett år är ett skottår
bool Date::isLeapYear(int year) {
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}
// Overloaded operator<< (output)
std::ostream& operator<<(std::ostream& os, const Date& date) {
os << std::setw(4) << std::setfill('0') << date.getYear() << '-'
<< std::setw(2) << std::setfill('0') << date.getMonth() << '-'
<< std::setw(2) << std::setfill('0') << date.getDay();
return os;
}
// Overloaded operator>> (input)
std::istream& operator>>(std::istream& is, Date& date) {
std::string input;
is >> input;
std::istringstream iss(input);
char dash1, dash2;
int y, m, d;
if (iss >> y >> dash1 >> m >> dash2 >> d && dash1 == '-' && dash2 == '-') {
// Validera månad och dag
if (m >= 1 && m <= 12) {
int maxDay = Date::daysPerMonth[m - 1];
if (m == 2 && Date::isLeapYear(y)) {
maxDay = 29; // Februari har 29 dagar under skottår
}
if (d >= 1 && d <= maxDay) {
date = Date(y, m, d); // Sätt datumet om det är giltigt
return is;
}
}
}
// Ogiltig inmatning
is.setstate(std::ios_base::failbit);
return is;
}

View file

@ -1,19 +1,27 @@
#ifndef DATE_H #ifndef DATE_H
#define DATE_H #define DATE_H
#include <iostream>
class Date { class Date {
public: public:
Date(); // today's date Date(); // dagens datum
Date(int y, int m, int d); // yyyy-mm-dd Date(int y, int m, int d); // yyyy-mm-dd
int getYear() const; // get the year int getYear() const; // returnerar året
int getMonth() const; // get the month int getMonth() const; // returnerar månaden
int getDay() const; // get the day int getDay() const; // returnerar dagen
void next(); // advance to next day void next(); // går till nästa dag
// Overloaded operators
friend std::ostream& operator<<(std::ostream& os, const Date& date);
friend std::istream& operator>>(std::istream& is, Date& date);
private: private:
int year; // the year (four digits) int year; // året (fyra siffror)
int month; // the month (1-12) int month; // månaden (1-12)
int day; // the day (1-..) int day; // dagen (1-...)
static int daysPerMonth[12]; // number of days in each month static int daysPerMonth[12]; // antal dagar i varje månad
static bool isLeapYear(int year); // kontrollera skottår
}; };
#endif #endif

View file

@ -1,64 +1,40 @@
#include <iostream> #include <iostream>
#include <iomanip> // for setw and setfill
#include "date.h" #include "date.h"
using std::cout;
using std::endl;
using std::setw;
using std::setfill;
/*
* Prints the date d in the format yyyy-mm-dd. You shall replace this
* function with an overloaded operator<<, and add an overloaded operator>>.
*
*/
void print(const Date& d) {
cout << setw(4) << setfill('0') << d.getYear() << '-';
cout << setw(2) << setfill('0') << d.getMonth() << '-';
cout << setw(2) << setfill('0') << d.getDay();
}
int main() { int main() {
// Check input and output of dates. Uncomment the following when you // Test input och output
// have added operator>> and operator<<. bool cont = true;
/* while (cont) {
bool cont = true; std::cout << "Type a date: ";
while (cont) { Date aDate;
cout << "Type a date: "; std::cin >> aDate;
Date aDate;
cin >> aDate;
if (cin.eof()) {
cont = false;
} else if (!cin.good()) {
cout << "Wrong input format" << endl;
// restore stream state and ignore the rest of the line
cin.clear();
cin.ignore(10000, '\n');
}
else {
cout << "Output: " << aDate << endl;
}
}
*/
// Check 'next' by creating an object describing today's date, then if (std::cin.eof()) {
// printing dates more than a month ahead cont = false;
cout << "--- Today and more than a month ahead:" << endl; } else if (!std::cin.good()) {
Date d1; std::cout << "Wrong input format" << std::endl;
print(d1); std::cin.clear();
cout << endl; std::cin.ignore(10000, '\n');
for (int i = 1; i <= 35 ; ++i) { } else {
d1.next(); std::cout << "Output: " << aDate << std::endl;
print(d1); }
cout << endl; }
}
// Check so 'next' functions correctly from one year to the next // Testa 'next' med dagens datum
cout << "--- New Year's Eve and the next day:" << endl; std::cout << "--- Today and more than a month ahead:" << std::endl;
Date d2(2013, 12, 31); Date d1;
print(d2); std::cout << d1 << std::endl;
cout << endl; for (int i = 1; i <= 35; ++i) {
d2.next(); d1.next();
print(d2); std::cout << d1 << std::endl;
cout << endl; }
// Testa 'next' från nyårsafton
std::cout << "--- New Year's Eve and the next day:" << std::endl;
Date d2(2013, 12, 31);
std::cout << d2 << std::endl;
d2.next();
std::cout << d2 << std::endl;
return 0;
} }

56
lab4/makefile Normal file
View file

@ -0,0 +1,56 @@
# Compiler
CXX = g++
CXXFLAGS = -std=c++17 -Wall -Wextra -pedantic
# Targets
TARGETS = TagRemover Sieve date_test toString_test string_cast_test
# Source files
SRCS_TAGREMOVER = TagRemover.cc
SRCS_SIEVE = Sieve.cc
SRCS_DATE = date.cc
SRCS_DATE_TEST = date_test.cc
SRCS_TOSTRING_TEST = toString_test.cc date.cc
SRCS_STRING_CAST_TEST = string_cast_test.cc date.cc
# Object files
OBJS_TAGREMOVER = $(SRCS_TAGREMOVER:.cc=.o)
OBJS_SIEVE = $(SRCS_SIEVE:.cc=.o)
OBJS_DATE = $(SRCS_DATE:.cc=.o)
OBJS_DATE_TEST = $(SRCS_DATE_TEST:.cc=.o)
OBJS_TOSTRING_TEST = $(SRCS_TOSTRING_TEST:.cc=.o)
OBJS_STRING_CAST_TEST = $(SRCS_STRING_CAST_TEST:.cc=.o)
# Default target
all: $(TARGETS)
# Rule to build TagRemover
TagRemover: $(OBJS_TAGREMOVER)
$(CXX) $(CXXFLAGS) -o $@ $^
# Rule to build Sieve
Sieve: $(OBJS_SIEVE)
$(CXX) $(CXXFLAGS) -o $@ $^
# Rule to build date_test
date_test: $(OBJS_DATE_TEST) $(OBJS_DATE)
$(CXX) $(CXXFLAGS) -o $@ $^
# Rule to build toString_test
toString_test: $(OBJS_TOSTRING_TEST)
$(CXX) $(CXXFLAGS) -o $@ $^
# Rule to build string_cast_test
string_cast_test: $(OBJS_STRING_CAST_TEST)
$(CXX) $(CXXFLAGS) -o $@ $^
# Rule to compile source files into object files
%.o: %.cc
$(CXX) $(CXXFLAGS) -c $<
# Clean up build artifacts
clean:
rm -f $(OBJS_TAGREMOVER) $(OBJS_SIEVE) $(OBJS_DATE) $(OBJS_DATE_TEST) $(OBJS_TOSTRING_TEST) $(OBJS_STRING_CAST_TEST) $(TARGETS)
# Phony targets
.PHONY: all clean

61
lab4/reflektion.txt Normal file
View file

@ -0,0 +1,61 @@
1. In your tests, how did you test the error handling (e.g., that a wrong string_cast actually
throws?)
Genom att använda en ogiltig inmatning, som "abc" eller "123abc", och verifiera att std::invalid_argument kastas korrekt.
2. In TagRemover, why do you think the constructor takes an istream instead of just the
filename?
Det gör klassen mer flexibel eftersom den kan arbeta med vilken ström som helst, inte bara filer. Till exempel kan den användas med std::cin eller std::stringstream.
3. In TagRemover, did you process the file line by line, or did you first read the entire file?
What are the pros and cons of these two approaches?
Jag läste hela filen först eftersom det är enklare att använda regex för att bearbeta hela texten på en gång.
Fördelen är att det är mer effektivt för regex-matchning, men nackdelen är att det kräver mer minne för stora filer.
4. How do you read the entire contents of an std::istream into a std::string without using
a for or while loop?
Genom att använda:
std::string content((std::istreambuf_iterator<char>(istream)), std::istreambuf_iterator<char>());
5. In TagRemover, do you have duplicate code for translating the special characters? If so, how
would you refactor your code to avoid duplicate code?
Ja, det finns duplicerad kod för varje specialtecken. Jag skulle använda en std::map för att lagra mönster och ersättningar och iterera genom den:
std::map<std::string, std::string> replacements = {
{"&lt;", "<"}, {"&gt;", ">"}, {"&nbsp;", " "}, {"&amp;", "&"}
};
for (const auto& [pattern, replacement] : replacements) {
result = std::regex_replace(result, std::regex(pattern), replacement);
}
6. How do you check if an input or output operation on a stream (e.g., operator>> or
operator<<) has failed?
Genom att använda std::istream::fail() eller std::ostream::fail().
7. How do you know if you have reached the end of an istream?
Genom att använda std::istream::eof().
8. Does string_cast<int>("123kalle") return the value 123 or does it throw an exception?
How do you implement each of those behaviours?
Det kastar ett undantag eftersom std::istringstream misslyckas med att konsumera hela strängen. För att tillåta delvis inläsning skulle vi behöva anpassa funktionen.
9. When calling the function template toString, the template type argument is not ex-
plicitly given in the call. For string_cast, on the other hand, you have to specify
string_cast<int> or string_cast<Date>. What is the difference? When should explicit
template arguments be given to function templates?
toString använder <<-operatören, som automatiskt identifierar typen av objekt, medan string_cast kräver en explicit typ eftersom det behöver veta vad strängen ska konverteras till.

14
lab4/string_cast.h Normal file
View file

@ -0,0 +1,14 @@
#include <iostream>
#include <sstream>
#include <stdexcept> // För std::invalid_argument
// Template function for string_cast
template <typename T>
T string_cast(const std::string& str) {
std::istringstream iss(str);
T value;
if (!(iss >> value) || !(iss.eof())) {
throw std::invalid_argument("Invalid conversion from string: " + str);
}
return value;
}

29
lab4/string_cast_test.cc Normal file
View file

@ -0,0 +1,29 @@
#include <iostream>
#include <string>
#include "date.h" // För Date-klassen
#include "string_cast.h" // Inkludera string_cast
int main() {
try {
// Testa string_cast med int
int i = string_cast<int>("123");
std::cout << "Integer: " << i << std::endl;
// Testa string_cast med double
double d = string_cast<double>("12.34");
std::cout << "Double: " << d << std::endl;
// Testa string_cast med Date
Date date = string_cast<Date>("2015-01-10");
std::cout << "Date: " << date << std::endl;
// Testa ogiltig konvertering
int invalid = string_cast<int>("abc");
std::cout << "Invalid conversion: " << invalid << std::endl;
} catch (const std::invalid_argument& e) {
std::cout << "Error: " << e.what() << std::endl;
}
return 0;
}

10
lab4/toString.h Normal file
View file

@ -0,0 +1,10 @@
#include <sstream>
#include <string>
// Template function to convert an object to a string
template <typename T>
std::string toString(const T& obj) {
std::ostringstream oss;
oss << obj;
return oss.str();
}

22
lab4/toString_test.cc Normal file
View file

@ -0,0 +1,22 @@
#include <iostream>
#include "date.h" // Inkludera Date-klassen från tidigare lösning
#include "toString.h" // Inkludera toString-mallen
int main() {
// Testa med ett primitivt datatyper
double d = 1.234;
int i = 42;
std::string strDouble = toString(d);
std::string strInt = toString(i);
std::cout << "Double as string: " << strDouble << std::endl;
std::cout << "Integer as string: " << strInt << std::endl;
// Testa med Date-klassen
Date today(2023, 12, 11); // Skapa ett datumobjekt
std::string strDate = toString(today);
std::cout << "Date as string: " << strDate << std::endl;
return 0;
}