#include "CsvFeldolgozo.hpp"
#include "DatumKezeles.hpp"
#include <sstream>
#include <vector>
#include <iostream>
#include <tuple>
using namespace std;
CsvFeldolgozo::CsvFeldolgozo(const string& bemeneti_fajl,
const string& szoveges_kimenet, // A szöveges fájl emberi és eszközökkel (Excel, script) kezelhető.
const string& binaris_kimenet) // A bináris fájl hatékony, strukturált adatkezelésre hasznos, write()/read() függvényekkel
: bemeneti_fajl(bemeneti_fajl),
szoveges_kimenet(szoveges_kimenet),
binaris_kimenet(binaris_kimenet)
{}
void CsvFeldolgozo::fajl_megnyitas() {
be_fajl.open(bemeneti_fajl, //.open() második paramétere egy bitmask, amiben a különböző ios-flag-eket bit OR-ral (|) kombináljuk.
ios::in); // ::in A fájlt olvasásra (input) nyitja meg
if (!be_fajl.is_open())
throw runtime_error("Nem nyitható be: " + bemeneti_fajl); // throw kulcsszó célja: hibák („kivételes állapotok”) jelzése futás közben
// Amikor a program ezt a sort eléri, azonnal kilép a jelenlegi függvényből,
// és keresni kezdi a megfelelő catch ágat, amelyik kezelni tudja azt a kivételt
szoveges_fajl.open(szoveges_kimenet,
ios::out | ios::trunc); // ::out - Megnyitja a fájlt író (output) módban, Ha csak ez lenne beállítva, akkor a fájl elejétől kezdve írunk
if (!szoveges_fajl.is_open()) // ha nem adsz meg ios::app-et, akkor a kezdő pozíció az eleje lesz
throw runtime_error("Nem nyitható ki: " + szoveges_kimenet);// ::trunc - („truncate”) Ha a fájl már létezik, azonnal nullára törli a tartalmát, mielőtt bármit írnánk bele
binaris_fajl.open(binaris_kimenet,
ios::out | ios::binary | ios::trunc); // ::binary - A fájl bináris módú megnyitását kéri.
if (!binaris_fajl.is_open())
throw runtime_error("Nem nyitható bináris ki: " + binaris_kimenet);
}
void CsvFeldolgozo::fajl_bezaras() {
be_fajl.close();
szoveges_fajl.close();
binaris_fajl.close();
}
string CsvFeldolgozo::vagas(const string& s) {
auto l = s.find_first_not_of(" \t\r\n"); // Megkeresi az első olyan karakter indexét (l), ami nem tartozik a " (szóköz), \t (tab),
// \r (carriage return), \n (newline) halmazba.
if (l == string::npos) return "";
// string::npos Jelentése: „nincs találat” vagy „érvénytelen pozíció”
auto r = s.find_last_not_of(" \t\r\n"); // Megkeresi az utolsó olyan karakter indexét (r), ami nem whitespace
return s.substr(l, r - l + 1); // Visszaadja a bemeneti stringnek azt a részét, ami az l és r index közé esik beleértve mindkettőt.
}
bool CsvFeldolgozo::datum_utani_feldolgozas(const string& bekert_datum) {
// dátum bontása: év,hónap,nap
auto [bekert_ev, bekert_honap, bekert_nap] = DatumKezeles::bontott_datum(bekert_datum); // C++17-es featureök: a auto típusdedukció és strukturált kicsomagolás (structured binding).
// auto–típusdedukció: Nem kell kézzel kiírni a bal oldalon a 3-elemű tuple<int,int,int> típust
// fájlok megnyitása Ha később a visszatérési típus változik, az auto-val nem kell módosítani.
fajl_megnyitas(); // [bekert_ev, bekert_honap, bekert_nap] — strukturált kicsomagolás
// bontott_datum visszaad egy tuple<int,int,int> objektumot, amiben az év, hónap és nap van.
// fejléc eldobása Létrehoz három lokális változót (ev, honap, nap)
string sor;
getline(be_fajl, sor);
int sorszam = 1;
// soronkénti beolvasás ÉS feldolgozás
while (getline(be_fajl, sor)) {
++sorszam;
sor_feldolgozas(sor, sorszam,
bekert_ev, bekert_honap, bekert_nap); // auto[…]-val létrehozott változók teljes egészében lokálisak abban a függvényben,
} // és bármely belső kódrészletben (például a while-ciklusban) ugyanúgy használhatók,
// mint bármely más lokális változó
// hibadetektálás
if (!be_fajl.eof() && be_fajl.fail()) { // Ha már csak az EOF- miatt nem tudunk több sort olvasni, azt nem tekintjük hibának itt.
// A failbit vagy a badbit beálltát jelzi: failbit akkor, ha a formázott beolvasás sikertelen (pl. rossz típusú adat)
cerr << "[fail] Hiba olvasás közben\n"; // badbit akkor, ha súlyosabb, nem helyreállítható hiba lépett fel (pl. I/O hiba)
fajl_bezaras();
return false;
}
fajl_bezaras();
return true;
}
void CsvFeldolgozo::sor_feldolgozas(const string& sor,
int sorszam,
int bekert_ev, int bekert_honap, int bekert_nap)
{
// 1) sor felbontása
stringstream ss(sor);
vector<string> mezok;
string cella;
while (getline(ss, cella, ';'))
mezok.push_back(cella);
// 2) oktató neve (4. mező)
//string oktato = DatumKezeles::vagas(mezok[3]); // tudjuk, hogy a 4. mezőben lesz
string oktato = (mezok.size() > 3 ? CsvFeldolgozo::vagas(mezok[3]) : ""); // defenzív programozási minta: ha valamiért mégis olyan sor kerülne be,
// amiben nincs meg a 4. mező (azaz mezok[3]), akkor ne dereferáljunk
// out-of-range indexet, hanem adjunk vissza egyszerűen egy üres stringet,
// és ne essen szét a program
// 3) időpont-párok: indexek (7,8), (9,10), (11,12)
for (int j = 7; j <= 11; j += 2) { // menjünk kettesével += 2
if (j + 1 >= (int)mezok.size()) break; // size_t -> int
// 3a) kezdő és vég cellák vágása
string kezdes_full = CsvFeldolgozo::vagas(mezok[j]);
string vege_full = CsvFeldolgozo::vagas(mezok[j + 1]);
// 3b) üres cellákat kihagyjuk
if (/*kezdes_full.empty() || vege_full.empty() ||*/
kezdes_full == "-" || vege_full == "-")
continue;
//// 4) ha nem lenne időrész csak dátum pótoljuk " 00:00"-val
//if (kezdes_full.find_first_of(" \t") == string::npos)
// kezdes_full += " 00:00";
//if (vege_full.find_first_of(" \t") == string::npos)
// vege_full += " 00:00";
// 5) levágott date-only rész (a regexhez)
auto pk = kezdes_full.find_first_of(" \t");
auto pv = vege_full.find_first_of(" \t");
string kezdes_datum = kezdes_full.substr(0, pk);
string vege_datum = vege_full.substr(0, pv);
// 6) YYYY.MM.DD validálása
if (!regex_match(kezdes_datum, datum_minta) ||
!regex_match(vege_datum, datum_minta)) {
cout << "[HIBA] Sor " << sorszam
<< ": nem szabványos dátum: '"
<< kezdes_datum << "' vagy '" << vege_datum << "'\n";
continue;
}
// 7) bontott dátum numerikus összehasonlításra
auto [zh_ev, zh_honap, zh_nap] = DatumKezeles::bontott_datum(kezdes_datum);
// 8) cutoff utáni esetek
if (make_tuple(zh_ev, zh_honap, zh_nap)
> make_tuple(bekert_ev, bekert_honap, bekert_nap)) {
// 9) konzolra minden találat
cout << sorszam << ": "
<< oktato << " "
<< kezdes_full << " – " << vege_full << "\n";
// 10) szövegfájlba
szoveges_fajl << oktato << ";"
<< kezdes_full << "-" << vege_full << "\n";
// 11) bináris fájlba
string adat = oktato + " " + kezdes_full + "-" + vege_full;
uint32_t hossz = uint32_t(adat.size());
binaris_fajl.write(reinterpret_cast<char*>(&hossz), sizeof(hossz));
binaris_fajl.write(adat.data(), adat.size());
// **NINCS** break; — így a sor minden ZH-párját kiírjuk
}
}
}