Compare commits
40 commits
Author | SHA1 | Date | |
---|---|---|---|
|
daa2dfc077 | ||
|
69af73e315 | ||
|
2769a3a0ad | ||
|
1dae994364 | ||
|
6130bdee66 | ||
|
8abebb692a | ||
|
4bb3629a76 | ||
|
cc34c03648 | ||
|
c5e034c3ac | ||
|
686fd07e08 | ||
|
abec11b35b | ||
|
42f69296f6 | ||
|
d4970c8d76 | ||
|
57d5f34ee5 | ||
|
1c88d49452 | ||
|
7a62bebf76 | ||
|
70170ea995 | ||
|
d10300509e | ||
|
7dd7f5610b | ||
|
94d807fc67 | ||
|
8c8930f5c5 | ||
|
fe00d47e02 | ||
|
c6eb4cb6ae | ||
|
5b669caddb | ||
|
b843fc98e0 | ||
|
fdae90ad9f | ||
|
744d0f7a3a | ||
|
f9f3674fde | ||
|
7d93d5dbfa | ||
|
e4e27d421a | ||
|
3645b14f3c | ||
|
dbd64f4545 | ||
|
53b9f8e4a3 | ||
|
1f44b48fde | ||
|
74ce672841 | ||
|
63265d5cfb | ||
|
bdc3832fd1 | ||
|
b72f78839c | ||
|
43568e947f | ||
|
3f1ca7ecc1 |
40 changed files with 1357 additions and 247 deletions
274
.clang-format
Normal file
274
.clang-format
Normal file
|
@ -0,0 +1,274 @@
|
|||
---
|
||||
Language: Cpp
|
||||
AccessModifierOffset: -2
|
||||
AlignAfterOpenBracket: Align
|
||||
AlignArrayOfStructures: None
|
||||
AlignConsecutiveAssignments:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: true
|
||||
AlignConsecutiveBitFields:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveDeclarations:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveMacros:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveShortCaseStatements:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCaseArrows: false
|
||||
AlignCaseColons: false
|
||||
AlignConsecutiveTableGenBreakingDAGArgColons:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveTableGenCondOperatorColons:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignConsecutiveTableGenDefinitionColons:
|
||||
Enabled: false
|
||||
AcrossEmptyLines: false
|
||||
AcrossComments: false
|
||||
AlignCompound: false
|
||||
AlignFunctionPointers: false
|
||||
PadOperators: false
|
||||
AlignEscapedNewlines: Right
|
||||
AlignOperands: Align
|
||||
AlignTrailingComments:
|
||||
Kind: Always
|
||||
OverEmptyLines: 0
|
||||
AllowAllArgumentsOnNextLine: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowBreakBeforeNoexceptSpecifier: Never
|
||||
AllowShortBlocksOnASingleLine: Never
|
||||
AllowShortCaseExpressionOnASingleLine: true
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortCompoundRequirementOnASingleLine: true
|
||||
AllowShortEnumsOnASingleLine: true
|
||||
AllowShortFunctionsOnASingleLine: All
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
AllowShortLambdasOnASingleLine: All
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterDefinitionReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
AttributeMacros:
|
||||
- __capability
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BitFieldColonSpacing: Both
|
||||
BraceWrapping:
|
||||
AfterCaseLabel: false
|
||||
AfterClass: false
|
||||
AfterControlStatement: Never
|
||||
AfterEnum: false
|
||||
AfterExternBlock: false
|
||||
AfterFunction: false
|
||||
AfterNamespace: false
|
||||
AfterObjCDeclaration: false
|
||||
AfterStruct: false
|
||||
AfterUnion: false
|
||||
BeforeCatch: false
|
||||
BeforeElse: false
|
||||
BeforeLambdaBody: false
|
||||
BeforeWhile: false
|
||||
IndentBraces: false
|
||||
SplitEmptyFunction: true
|
||||
SplitEmptyRecord: true
|
||||
SplitEmptyNamespace: true
|
||||
BreakAdjacentStringLiterals: true
|
||||
BreakAfterAttributes: Leave
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakAfterReturnType: None
|
||||
BreakArrays: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeConceptDeclarations: Always
|
||||
BreakBeforeBraces: Attach
|
||||
BreakBeforeInlineASMColon: OnlyMultiline
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializers: BeforeColon
|
||||
BreakFunctionDefinitionParameters: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakStringLiterals: true
|
||||
BreakTemplateDeclarations: MultiLine
|
||||
ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: false
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
EmptyLineAfterAccessModifier: Never
|
||||
EmptyLineBeforeAccessModifier: LogicalBlock
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- foreach
|
||||
- Q_FOREACH
|
||||
- BOOST_FOREACH
|
||||
IfMacros:
|
||||
- KJ_IF_MAYBE
|
||||
IncludeBlocks: Preserve
|
||||
IncludeCategories:
|
||||
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
|
||||
Priority: 2
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
|
||||
Priority: 3
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
- Regex: '.*'
|
||||
Priority: 1
|
||||
SortPriority: 0
|
||||
CaseSensitive: false
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IncludeIsMainSourceRegex: ''
|
||||
IndentAccessModifiers: false
|
||||
IndentCaseBlocks: false
|
||||
IndentCaseLabels: false
|
||||
IndentExternBlock: AfterExternBlock
|
||||
IndentGotoLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentRequiresClause: true
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
InsertBraces: false
|
||||
InsertNewlineAtEOF: false
|
||||
InsertTrailingCommas: None
|
||||
IntegerLiteralSeparator:
|
||||
Binary: 0
|
||||
BinaryMinDigits: 0
|
||||
Decimal: 0
|
||||
DecimalMinDigits: 0
|
||||
Hex: 0
|
||||
HexMinDigits: 0
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLines:
|
||||
AtEndOfFile: false
|
||||
AtStartOfBlock: true
|
||||
AtStartOfFile: true
|
||||
LambdaBodyIndentation: Signature
|
||||
LineEnding: DeriveLF
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MainIncludeChar: Quote
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCBinPackProtocolList: Auto
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCBreakBeforeNestedBlockParam: true
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PackConstructorInitializers: BinPack
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakOpenParenthesis: 0
|
||||
PenaltyBreakScopeResolution: 500
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyIndentedWhitespace: 0
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerAlignment: Right
|
||||
PPIndentWidth: -1
|
||||
QualifierAlignment: Leave
|
||||
ReferenceAlignment: Pointer
|
||||
ReflowComments: true
|
||||
RemoveBracesLLVM: false
|
||||
RemoveParentheses: Leave
|
||||
RemoveSemicolon: false
|
||||
RequiresClausePosition: OwnLine
|
||||
RequiresExpressionIndentation: OuterScope
|
||||
SeparateDefinitionBlocks: Leave
|
||||
ShortNamespaceLines: 1
|
||||
SkipMacroDefinitionBody: false
|
||||
SortIncludes: CaseSensitive
|
||||
SortJavaStaticImport: Before
|
||||
SortUsingDeclarations: LexicographicNumeric
|
||||
SpaceAfterCStyleCast: false
|
||||
SpaceAfterLogicalNot: false
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceAroundPointerQualifiers: Default
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCaseColon: false
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeJsonColon: false
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeParensOptions:
|
||||
AfterControlStatements: true
|
||||
AfterForeachMacros: true
|
||||
AfterFunctionDefinitionName: false
|
||||
AfterFunctionDeclarationName: false
|
||||
AfterIfMacros: true
|
||||
AfterOverloadedOperator: false
|
||||
AfterPlacementOperator: true
|
||||
AfterRequiresInClause: false
|
||||
AfterRequiresInExpression: false
|
||||
BeforeNonEmptyParentheses: false
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceBeforeSquareBrackets: false
|
||||
SpaceInEmptyBlock: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
SpacesInAngles: Never
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInLineCommentPrefix:
|
||||
Minimum: 1
|
||||
Maximum: -1
|
||||
SpacesInParens: Never
|
||||
SpacesInParensOptions:
|
||||
ExceptDoubleParentheses: false
|
||||
InCStyleCasts: false
|
||||
InConditionalStatements: false
|
||||
InEmptyParentheses: false
|
||||
Other: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Latest
|
||||
StatementAttributeLikeMacros:
|
||||
- Q_EMIT
|
||||
StatementMacros:
|
||||
- Q_UNUSED
|
||||
- QT_REQUIRE_VERSION
|
||||
TableGenBreakInsideDAGArg: DontBreak
|
||||
TabWidth: 8
|
||||
UseTab: Never
|
||||
VerilogBreakBetweenInstancePorts: true
|
||||
WhitespaceSensitiveMacros:
|
||||
- BOOST_PP_STRINGIZE
|
||||
- CF_SWIFT_NAME
|
||||
- NS_SWIFT_NAME
|
||||
- PP_STRINGIZE
|
||||
- STRINGIZE
|
||||
...
|
||||
|
11
.gitignore
vendored
Normal file
11
.gitignore
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
*.o
|
||||
*.d
|
||||
*a.out*
|
||||
build
|
||||
.cache/
|
||||
words.txt
|
||||
compile_commands.json
|
||||
lab2/edit
|
||||
lab2/spell
|
||||
lab3/tabletest
|
||||
./vscode
|
2
Makefile
Normal file
2
Makefile
Normal file
|
@ -0,0 +1,2 @@
|
|||
format:
|
||||
find . -regex '.*\.\(c\|cpp\|cc\|cxx\|h\|hpp\|hh\|hxx\)' -exec clang-format {} +
|
BIN
cpplabs.pdf
Normal file
BIN
cpplabs.pdf
Normal file
Binary file not shown.
2
lab1/.gitignore
vendored
Normal file
2
lab1/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
hello
|
||||
test_editor
|
51
lab1/Justfile
Normal file
51
lab1/Justfile
Normal file
|
@ -0,0 +1,51 @@
|
|||
all: a1 a2 a3 a4 cmake buggy
|
||||
|
||||
# Build the A1 lab exercise
|
||||
a1:
|
||||
g++ -o hello hello.cc
|
||||
./hello
|
||||
|
||||
# Build the A2 lab exercise
|
||||
a2:
|
||||
g++ -std=c++11 -o separate_main separate_main.cc separate_fn.cc
|
||||
|
||||
# Build the A3 lab exercise
|
||||
a3:
|
||||
g++ -std=c++11 -c separate_main.cc
|
||||
g++ -std=c++11 -c separate_fn.cc
|
||||
g++ -std=c++11 -o separate_main separate_main.o separate_fn.o
|
||||
|
||||
# Build the A4 lab exercise
|
||||
a4:
|
||||
@echo "Will fail"
|
||||
-g++ -std=c++11 -o separate_main separate_main.cc
|
||||
|
||||
# Build the A5 lab exercise
|
||||
a5:
|
||||
g++ -c -O2 -Wall -Wextra -pedantic-errors -Wold-style-cast -std=c++11 hello.cc
|
||||
|
||||
# Build the cmake exercise
|
||||
cmake:
|
||||
rm -rf ./cmake-example/build # Start over
|
||||
cd ./cmake-example && mkdir -p build
|
||||
cd ./cmake-example/build && cmake ..
|
||||
cd ./cmake-example/build && make -j$(nproc)
|
||||
./cmake-example/build/SimpleMain
|
||||
|
||||
buggy:
|
||||
cd ./buggy_programs && make -j4
|
||||
cd ./buggy_programs && mkdir -p build
|
||||
cd ./buggy_programs && cmake -B build
|
||||
cd ./buggy_programs && make -C build
|
||||
|
||||
valgrind: buggy
|
||||
cd ./buggy_programs/build/ && valgrind ./dangling
|
||||
cd ./buggy_programs/build/ && valgrind ./bounds
|
||||
cd ./buggy_programs/build/ && valgrind ./bounds-heap
|
||||
cd ./buggy_programs/build/ && valgrind ./leak
|
||||
cd ./buggy_programs/build/ && valgrind ./sum
|
||||
cd ./buggy_programs/build/ && valgrind ./sum_alt
|
||||
cd ./buggy_programs/build/ && valgrind ./ub
|
||||
|
||||
clean:
|
||||
git clean -fdx
|
6
lab1/buggy_programs/.gitignore
vendored
Normal file
6
lab1/buggy_programs/.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
bounds
|
||||
bounds-heap
|
||||
dangling
|
||||
leak
|
||||
sum
|
||||
ub
|
|
@ -3,7 +3,7 @@
|
|||
# It uses generator expressions, to set additional flags when the build type
|
||||
# is Debug.
|
||||
#
|
||||
# To try this out, first create a build directory for a release build,
|
||||
# To try this out, first create a build directory for a release build,
|
||||
# and do a release build, e.g.,
|
||||
# % mkdir build-rel
|
||||
# % cd build-rel
|
||||
|
@ -21,7 +21,7 @@
|
|||
#
|
||||
# where SRC_DIR is the directory containing the source and CMakeLists.txt,
|
||||
# e.g., .. if your build directories are placed in this directory.
|
||||
#
|
||||
#
|
||||
# Run the examples and verify that the sanitizers find the errors.
|
||||
#
|
||||
# If you want to see the actual commands run during the build, for instance
|
||||
|
|
1
lab1/cmake-example/.gitignore
vendored
Normal file
1
lab1/cmake-example/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
build
|
|
@ -1,10 +1,9 @@
|
|||
#include<coding.h>
|
||||
#include <coding.h>
|
||||
|
||||
unsigned char encode(unsigned char c)
|
||||
{
|
||||
return c;
|
||||
unsigned char encode(unsigned char c) {
|
||||
return ++c;
|
||||
}
|
||||
unsigned char decode(unsigned char c)
|
||||
{
|
||||
return c;
|
||||
|
||||
unsigned char decode(unsigned char c) {
|
||||
return --c;
|
||||
}
|
||||
|
|
|
@ -6,11 +6,29 @@ using std::string;
|
|||
|
||||
using size_type = Editor::size_type;
|
||||
|
||||
size_type Editor::get_size() const
|
||||
{
|
||||
return text.size();
|
||||
}
|
||||
size_type Editor::get_size() const { return text.size(); }
|
||||
|
||||
size_type Editor::find_left_par(size_type pos) const {
|
||||
return string::npos;
|
||||
Editor::size_type Editor::find_left_par(size_type pos) const {
|
||||
char right_par = text[pos];
|
||||
char left_par;
|
||||
|
||||
// Determine the matching left parenthesis for the given right parenthesis
|
||||
switch (right_par) {
|
||||
case ')': left_par = '('; break;
|
||||
case ']': left_par = '['; break;
|
||||
case '}': left_par = '{'; break;
|
||||
default: return string::npos; // Not a valid right parenthesis
|
||||
}
|
||||
|
||||
int balance = 1; // Start with the right parenthesis at text[pos]
|
||||
for (size_type i = pos; i-- > 0;) {
|
||||
if (text[i] == left_par) {
|
||||
balance--;
|
||||
if (balance == 0) return i; // Found the matching left parenthesis
|
||||
} else if (text[i] == right_par) {
|
||||
balance++;
|
||||
}
|
||||
}
|
||||
|
||||
return string::npos; // No matching left parenthesis found
|
||||
}
|
||||
|
|
|
@ -4,24 +4,24 @@
|
|||
#include <string>
|
||||
|
||||
class Editor {
|
||||
public:
|
||||
public:
|
||||
using size_type = std::string::size_type;
|
||||
/* Creates a text editor containing the text t */
|
||||
Editor(const std::string& t) : text(t) {}
|
||||
/* Creates a text editor containing the text t */
|
||||
Editor(const std::string &t) : text(t) {}
|
||||
|
||||
/* Get the size of the current contents */
|
||||
size_type get_size() const;
|
||||
|
||||
/*
|
||||
* Text[pos] contains a right parentheses. Returns the position of
|
||||
* the corresponding left parentheses, or string::npos if there
|
||||
* is no match.
|
||||
*/
|
||||
size_type find_left_par(size_type pos) const;
|
||||
/*
|
||||
* Text[pos] contains a right parentheses. Returns the position of
|
||||
* the corresponding left parentheses, or string::npos if there
|
||||
* is no match.
|
||||
*/
|
||||
size_type find_left_par(size_type pos) const;
|
||||
|
||||
// ... functions to edit the text (insert and delete characters)
|
||||
private:
|
||||
std::string text;
|
||||
// ... functions to edit the text (insert and delete characters)
|
||||
private:
|
||||
std::string text;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
5
lab1/hello.cc
Normal file
5
lab1/hello.cc
Normal file
|
@ -0,0 +1,5 @@
|
|||
#include <iostream>
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
std::cout << "Hello" << std::endl;
|
||||
}
|
37
lab2/Makefile
Normal file
37
lab2/Makefile
Normal file
|
@ -0,0 +1,37 @@
|
|||
CXX = g++
|
||||
CXXFLAGS = -Wall -Wextra -Wpedantic -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wunused -Woverloaded-virtual -Wconversion -Wsign-conversion -Wnull-dereference -Wdouble-promotion -Wformat=2 -std=c++17
|
||||
#CXXFLAGS += -Werror
|
||||
|
||||
SRC = $(wildcard *.cc)
|
||||
HDR = $(wildcard *.h)
|
||||
OBJ = $(SRC:.cc=.o)
|
||||
|
||||
all: spell edit $(OBJ)
|
||||
|
||||
edit: test_edit_distance.o edit_distance.o
|
||||
@echo "Building & linking $@"
|
||||
@$(CXX) $(CXXFLAGS) $^ -o $@
|
||||
|
||||
spell: spell.o word.o dictionary.o edit_distance.o
|
||||
@echo "Building & linking $@"
|
||||
@$(CXX) $(CXXFLAGS) $^ -o $@
|
||||
|
||||
%.o:%.cc
|
||||
@echo "Building $@"
|
||||
@$(CXX) -c $(CXXFLAGS) $< -o $@
|
||||
|
||||
lint: clang-tidy cppcheck clang-format
|
||||
|
||||
clang-tidy:
|
||||
clang-tidy $(SRC) -- $(CXXFLAGS)
|
||||
|
||||
cppcheck:
|
||||
cppcheck --enable=all --language=c++ --std=c++17 --suppress=missingIncludeSystem -I/usr/include $(SRC) $(HDR)
|
||||
|
||||
clang-format:
|
||||
clang-format -i $(SRC) $(HDR)
|
||||
|
||||
clean:
|
||||
rm -f *.o spell edit
|
||||
|
||||
.PHONY: clean all lint clang-tidy cppcheck clang-format
|
|
@ -1,22 +1,131 @@
|
|||
#include <string>
|
||||
#include <vector>
|
||||
#include "dictionary.h"
|
||||
#include "edit_distance.h"
|
||||
#include "word.h"
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include "word.h"
|
||||
#include "dictionary.h"
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
Dictionary::Dictionary() {
|
||||
Dictionary::Dictionary() {}
|
||||
|
||||
bool Dictionary::contains(const string &word) const {
|
||||
auto l = word.length();
|
||||
Word w = Word(word);
|
||||
if (std::find(this->words[l].begin(), this->words[l].end(), w) !=
|
||||
std::end(this->words[l])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Dictionary::contains(const string& word) const {
|
||||
return true;
|
||||
std::vector<string> Dictionary::get_suggestions(const string &word) const {
|
||||
vector<string> suggestions;
|
||||
add_trigram_suggestions(suggestions, word);
|
||||
trim_suggestions(suggestions, word);
|
||||
rank_suggestions(suggestions, word);
|
||||
return suggestions;
|
||||
}
|
||||
|
||||
vector<string> Dictionary::get_suggestions(const string& word) const {
|
||||
vector<string> suggestions;
|
||||
return suggestions;
|
||||
void Dictionary::add_trigram_suggestions(std::vector<std::string> &suggestions,
|
||||
const std::string &word) const {
|
||||
// Get trigrams of the input word
|
||||
Word input_word(word);
|
||||
const std::vector<std::string> &input_trigrams = input_word.get_trigrams();
|
||||
|
||||
// Iterate through all words in the dictionary
|
||||
for (int i = 0; i < MAXLEN; ++i) {
|
||||
for (const Word &dict_word : words[i]) {
|
||||
// Get the trigrams of the dictionary word
|
||||
const std::vector<std::string> &dict_word_trigrams =
|
||||
dict_word.get_trigrams();
|
||||
|
||||
// Count how many trigrams match
|
||||
unsigned int match_count = dict_word.get_matches(input_trigrams);
|
||||
|
||||
// If there are any matches, add the word to suggestions
|
||||
if (match_count > 0) {
|
||||
suggestions.push_back(dict_word.get_word());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Dictionary::rank_suggestions(std::vector<std::string> &suggestions,
|
||||
const std::string &word) const {
|
||||
// Sort suggestions based on the levenshtein distance
|
||||
std::sort(suggestions.begin(), suggestions.end(),
|
||||
[&](const std::string &a, const std::string &b) {
|
||||
auto dist_a = edit_distance(a, word);
|
||||
auto dist_b = edit_distance(b, word);
|
||||
return dist_a < dist_b;
|
||||
});
|
||||
}
|
||||
|
||||
void Dictionary::trim_suggestions(std::vector<std::string> &suggestions,
|
||||
const std::string &word) const {
|
||||
// Remove duplicates using a set
|
||||
std::set<std::string> unique_suggestions(suggestions.begin(),
|
||||
suggestions.end());
|
||||
suggestions.assign(unique_suggestions.begin(), unique_suggestions.end());
|
||||
|
||||
// Remove the input word from the suggestions list (if present)
|
||||
suggestions.erase(std::remove(suggestions.begin(), suggestions.end(), word),
|
||||
suggestions.end());
|
||||
|
||||
auto l = word.length();
|
||||
std::cout << "WTF" << l << std::endl;
|
||||
|
||||
// Example: Remove any suggestions that are not within 1 string length
|
||||
suggestions.erase(std::remove_if(suggestions.begin(), suggestions.end(),
|
||||
[l](const std::string &s) {
|
||||
return s.length() > (l + 1) ||
|
||||
s.length() < (l - 1);
|
||||
}),
|
||||
suggestions.end());
|
||||
}
|
||||
|
||||
int Dictionary::spit(path p) {
|
||||
std::ofstream file(p);
|
||||
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Error opening file! " << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (int a = 0; a < MAXLEN; a++) {
|
||||
for (auto &word : words[a]) {
|
||||
file << word;
|
||||
file << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
file.flush();
|
||||
file.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Dictionary::slurp(path p) {
|
||||
std::ifstream file(p.string());
|
||||
|
||||
if (!file.is_open()) {
|
||||
std::cerr << "Error opening file! " << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
while (std::getline(file, line)) {
|
||||
if (line.empty())
|
||||
continue;
|
||||
// Words larger than max gets placed in the topmost bucket
|
||||
words[std::min(line.size(), static_cast<size_t>(MAXLEN) - 1)].push_back(
|
||||
Word(line));
|
||||
}
|
||||
|
||||
file.close();
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
#ifndef DICTIONARY_H
|
||||
#define DICTIONARY_H
|
||||
#pragma once
|
||||
|
||||
#include "word.h"
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class Dictionary {
|
||||
public:
|
||||
Dictionary();
|
||||
bool contains(const std::string& word) const;
|
||||
std::vector<std::string> get_suggestions(const std::string& word) const;
|
||||
private:
|
||||
};
|
||||
#define MAXLEN 30
|
||||
|
||||
#endif
|
||||
// using std::vector;
|
||||
using std::filesystem::path;
|
||||
|
||||
class Dictionary {
|
||||
public:
|
||||
Dictionary();
|
||||
void add_trigram_suggestions(std::vector<std::string> &suggestions,
|
||||
const std::string &word) const;
|
||||
void rank_suggestions(std::vector<std::string> &suggestions,
|
||||
const std::string &word) const;
|
||||
void trim_suggestions(std::vector<std::string> &suggestions,
|
||||
const std::string &word) const;
|
||||
bool contains(const std::string &word) const;
|
||||
std::vector<std::string> get_suggestions(const std::string &word) const;
|
||||
int slurp(path p);
|
||||
int spit(path p);
|
||||
|
||||
private:
|
||||
std::vector<Word> words[MAXLEN];
|
||||
};
|
||||
|
|
35
lab2/edit_distance.cc
Normal file
35
lab2/edit_distance.cc
Normal file
|
@ -0,0 +1,35 @@
|
|||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
int edit_distance(const std::string &s1, const std::string &s2) {
|
||||
size_t m = s1.size();
|
||||
size_t n = s2.size();
|
||||
|
||||
// Create a 2D DP table
|
||||
std::vector<std::vector<size_t>> dp(m + 1, std::vector<size_t>(n + 1));
|
||||
|
||||
// Fill the base cases
|
||||
for (size_t i = 0; i <= m; ++i)
|
||||
dp[i][0] = i; // Deletion cost
|
||||
|
||||
for (size_t j = 0; j <= n; ++j)
|
||||
dp[0][j] = j; // Insertion cost
|
||||
|
||||
// Fill the DP table
|
||||
for (size_t i = 1; i <= m; ++i) {
|
||||
for (size_t j = 1; j <= n; ++j) {
|
||||
if (s1[i - 1] == s2[j - 1]) {
|
||||
dp[i][j] = dp[i - 1][j - 1]; // No operation needed
|
||||
} else {
|
||||
dp[i][j] = 1 + std::min({
|
||||
dp[i - 1][j], // Deletion
|
||||
dp[i][j - 1], // Insertion
|
||||
dp[i - 1][j - 1] // Substitution
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return static_cast<int>(dp[m][n]);
|
||||
}
|
17
lab2/edit_distance.h
Normal file
17
lab2/edit_distance.h
Normal file
|
@ -0,0 +1,17 @@
|
|||
#include <string>
|
||||
|
||||
/**
|
||||
* @brief Computes the edit distance (Levenshtein distance) between two strings.
|
||||
*
|
||||
* The edit distance is defined as the minimum number of single-character edits
|
||||
* (insertions, deletions, or substitutions) required to transform one string
|
||||
* into the other.
|
||||
*
|
||||
* This implementation uses dynamic programming to compute the distance
|
||||
* efficiently.
|
||||
*
|
||||
* @param s1 The first string.
|
||||
* @param s2 The second string.
|
||||
* @return The edit distance between the two strings.
|
||||
*/
|
||||
int edit_distance(const std::string &s1, const std::string &s2);
|
|
@ -1,18 +1,18 @@
|
|||
#include "dictionary.h"
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include <cctype>
|
||||
#include "dictionary.h"
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using std::cin;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
void check_word(const string& word, const Dictionary& dict)
|
||||
{
|
||||
void check_word(const string &word, const Dictionary &dict) {
|
||||
if (dict.contains(word)) {
|
||||
cout << "Correct." << endl;
|
||||
} else {
|
||||
|
@ -21,18 +21,22 @@ void check_word(const string& word, const Dictionary& dict)
|
|||
cout << "Wrong, no suggestions." << endl;
|
||||
} else {
|
||||
cout << "Wrong. Suggestions:" << endl;
|
||||
for (const auto& w : suggestions) {
|
||||
for (const auto &w : suggestions) {
|
||||
cout << " " << w << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
Dictionary dict;
|
||||
string word;
|
||||
while (cin >> word) {
|
||||
transform(word.begin(), word.end(), word.begin(), ::tolower);
|
||||
Dictionary dict;
|
||||
string word;
|
||||
dict.slurp(std::filesystem::path("/usr/share/dict/words"));
|
||||
// dict.spit(std::filesystem::path("words.txt"));
|
||||
|
||||
while (cin >> word) {
|
||||
std::transform(word.begin(), word.end(), word.begin(), ::tolower);
|
||||
check_word(word, dict);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
|
||||
#include <iostream>
|
||||
|
||||
bool do_test(const std::string& x, const std::string& y, int expected)
|
||||
{
|
||||
bool do_test(const std::string &x, const std::string &y, int expected) {
|
||||
auto actual = edit_distance(x, y);
|
||||
if (actual != expected) {
|
||||
std::cout << "*** WRONG: distance(" << x << ", " << y << ") was "
|
||||
|
@ -19,8 +18,7 @@ bool do_test(const std::string& x, const std::string& y, int expected)
|
|||
return false;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
int main() {
|
||||
int res = do_test("foobar", "foobar", 0);
|
||||
res += do_test("x", "x", 0);
|
||||
res += do_test("baz", "bar", 1);
|
||||
|
|
63
lab2/word.cc
63
lab2/word.cc
|
@ -1,16 +1,63 @@
|
|||
#include "word.h"
|
||||
#include "dictionary.h"
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "word.h"
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
Word::Word(const string& w, const vector<string>& t) {}
|
||||
|
||||
string Word::get_word() const {
|
||||
return string();
|
||||
Word::Word(const string &w, const vector<string> &t) : word(w), trigrams(t) {
|
||||
std::sort(trigrams.begin(), trigrams.end());
|
||||
}
|
||||
|
||||
unsigned int Word::get_matches(const vector<string>& t) const {
|
||||
return 0;
|
||||
std::vector<std::string> get_trigrams(const std::string &text) {
|
||||
std::vector<std::string> trigrams;
|
||||
if (text.size() < 3) {
|
||||
return trigrams; // Return an empty vector if the input is too short
|
||||
}
|
||||
|
||||
for (size_t i = 0; i <= text.size() - 3; ++i) {
|
||||
trigrams.push_back(
|
||||
text.substr(i, 3)); // Extract a substring of length 3
|
||||
}
|
||||
|
||||
return trigrams;
|
||||
}
|
||||
|
||||
Word::Word(const std::string &w) : word(w) {
|
||||
this->trigrams = ::get_trigrams(w);
|
||||
std::sort(trigrams.begin(), trigrams.end());
|
||||
}
|
||||
|
||||
string Word::get_word() const { return word; }
|
||||
|
||||
vector<std::string> Word::get_trigrams() const { return trigrams; }
|
||||
|
||||
unsigned int Word::get_matches(const vector<string> &t) const {
|
||||
unsigned int matches = 0;
|
||||
|
||||
for (const auto &trigram : t) {
|
||||
if (std::binary_search(trigrams.begin(), trigrams.end(), trigram)) {
|
||||
++matches;
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const Word &w) {
|
||||
auto space = string(" ");
|
||||
out << w.word;
|
||||
out << space;
|
||||
out << w.trigrams.size();
|
||||
for (const auto &tri : w.trigrams) {
|
||||
out << space << tri;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool operator==(const Word &lhs, const Word &rhs) {
|
||||
return lhs.word == rhs.word &&
|
||||
std::equal(lhs.trigrams.begin(), lhs.trigrams.end(),
|
||||
rhs.trigrams.begin());
|
||||
}
|
||||
|
|
41
lab2/word.h
41
lab2/word.h
|
@ -1,21 +1,34 @@
|
|||
#ifndef WORD_H
|
||||
#define WORD_H
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
/*
|
||||
* Contains a word and its trigrams
|
||||
*/
|
||||
class Word {
|
||||
public:
|
||||
/* Creates a word w with the sorted trigrams t */
|
||||
Word(const std::string& w, const std::vector<std::string>& t);
|
||||
|
||||
/* Returns the word */
|
||||
std::string get_word() const;
|
||||
|
||||
/* Returns how many of the trigrams in t that are present
|
||||
in this word's trigram vector */
|
||||
unsigned int get_matches(const std::vector<std::string>& t) const;
|
||||
private:
|
||||
public:
|
||||
/** Creates a word w with the sorted trigrams t */
|
||||
Word(const std::string &w, const std::vector<std::string> &t);
|
||||
|
||||
/** Creates a word w and derives the trigrams internally */
|
||||
Word(const std::string &w);
|
||||
|
||||
/** Returns the word */
|
||||
std::string get_word() const;
|
||||
|
||||
/** Returns trigrams */
|
||||
std::vector<std::string> get_trigrams() const;
|
||||
|
||||
/** Returns how many of the trigrams in t that are present
|
||||
in this word's trigram vector */
|
||||
unsigned int get_matches(const std::vector<std::string> &t) const;
|
||||
|
||||
private:
|
||||
const std::string word;
|
||||
std::vector<std::string> trigrams;
|
||||
friend std::ostream &operator<<(std::ostream &out, const Word &o);
|
||||
friend bool operator==(const Word &lhs, const Word &rhs);
|
||||
};
|
||||
|
||||
#endif
|
||||
bool operator==(const Word &lhs, const Word &rhs);
|
||||
|
|
33
lab3/Makefile
Normal file
33
lab3/Makefile
Normal file
|
@ -0,0 +1,33 @@
|
|||
CXX = g++
|
||||
CXXFLAGS = -Wall -Wextra -Wpedantic -Wshadow -Wnon-virtual-dtor -Wold-style-cast -Wcast-align -Wunused -Woverloaded-virtual -Wconversion -Wsign-conversion -Wnull-dereference -Wdouble-promotion -Wformat=2 -std=c++17
|
||||
#CXXFLAGS += -Werror
|
||||
|
||||
SRC = $(wildcard *.cc)
|
||||
HDR = $(wildcard *.h)
|
||||
OBJ = $(SRC:.cc=.o)
|
||||
|
||||
all: tabletest $(OBJ)
|
||||
|
||||
tabletest: $(OBJ)
|
||||
@echo "Building & linking $@"
|
||||
@$(CXX) $(CXXFLAGS) $^ -o $@
|
||||
|
||||
%.o:%.cc
|
||||
@echo "Building $@"
|
||||
@$(CXX) -c $(CXXFLAGS) $< -o $@
|
||||
|
||||
lint: clang-tidy cppcheck clang-format
|
||||
|
||||
clang-tidy:
|
||||
clang-tidy $(SRC) -- $(CXXFLAGS)
|
||||
|
||||
cppcheck:
|
||||
cppcheck --enable=all --language=c++ --std=c++17 --suppress=missingIncludeSystem -I/usr/include $(SRC) $(HDR)
|
||||
|
||||
clang-format:
|
||||
clang-format -i $(SRC) $(HDR)
|
||||
|
||||
clean:
|
||||
rm -f *.o spell edit
|
||||
|
||||
.PHONY: clean all lint clang-tidy cppcheck clang-format
|
|
@ -1,6 +1,5 @@
|
|||
#include "User.h"
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const User& u)
|
||||
{
|
||||
return os << "(" << u.getCardNbr() << ") "<< u.getName();
|
||||
std::ostream &operator<<(std::ostream &os, const User &u) {
|
||||
return os << "(" << u.getCardNbr() << ") " << u.getName();
|
||||
}
|
||||
|
|
30
lab3/User.h
30
lab3/User.h
|
@ -8,20 +8,26 @@ using std::cout;
|
|||
using std::endl;
|
||||
|
||||
class User {
|
||||
public:
|
||||
User() :cardNbr{0},name{"default"}{}
|
||||
User(int c, std::string n) :cardNbr{c},name{n} {}
|
||||
~User() {cardNbr=-2; name="--------------------";} // overwrite values for security reasons
|
||||
User(const User& u) =default;
|
||||
User& operator=(const User&) =default;
|
||||
int getCardNbr() const {return cardNbr;}
|
||||
std::string getName() const {return name;}
|
||||
bool operator==(const User& u) const {return cardNbr == u.cardNbr && name == u.name;}
|
||||
bool operator!=(const User& u) const {return ! (u == *this);}
|
||||
private:
|
||||
public:
|
||||
User() : cardNbr{0}, name{"default"} {}
|
||||
User(int c, std::string n) : cardNbr{c}, name{n} {}
|
||||
~User() {
|
||||
cardNbr = -2;
|
||||
name = "--------------------";
|
||||
} // overwrite values for security reasons
|
||||
User(const User &u) = default;
|
||||
User &operator=(const User &) = default;
|
||||
int getCardNbr() const { return cardNbr; }
|
||||
std::string getName() const { return name; }
|
||||
bool operator==(const User &u) const {
|
||||
return cardNbr == u.cardNbr && name == u.name;
|
||||
}
|
||||
bool operator!=(const User &u) const { return !(u == *this); }
|
||||
|
||||
private:
|
||||
int cardNbr;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const User& u);
|
||||
std::ostream &operator<<(std::ostream &os, const User &u);
|
||||
#endif
|
||||
|
|
|
@ -1,24 +1,22 @@
|
|||
#include "UserTable.h"
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
|
||||
const User UserTable::user_not_found = User{-1,"Not Found"};
|
||||
const User UserTable::user_not_found = User{-1, "Not Found"};
|
||||
|
||||
UserTable::UserTable() :users{new User[capacity]} { }
|
||||
UserTable::UserTable() : users{new User[capacity]} {}
|
||||
|
||||
UserTable::UserTable(const std::string& fname) :UserTable{}
|
||||
{
|
||||
UserTable::UserTable(const std::string &fname) : UserTable{} {
|
||||
std::ifstream ufile(fname);
|
||||
|
||||
if(ufile.is_open()) {
|
||||
while(ufile) {
|
||||
if (ufile.is_open()) {
|
||||
while (ufile) {
|
||||
int cn;
|
||||
if(ufile >> cn ) {
|
||||
if (ufile >> cn) {
|
||||
ufile.ignore(); // skip space
|
||||
char n[80];
|
||||
ufile.getline(n,80);
|
||||
addUser(User(cn,n));
|
||||
|
||||
ufile.getline(n, 80);
|
||||
addUser(User(cn, n));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -26,38 +24,39 @@ UserTable::UserTable(const std::string& fname) :UserTable{}
|
|||
}
|
||||
}
|
||||
|
||||
void UserTable::addUser(const User& u)
|
||||
{
|
||||
void UserTable::addUser(const User &u) {
|
||||
// gör tabellen större vid behov
|
||||
ensureCapacity(n+1);
|
||||
ensureCapacity(n + 1);
|
||||
|
||||
// 1. Hitta rätt plats
|
||||
int pos{0};
|
||||
while ( (pos < n) && (users[pos].getCardNbr() < u.getCardNbr())){
|
||||
while ((pos < n) && (users[pos].getCardNbr() < u.getCardNbr())) {
|
||||
++pos;
|
||||
}
|
||||
|
||||
//2. skapa lucka i vektorn
|
||||
for(int i=n; i > pos; --i){
|
||||
users[i] = users[i-1];
|
||||
// 2. skapa lucka i vektorn
|
||||
for (int i = n; i > pos; --i) {
|
||||
users[i] = users[i - 1];
|
||||
}
|
||||
|
||||
//3. stoppa in den nya användaren i luckan
|
||||
// 3. stoppa in den nya användaren i luckan
|
||||
users[pos] = u;
|
||||
|
||||
++n;
|
||||
}
|
||||
|
||||
User UserTable::find(int c) const
|
||||
{
|
||||
User UserTable::find(int c) const {
|
||||
// binärsökning (baserad på Holm, 2007)
|
||||
|
||||
int low = 0;
|
||||
int high = n - 1;
|
||||
int mid = -1;
|
||||
bool found = false;
|
||||
while (low < high && ! found) {
|
||||
while (low < high && !found) {
|
||||
mid = (low + high) / 2;
|
||||
//
|
||||
int midnbr = users[mid].getCardNbr();
|
||||
if (midnbr = c) {
|
||||
const int midnbr = users[mid].getCardNbr();
|
||||
if (c == midnbr) {
|
||||
found = true;
|
||||
} else if (users[mid].getCardNbr() < c) {
|
||||
low = mid + 1;
|
||||
|
@ -68,48 +67,41 @@ User UserTable::find(int c) const
|
|||
|
||||
return found ? users[mid] : user_not_found;
|
||||
}
|
||||
User UserTable::find(std::string name) const
|
||||
{
|
||||
for (int i = 0; i != n; ++i) {
|
||||
if (users[i].getName() == name) {
|
||||
return users[i];
|
||||
} else {
|
||||
return user_not_found;
|
||||
}
|
||||
}
|
||||
return user_not_found;
|
||||
User UserTable::find(std::string name) const {
|
||||
auto it = std::find_if(users, users + n, [&name](const User &u) {
|
||||
return u.getName() == name;
|
||||
});
|
||||
|
||||
// If it is at the 'end' of users, the result is not found
|
||||
return (it != users+n) ? *it : user_not_found;
|
||||
}
|
||||
|
||||
void UserTable::ensureCapacity(int s)
|
||||
{
|
||||
if(s>capacity) {
|
||||
while(s > capacity) {
|
||||
capacity*=4;
|
||||
void UserTable::ensureCapacity(int s) {
|
||||
if (s > capacity) {
|
||||
while (s > capacity) {
|
||||
capacity *= 4;
|
||||
}
|
||||
auto tmp = new User[capacity];
|
||||
std::copy(users, users+n, tmp);
|
||||
std::copy(users, users + n, tmp);
|
||||
delete[] users;
|
||||
users=tmp;
|
||||
users = tmp;
|
||||
}
|
||||
|
||||
}
|
||||
void UserTable::print(std::ostream& os) const
|
||||
{
|
||||
void UserTable::print(std::ostream &os) const {
|
||||
os << "-------------" << std::endl;
|
||||
for(int i = 0; i != getNbrUsers(); ++i) {
|
||||
const auto& u = users[i];
|
||||
os << "(" <<u.getCardNbr() << ") " << u.getName() << std::endl;
|
||||
for (int i = 0; i != getNbrUsers(); ++i) {
|
||||
const auto &u = users[i];
|
||||
os << "(" << u.getCardNbr() << ") " << u.getName() << std::endl;
|
||||
}
|
||||
os << "=============" << std::endl;
|
||||
}
|
||||
/**
|
||||
* Testmetod för binärsökningen:
|
||||
* går igenom alla användare och kollar att deras kortnummer kan sökas upp.
|
||||
* Om något kortnummer inte kunde sökas upp returneras detta. Annars, om
|
||||
* alla sökningar lyckades, returneras 0.
|
||||
*/
|
||||
int testFindNbr(const UserTable ut)
|
||||
{
|
||||
* Testmetod för binärsökningen:
|
||||
* går igenom alla användare och kollar att deras kortnummer kan sökas upp.
|
||||
* Om något kortnummer inte kunde sökas upp returneras detta. Annars, om
|
||||
* alla sökningar lyckades, returneras 0.
|
||||
*/
|
||||
int testFindNbr(const UserTable ut) {
|
||||
for (int i = 0; i < ut.n; i++) {
|
||||
int nbr = ut.users[i].getCardNbr();
|
||||
User found = ut.find(nbr);
|
||||
|
@ -119,4 +111,3 @@ int testFindNbr(const UserTable ut)
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,28 +5,27 @@
|
|||
|
||||
#include "User.h"
|
||||
|
||||
class UserTable{
|
||||
public:
|
||||
class UserTable {
|
||||
public:
|
||||
UserTable();
|
||||
UserTable(const std::string&);
|
||||
~UserTable() {delete[] users;}
|
||||
UserTable(const std::string &);
|
||||
~UserTable() { delete[] users; }
|
||||
|
||||
void addUser(const User&);
|
||||
void addUser(const User &);
|
||||
User find(int) const;
|
||||
User find(std::string) const;
|
||||
|
||||
int getNbrUsers() const {
|
||||
return n;
|
||||
}
|
||||
int getNbrUsers() const { return n; }
|
||||
|
||||
void print(std::ostream&) const;
|
||||
void print(std::ostream &) const;
|
||||
|
||||
static const User user_not_found;
|
||||
private:
|
||||
|
||||
private:
|
||||
int capacity{1000};
|
||||
void ensureCapacity(int);
|
||||
int n{0};
|
||||
User* users;
|
||||
User *users;
|
||||
|
||||
friend int testFindNbr(const UserTable ut);
|
||||
};
|
||||
|
|
42
lab3/UserTableTest.cc
Normal file
42
lab3/UserTableTest.cc
Normal file
|
@ -0,0 +1,42 @@
|
|||
#include "UserTable.h"
|
||||
#include "User.h"
|
||||
#include <cassert>
|
||||
|
||||
int main() {
|
||||
{ /* User Related Tests */
|
||||
// Two identical users
|
||||
User u1 = User(1234, "Name Nameson");
|
||||
User u2 = User(1234, "Name Nameson");
|
||||
|
||||
assert(u1.getName() == u2.getName());
|
||||
assert(u1.getCardNbr() == u2.getCardNbr());
|
||||
|
||||
// Two different users
|
||||
User u3 = User(1200, "Name Surname");
|
||||
User u4 = User(1201, "Name Nameson");
|
||||
|
||||
assert(u3.getName() != u4.getName());
|
||||
assert(u3.getCardNbr() != u4.getCardNbr());
|
||||
}
|
||||
{ /* Table Related Tests */
|
||||
UserTable tbl1 = UserTable();
|
||||
assert(tbl1.find("Jens Holmgren") == UserTable::user_not_found);
|
||||
tbl1.addUser(User(1200, "Table Yes"));
|
||||
assert(tbl1.getNbrUsers() == 1);
|
||||
}
|
||||
{
|
||||
// Assumes 'users.txt' is present, containing
|
||||
// line separated records formatted as:
|
||||
// "89524 Adam Abrahamsson"
|
||||
UserTable tbl = UserTable("users.txt");
|
||||
assert(tbl.getNbrUsers() > 0);
|
||||
assert(tbl.find("Jens Holmgren") != UserTable::user_not_found);
|
||||
|
||||
tbl.addUser(User(1200, "Name Surname"));
|
||||
User ufind1 = tbl.find(1200);
|
||||
assert(ufind1 != UserTable::user_not_found);
|
||||
|
||||
User ufind2 = tbl.find("Name Surname");
|
||||
assert(ufind2 != UserTable::user_not_found);
|
||||
}
|
||||
}
|
59
lab4/.vscode/settings.json
vendored
Normal file
59
lab4/.vscode/settings.json
vendored
Normal 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
74
lab4/Sieve.cc
Normal 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
32
lab4/TagRemover.cc
Normal 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("<"), "<");
|
||||
result = std::regex_replace(result, std::regex(">"), ">");
|
||||
result = std::regex_replace(result, std::regex(" "), " ");
|
||||
result = std::regex_replace(result, std::regex("&"), "&");
|
||||
|
||||
output << result;
|
||||
}
|
||||
};
|
||||
|
||||
int main() {
|
||||
TagRemover tr(std::cin);
|
||||
tr.print(std::cout);
|
||||
return 0;
|
||||
}
|
76
lab4/date.cc
76
lab4/date.cc
|
@ -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"
|
||||
|
||||
int Date::daysPerMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
|
||||
// Konstruktor: dagens datum
|
||||
Date::Date() {
|
||||
time_t timer = time(0); // time in seconds since 1970-01-01
|
||||
tm* locTime = localtime(&timer); // broken-down time
|
||||
year = 1900 + locTime->tm_year;
|
||||
month = 1 + locTime->tm_mon;
|
||||
day = locTime->tm_mday;
|
||||
time_t timer = time(0); // tid i sekunder sedan 1970-01-01
|
||||
tm* locTime = localtime(&timer); // lokal tid
|
||||
year = 1900 + locTime->tm_year;
|
||||
month = 1 + locTime->tm_mon;
|
||||
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 {
|
||||
return 0;
|
||||
return year;
|
||||
}
|
||||
|
||||
int Date::getMonth() const {
|
||||
return 0;
|
||||
return month;
|
||||
}
|
||||
|
||||
int Date::getDay() const {
|
||||
return 0;
|
||||
return day;
|
||||
}
|
||||
|
||||
// Gå till nästa dag
|
||||
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;
|
||||
}
|
||||
|
|
28
lab4/date.h
28
lab4/date.h
|
@ -1,19 +1,27 @@
|
|||
#ifndef DATE_H
|
||||
#define DATE_H
|
||||
|
||||
#include <iostream>
|
||||
|
||||
class Date {
|
||||
public:
|
||||
Date(); // today's date
|
||||
Date(int y, int m, int d); // yyyy-mm-dd
|
||||
int getYear() const; // get the year
|
||||
int getMonth() const; // get the month
|
||||
int getDay() const; // get the day
|
||||
void next(); // advance to next day
|
||||
Date(); // dagens datum
|
||||
Date(int y, int m, int d); // yyyy-mm-dd
|
||||
int getYear() const; // returnerar året
|
||||
int getMonth() const; // returnerar månaden
|
||||
int getDay() const; // returnerar dagen
|
||||
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:
|
||||
int year; // the year (four digits)
|
||||
int month; // the month (1-12)
|
||||
int day; // the day (1-..)
|
||||
static int daysPerMonth[12]; // number of days in each month
|
||||
int year; // året (fyra siffror)
|
||||
int month; // månaden (1-12)
|
||||
int day; // dagen (1-...)
|
||||
static int daysPerMonth[12]; // antal dagar i varje månad
|
||||
static bool isLeapYear(int year); // kontrollera skottår
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,64 +1,40 @@
|
|||
#include <iostream>
|
||||
#include <iomanip> // for setw and setfill
|
||||
#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() {
|
||||
// Check input and output of dates. Uncomment the following when you
|
||||
// have added operator>> and operator<<.
|
||||
/*
|
||||
bool cont = true;
|
||||
while (cont) {
|
||||
cout << "Type a date: ";
|
||||
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
|
||||
// printing dates more than a month ahead
|
||||
cout << "--- Today and more than a month ahead:" << endl;
|
||||
Date d1;
|
||||
print(d1);
|
||||
cout << endl;
|
||||
for (int i = 1; i <= 35 ; ++i) {
|
||||
d1.next();
|
||||
print(d1);
|
||||
cout << endl;
|
||||
}
|
||||
|
||||
// Check so 'next' functions correctly from one year to the next
|
||||
cout << "--- New Year's Eve and the next day:" << endl;
|
||||
Date d2(2013, 12, 31);
|
||||
print(d2);
|
||||
cout << endl;
|
||||
d2.next();
|
||||
print(d2);
|
||||
cout << endl;
|
||||
// Test input och output
|
||||
bool cont = true;
|
||||
while (cont) {
|
||||
std::cout << "Type a date: ";
|
||||
Date aDate;
|
||||
std::cin >> aDate;
|
||||
|
||||
if (std::cin.eof()) {
|
||||
cont = false;
|
||||
} else if (!std::cin.good()) {
|
||||
std::cout << "Wrong input format" << std::endl;
|
||||
std::cin.clear();
|
||||
std::cin.ignore(10000, '\n');
|
||||
} else {
|
||||
std::cout << "Output: " << aDate << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// Testa 'next' med dagens datum
|
||||
std::cout << "--- Today and more than a month ahead:" << std::endl;
|
||||
Date d1;
|
||||
std::cout << d1 << std::endl;
|
||||
for (int i = 1; i <= 35; ++i) {
|
||||
d1.next();
|
||||
std::cout << d1 << std::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
56
lab4/makefile
Normal 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
61
lab4/reflektion.txt
Normal 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 = {
|
||||
{"<", "<"}, {">", ">"}, {" ", " "}, {"&", "&"}
|
||||
};
|
||||
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
14
lab4/string_cast.h
Normal 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
29
lab4/string_cast_test.cc
Normal 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
10
lab4/toString.h
Normal 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
22
lab4/toString_test.cc
Normal 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;
|
||||
}
|
Loading…
Reference in a new issue