-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathaggiorna.cpp
340 lines (305 loc) · 13.4 KB
/
aggiorna.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
/*
Purpose: Aggiornamenti (aggiorna)
Author: Emanuele Rizzolo
Class: 3CIN
Date: 2017/05/02
Note:
*/
/*
Aggiornamenti (aggiorna)
È dato un file di testo (il “dizionario”) contenente le definizioni delle
parole in lingua S. In tale lingua le parole contengono da un minimo di tre
fino ad un massimo di dieci caratteri latini e ciascuna parola può avere
più definizioni (fino ad un massimo di 26), a seconda dell’ambito in cui
viene utilizzata.
Di conseguenza il dizionario è formato da più righe ciascuna con il seguente
formato:
<parola><:><n><spazio><definizione>
dove <parola> è la parola definita, <:> è un separatore costante (:),
<n> è una singola lettera maiuscola dell'alfabeto latino indicante l'ambito
della definizione, <spazio> è un singolo spazio separatore e <definizione>
è una frase (di un'unica riga) che spiega il significato della parola.
Ad esempio, il dizionario può contenere le seguenti righe:
ancora:A Elemento in ferro o acciaio, proprio... [omissis]
ancora:B Elemento di ferro dolce che comandato da... [omissis]
ancora:C Nei moderni orologi, parte dello scappamento... [omissis]
Periodicamente vengono rilasciati degli aggiornamenti per inserire i
neologismi, eliminare le voci obsolete o correggere le definizioni imprecise.
Il file di testo che descrive gli aggiornamenti è formato da più righe
(massimo 1000), ciascuna con il seguente formato:
<operazione><parola><:><n>[<spazio><definizione>]
dove <operazione> è un singolo carattere indicante il tipo di modifica da effettuare,
<parola> è la parola cui la modifica deve essere applicata, <:> è un separatore
costante (:), <n> è una singola lettera maiuscola dell'alfabeto latino indicante
l'ambito della definizione, <spazio> è un singolo spazio separatore e <definizione>
è una frase (di un'unica riga) che spiega il significato della parola.
Questi ultimi due elementi sono presenti solo per alcuni tipi di modifiche.
I caratteri indicanti le possibili modifiche sono i seguenti:
+ nuova parola o nuova definizione in un nuovo ambito (definizione presente)
/ parola obsoleta nell'ambito, voce da rimuovere (definizione assente)
= parola già definita nell'ambito, definizione sostitutiva (definizione presente)
Ad esempio, l'aggiornamento può contenere può contenere le seguenti righe:
/ancora:B
=ancora:C Nei vecchi orologi, parte dello scappamento... [omissis]
+ancora:D Nel linguaggio HTML, elemento che... [omissis]
in base alle quali il nuovo dizionario verrebbe ad essere:
ancora:A Elemento in ferro o acciaio, proprio... [omissis]
ancora:C Nei vecchi orologi, parte dello scappamento... [omissis]
ancora:D Nel linguaggio HTML, elemento che... [omissis]Esercizi
Scrivere un'applicazione che riceva tre parametri da riga comando, indicanti
rispettivamente il (nome del) file di dizionario attuale, il file degli aggiornamenti ed
il file del nuovo dizionario, produca quest'ultimo leggendo i dati dei primi due.
Facoltativamente, considerare il dizionario attuale e l'aggiornamento ordinati per
parola/ambito e produrre un nuovo dizionario anch'esso ordinato.
Facoltativamente, prevedere quali anomalie si possono verificare (ad esempio
modifica di rimozione per una voce non presente) e scrivere su un (altro) file (di log)
dei messaggi di errore significativi per ciascuna anomalia.
*/
// directive for standard io functions
#include <iostream>
#include <fstream>
#include <sstream>
#include <cstring>
#include <limits>
#include <algorithm>
using namespace std;
const bool DEBUG = true;
#define INSERT ('+')
#define DELETE ('/')
#define UPDATE ('=')
#define PROCESSED (' ')
const char VALID_OPERATIONS[]{INSERT, DELETE, UPDATE, 0}; // make it a null terminated string
#define MAX_WORD_LENGTH (30)
#define MAX_DEFINITION_LENGTH (1000)
#define MAX_ENTRY_LENGTH (MAX_WORD_LENGTH + 3 + MAX_DEFINITION_LENGTH)
#define BUFFER_SIZE (1 + MAX_ENTRY_LENGTH)
struct entry_t // a dictionary entry
{
char word[MAX_WORD_LENGTH + 1];
char scope;
char definition[MAX_DEFINITION_LENGTH + 1];
};
struct update_t // an update entry
{
char operation;
entry_t entry;
};
#define MAX_UPDATES (1000)
// update_t updates[MAX_UPDATES]; global ?
bool decodeEntry(char *line, entry_t &entry); // decodes an entry from a string
bool writeEntry(ostream &file, const entry_t &entry, bool newLine = true); // writes an entry to file
bool decodeUpdate(char *line, update_t &update); // decodes an update from a string
int compareEntries(const entry_t &first, const entry_t &second); // compare entries
size_t readUpdates(istream &file, update_t updates[]); // read all updates from file
void sortUpdates(update_t updates[], size_t numUpdates); // sort all updates
size_t searchUpdate(update_t updates[], size_t nUpdates, const entry_t &entry); // search for a matching update
bool lessByEntry(const update_t &lhs, const update_t &rhs); // comparison function
void log(const char *msg, const char *details = ""); // simple text
void log(const entry_t &e, const char *details = ""); // an entry
void log(const update_t &u, const char *details = ""); // an update
// global log file, used by all log functions
ofstream logFile("dizionario.log", ios::app); // explicit open append mode
int main(int argc, char *argv[])
{
// check parameters
if (argc != 4)
log("Wrong number of parameters.");
else
{
ifstream dictFile(argv[1]); // open dictionary file
if (!dictFile)
log("Could not open dictionary file ", argv[1]);
else
{
ifstream updFile(argv[2]); // open update file
if (!updFile)
log("Could not open update file ", argv[2]);
else
{
ofstream outFile(argv[3]); // open output file
if (!outFile)
log("Could not open output file ", argv[3]);
else
{
update_t *updates = new update_t[MAX_UPDATES]; // allocate space for updates
if (updates == nullptr)
log("Cannot allocate memory for updates");
else
{
size_t nUpdates = readUpdates(updFile, updates); // read all updates
sortUpdates(updates, nUpdates); // sort them to apply binary search
if (DEBUG)
for (size_t i = 0; i < nUpdates; i++)
{
cout << i;
cout << updates[i].operation;
writeEntry(cout, updates[i].entry);
cout << endl;
}
char line[BUFFER_SIZE]; // buffer for a line
while (!dictFile.eof())
{
entry_t entry; // an entry
if (dictFile.getline(line, BUFFER_SIZE) && decodeEntry(line, entry)) // try to read a line and decode an entry
{
if (DEBUG)
{
cout << "searching for ";
writeEntry(cout, entry);
}
size_t where = searchUpdate(updates, nUpdates, entry);
if (DEBUG)
{
cout << " found at " << where << "/" << nUpdates << endl;
}
if (where < nUpdates) // found
{
switch (updates[where].operation)
{
case DELETE:
if (DEBUG)
{
cout << "deleting entry ";
writeEntry(cout, entry);
}
break;
case INSERT:
log(updates[where], " Entry already exist:");
log(entry, " will be modified with new definition");
case UPDATE:
strcpy(entry.definition, updates[where].entry.definition);
writeEntry(outFile, entry);
}
updates[where].operation = PROCESSED; // processed
}
else
writeEntry(outFile, entry);
}
}
for (size_t u = 0; u < nUpdates; u++)
{
if (updates[u].operation != PROCESSED)
{
if (updates[u].operation == INSERT)
writeEntry(outFile, updates[u].entry);
else
{
log(updates[u], " No match found");
}
}
}
delete[] updates; // deallocate memory
}
outFile.close();
}
updFile.close();
}
dictFile.close();
}
}
logFile.close(); // just in case
return 0;
}
size_t readUpdates(istream &file, update_t updates[])
{
size_t result = 0;
char line[BUFFER_SIZE]; // buffer for a line
while (!file.eof())
{
file.getline(line, BUFFER_SIZE);
if (decodeUpdate(line, updates[result])) // try to read a line and decode an update
++result;
}
return result;
}
bool lessByEntry(const update_t &lhs, const update_t &rhs) // comparison function
{
int cmp = strcmp(lhs.entry.word, rhs.entry.word);
return cmp < 0 || (cmp == 0 && (lhs.entry.scope < rhs.entry.scope));
}
void sortUpdates(update_t updates[], size_t numUpdates) // sort all updates
{
sort(updates, updates + numUpdates, lessByEntry);
}
char *decodeWordAndScope(char *line, entry_t &entry) // decodes an entry from a string
{
char *colon = strchr(line, ':');
if (colon == nullptr) // not a correct entry
return nullptr;
*colon = 0; // word terminator
strcpy(entry.word, line); // copy word to entry
entry.scope = colon[1]; // copy scope to entry
if ('A' > entry.scope || entry.scope > 'Z') // invalid scope
return nullptr;
return colon + 2;
}
bool decodeEntry(char *line, entry_t &entry) // decodes an entry from a string
{
char *next = decodeWordAndScope(line, entry);
if (next == nullptr || *next != ' ') // no space
return false;
strcpy(entry.definition, next + 1); // copy definition to entry
return true;
}
bool decodeUpdate(char *line, update_t &update) // decodes an update from a string
{
if (strchr(VALID_OPERATIONS, line[0]) == nullptr) // invalid operation
return false;
update.operation = line[0]; // store operation to update
return (update.operation == DELETE && decodeWordAndScope(line + 1, update.entry)) ||
decodeEntry(line + 1, update.entry); // process rest of line
}
int cmpScope(const char lhs, const char rhs)
{
if (lhs < rhs)
return -1;
if (lhs > rhs)
return 1;
return 0;
}
int cmpWithEntry(const update_t &lhs, const entry_t &rhs) // comparison function
{
int cmp = strcmp(lhs.entry.word, rhs.word); // compare words first
return cmp == 0 ? cmpScope(lhs.entry.scope, rhs.scope) : cmp; // then scopes
}
size_t searchUpdate(update_t updates[], size_t nUpdates, const entry_t &entry) // search for a matching update
{
size_t result = nUpdates, end = nUpdates, count = nUpdates, begin = 0;
while (count > 0)
{
size_t middle = begin + count / 2;
int cmp = cmpWithEntry(updates[middle], entry);
if (cmp < 0)
begin = middle + 1;
else
{
end = middle;
if (cmp == 0)
result = middle;
}
count = end - begin;
}
return result;
}
bool writeEntry(ostream &file, const entry_t &entry, bool newLine /* = true */)
{
file << entry.word << ":" << entry.scope << " " << entry.definition;
if (newLine)
file << endl;
return file.fail();
}
void log(const char *msg, const char *details /* = "" */) // simple text
{
logFile << msg << details << endl;
}
void log(const entry_t &e, const char *details /* = "" */) // an entry
{
writeEntry(logFile, e, false);
logFile << details << endl;
}
void log(const update_t &u, const char *details /* = "" */) // an update
{
logFile << u.operation;
writeEntry(logFile, u.entry, false);
logFile << details << endl;
}