labamba63 Temat założony przez niniejszego użytkownika |
[C++] Odczyt listy jednokierunkowej z pliku do struktury » 2016-12-29 14:31:03 Cześć, dopiero zaczynam przygodę z programowaniem - zabrałem się za pewien projekt, z którym już walczę kilka dni ;) Chciałbym prosić o pomoc w rozwiązaniu problemu. Program w pierwszym etapie generuje pliki wynikowe za każdy miesiąc, nastepujące pliki wyglądają w takiej formie: Plik: styczen.txt Zestawienie za styczen 01-01-2016 4095, Kowalski, 160, wynagrodzenie 500, Tarnowski, 12, usluga 2590, Nowak, 120, wynagrodzenie Plik: luty.txt Zestawienie za luty 01-02-2016 3800, Kowalski, 150, wynagrodzenie 500, Tarnowski, 10, usluga 2990, Nowak, 131, wynagrodzenie Struktura miała postać: int kwota; string nazwisko; int czas_pracy; string rodzaj; Plik ten został wygenerowany przy pomocy listy jednokierunkowej, z dodaniem na początku pliku informacji: w 1 wierszu: Za jaki miesiąc jest to zestawienie w 2 wierszu: Data Teraz muszę te pliki odczytać i wpisać z niego kilka informacji do struktury, chciałbym aby program z pliku utworzył mi nowe zestawienia dla poszczególnych pracownikow czyli na przykład powstanie coś takiego: Plik: Kowalski.txt 01-01-2016 Zestawienie za styczen 4095 01-02-2016 Zestawienie za luty 3800 Z wygenerowaniem do pliku sobie poradzę tylko jak wypełnić tą strukturę danymi z pliku. Zatrzymałem się już na samym początku problemu, ponieważ o ile łatwo jest odczytać plik, gdy każdą daną mamy w innym wierszu to jak sobie poradzić z czymś, gdzie dane są wypisane w jednym wierszu i oddzielone przecinkami ze spacją, jak pomijać przecinki... Póki co tak wygląda początek mojego kodu: #include <iostream> #include <string> #include <fstream>
using namespace std;
struct pracownik { string data; string zestawienie; int kwota; pracownik * next; };
int main() { fstream plik; plik.open( "styczen.txt", ios::in ); if( plik.good() == false ) { cout << "Wystapil problem z plikiem\n"; system( "pause" ); return 1; } string linia; int nr_linii = 1; string zestawienie, data; while( getline( plik, linia ) ) { switch( nr_linii ) { case 1: zestawienie = linia; break; case 2: data = linia; break; case 3: } nr_linii++; }
Po case 3 wydaje mi się, że przydałaby się jakaś pętla, która wracałaby do nr_linii 2 a później ją inkrementowała znów do 3, przy czym wartości w case 3 powinny się zmieniać wraz z przejściem pętli. |
|
michal11 |
» 2016-12-29 16:35:47 Nie czytałem kodu, ale najprościej będzie pewnie wczytywać wyraz z przecinkiem i jeżeli ostatni znak stringa to właśnie przecinek to go usuwać. Możesz też pobawić się getline i wczytywać całą linijkę i odpowiednio ją parsować ale tutaj moim zdaniem za dużo roboty by było. Edit. Ok, widzę, że wczytujesz linię getlinem, w takim razie możesz skorzystać ze stringstream aby odpowiednio ją sparsować, mniej więcej tak int kwota; std::string nazwisko; int czas_pracy; std::string rodzaj; char comma;
std::istringstream iss( linia ); iss >> kwota >> comma >> nazwisko >> czas_pracy >> comma >> rodzaj; nazwisko.erase( nazwisko.length() - 1 );
albo od razu przerabiać linię na twoją strukturę lub zwrócić interesująca cię wartość. int ParseLine( std: string line ) { int kwota; std::string nazwisko; int czas_pracy; std::string rodzaj; char comma; std::istringstream iss( linia ); iss >> kwota >> comma >> nazwisko >> czas_pracy >> comma >> rodzaj; nazwisko.erase( nazwisko.length() - 1 ); return kwota; }
swoją drogą takie tworzenie listy struct pracownik { pracownik * next; };
jest moim zdaniem niepotrzebne i błędogenne, jeżeli już chcesz to robić ręcznie to skorzystaj ze smart pointerów a najlepiej w ogóle z jakiegoś standardowego kontenera std::list<> lub std::vector ( w twoim przypadku pewnie vector). |
|
labamba63 Temat założony przez niniejszego użytkownika |
» 2016-12-29 16:59:40 Dzięki, z przecinkiem poradziłem sobie w ten sposób: fstream plik; plik.open( "styczen.txt", ios::in );
if( plik.good() == false ) { cout << "Wystapil problem z plikiem\n"; system( "pause" ); return 1; } string linia; int licznik = 0;
while( !plik.eof() ) { getline( plik, linia, ',' ); cout << linia << endl; licznik++; }
zmieniłem nieco pętlę while teraz już zamiast getline sprawdza po prostu czy plik się nie skończył, getline jest wrzucony do pętli i ma warunek zapisywania do napotkania przecinka: "getline(plik, linia, ',')" cout dodałem testowo, żeby zobaczyć jak są wychwytywane zmienne, po odpaleniu programu wygląda to tak, że plik, który ma formę: Zestawienie za styczen 01-01-2016 4095, Kowalski, 160, wynagrodzenie 500, Tarnowski, 12, usluga 2590, Nowak, 120, wynagrodzenie Został przedstawiony w takiej formie: Zestawienie za styczen 01-01-2016 4095 Kowalski 160 wynagrodzenie 500 Tarnowski 12 usluga 2590 Nowak 120 wynagrodzenie Mianowicie zostały jeszcze spacje po przecinkach, których program nie usunął i nie wiem jak się tego pozbyć za bardzo. W pierwszym wierszu mam zmienną, która ma spacje i tam nie chciałbym ich kasować, natomiast wszystkie występujące po przecinkach są mi zbędne jakieś pomysły? ;) I jak mogę to teraz wpisać w strukturę. Edit: Takie tworzenie listy nie przypadkowe, w projekcie nie mogę używać kontenerów bibliotecznych czyli właśnie na przykład vector. |
|
michal11 |
» 2016-12-29 18:28:30 Miałeś dobry kod to go popsułeś, napisałem ci, że możesz kombinować z getline ale to jest za dużo zabawy z tymi spacjami i przecinkami. Napisałem ci coś takiego #include <iostream> #include <string> #include <fstream> #include <sstream>
struct Pracownik { std::string data; std::string zestawienie; int kwota; Pracownik * next; };
struct FileData { int Amount; std::string Name; int WorkTime; std::string Description; };
FileData ParseFileLine( const std::string & line ) { int kwota; std::string nazwisko; int czas_pracy; std::string rodzaj; char comma; std::istringstream iss( line ); iss >> kwota >> comma >> nazwisko >> czas_pracy >> comma >> rodzaj; nazwisko.erase( nazwisko.length() - 1 ); return FileData { kwota, nazwisko, czas_pracy, rodzaj }; }
std::ostream & operator <<( std::ostream & out, const FileData & data ) { out << data.Amount << "\t" << data.Name << "\t" << data.WorkTime << "\t" << data.Description; return out; }
int main() { using std::cout; using std::endl; using std::cin; std::fstream plik; plik.open( "styczen.txt", std::ios::in ); if( !plik.good() ) { cout << "Wystapil problem z plikiem\n"; system( "pause" ); return 1; } std::string zestawienie; std::getline( plik, zestawienie ); std::string data; std::getline( plik, data ); std::string line; line.reserve( 150 ); while( std::getline( plik, line ) ) { FileData PracownikData = ParseFileLine( line ); Pracownik nowy { data, zestawienie, PracownikData.Amount }; } return 0; }
nazwy zmiennych są pomieszane bo częściowo pisałem sam a częściowo kopiowałem z poprzednich postów. Myślę, że to jest dobra baza do dalszej rozbudowy twojego programu. |
|
mokrowski |
» 2016-12-29 20:42:20 Zainteresuj się istringstream. Możesz wczytać string do istringstream i operatorem>> wyłuskiwać dane do określonych typów. Wiem że jak sam się uczyłem, to przydawały się takie przykłady. Pisałem maksymalnie łopatologicznie i z premedytacją nie rozwiązywałem wszystkich problemów... #include <iostream> #include <string> #include <fstream> #include <sstream> #include <vector> #include <cstdlib>
using namespace std;
using rekord_pracownika_t = struct { int kwota; string nazwisko; int czas_pracy; string rodzaj; };
using rekordy_z_pliku_t = struct { string nazwa; string data; vector < rekord_pracownika_t > dane; };
void czytaj_dane( istringstream & wejscie, string & dane ); void czytaj_dane( istringstream & wejscie, int & dane );
rekordy_z_pliku_t czytaj_rekord( ifstream & plik ); void wyswietl_rekordy( const rekordy_z_pliku_t & rekordy );
int main() { string pliki_miesiecy[] = { "styczen.txt", "luty.txt" }; rekordy_z_pliku_t rekordy; for( const auto & plik_nazwa: pliki_miesiecy ) { auto plik = ifstream( plik_nazwa ); if( not plik ) { cerr << "Problem z dostępem do pliku " << plik_nazwa << ".\n"; exit( EXIT_FAILURE ); } rekordy = czytaj_rekord( plik ); wyswietl_rekordy( rekordy ); } return EXIT_SUCCESS; }
void czytaj_dane( istringstream & wejscie, string & dane ) { getline( wejscie, dane, ',' ); }
void czytaj_dane( istringstream & wejscie, int & dane ) { string dane_str; getline( wejscie, dane_str, ',' ); dane = stoi( dane_str ); }
rekordy_z_pliku_t czytaj_rekord( ifstream & plik ) { rekordy_z_pliku_t rekordy_plik; string linia; istringstream sstream; getline( plik, rekordy_plik.nazwa ); getline( plik, rekordy_plik.data ); while( getline( plik, linia ) ) { sstream.str( linia ); rekord_pracownika_t rekord; czytaj_dane( sstream, rekord.kwota ); czytaj_dane( sstream, rekord.nazwisko ); czytaj_dane( sstream, rekord.czas_pracy ); czytaj_dane( sstream, rekord.rodzaj ); sstream.clear(); rekordy_plik.dane.push_back( rekord ); } return rekordy_plik; }
void wyswietl_rekordy( const rekordy_z_pliku_t & rekordy ) { cout << "Rekordy z pliku " << plik_nazwa << "\n" << string( 40, '=' ) << "\nNazwa: " << rekordy.nazwa << "\nData: " << rekordy.data << "\nDane z rekordów:\n"; for( const auto & pracownik: rekordy.dane ) { cout << " Nazwisko: " << pracownik.nazwisko << "\n Kwota: " << pracownik.kwota << "\n Czas pracy: " << pracownik.czas_pracy << "\n Rodzaj: " << pracownik.rodzaj << endl; cout << string( 40, '-' ) << endl; } }
|
|
labamba63 Temat założony przez niniejszego użytkownika |
» 2016-12-30 00:43:41 6 godzinek pracy i coś tam się udało napisać ;)
Obecnie mam problem z posortowaniem - macie jakieś pomysły, tylko proszę o jak najprostsze podstawowe rozwiązania, gdyż dopiero raczkuję i np. istringstream podany wyżej był mi nie znany, ale poradziłem sobie jakoś na podstawowych komendach. Póki co program odczytuje zadane zestawienia za kolejne miesiące, tworzy nowy plik, króry ma następującą postać:
Plik: Kowalski.txt
Kowalski - zestawienie
2016-01-01 Zestawienie za styczen 4095 2016-03-01 Zestawienie za marzec 2044 2016-02-01 Zestawienie za luty 3800 2015-06-01 Zestawienie za czerwiec 1800
Teraz ten plik chciałbym posortować według daty - tj. otrzymać taką postać w zawartości:
Kowalski - zestawienie
2015-06-01 Zestawienie za czerwiec 1800 2016-01-01 Zestawienie za styczen 4095 2016-02-10 Zestawienie za luty 3800 2016-03-01 Zestawienie za marzec 2044
Teraz pytanie w jaki sposób mogę posortować takiego stringa z datą? Int'y można by posortować za pomocą utworzenia jakiejś pomocniczej zmiennej i porównywania z poprzednią, ale stringi jak się sortuje zmienne tego typu? Uprzedzając pytanie dlaczego date mam w stringu - W projekcie było wymagane, aby data została zapisana w formacie RRRR-MM-DD, taka forma przyszła mi na myśl żeby zastosować stringa.
|
|
michal11 |
» 2016-12-30 01:34:44 Masz szczęście, że data jest w takim właśnie formacie, wystarczy zwykły operator< dla stringa. #include <iostream> #include <string> #include <algorithm> #include <vector>
struct SomeStruct { std::string Data; int sth; float sth2; SomeStruct( const char * str ) : Data( str ) { } };
std::ostream & operator <<( std::ostream & out, const std::vector < SomeStruct >& dates ) { for( const SomeStruct & date: dates ) { out << date.Data << "\n"; } return out; }
int main() { using std::cout; using std::endl; using std::cin; std::vector < SomeStruct > dates { "2016-01-01", "2016-02-01", "2016-05-01", "2016-09-01", "2016-11-01", "2016-07-01", "2015-06-01" }; std::random_shuffle( dates.begin(), dates.end() ); cout << dates << endl; cout << "sorting" << "\n\n"; std::sort( dates.begin(), dates.end(),[]( const SomeStruct & lhs, const SomeStruct & rhs ) { return lhs.Data < rhs.Data; } ); cout << dates << endl; return 0; }
Możesz też od razu napisać sobie operator porównania dla swojej struktury i nie nie bawić się w lambdy struct SomeStruct { std::string Data; int sth; float sth2; };
bool operator <( const SomeStruct & lhs, const SomeStruct & rhs ) { return lhs.Data < rhs.Data; }
std::sort( dates.begin(), dates.end() );
jeżeli chcesz skorzystać z sort to pewnie będziesz też musiał napisać swoje iteratory ale to nie jest konieczne, możesz użyć swojej funkcji, ważne jest, że możesz to sobie posortować używając stringowego operatora. Aha i sortuj dane przed zapisaniem do pliku. @mokrowski Mam pytanie, dlaczego napisałeś coś takiego using rekord_pracownika_t = struct a nie zwyczajnie struct rekord_pracownika ? |
|
mokrowski |
» 2016-12-30 13:04:59 @michal11 wolę napisać using aby zdefiniować alias typu niż wiele razy w trakcie użycia wpisywać słowo kluczowe struct. W tym przypadku zamiast using mógłbym użyć typedef ale using jest nowocześniejsze i poddaje się łatwemu szablonowaniu. @labamba63 jeśli chcesz sortować, potrzebujesz iteratora o dostępnie swobodnym. Lista tego nie ma a vector ma :-) Tu masz przykład.... #include <iostream> #include <vector> #include <string> #include <sstream> #include <algorithm>
using namespace std;
bool sort_predykat( vector < string >& v1, vector < string >& v2 ) { return v1[ 0 ] > v2[ 0 ]; }
int main() { string dane_str = "140 Ala kot\n250 Franek pies\n250 Zenek chomik\n10 Agata patyczak"; string linia; istringstream sstream; sstream.str( dane_str ); vector < vector < string >> rekordy( 4, vector < string >( 3 ) );; for( size_t i = 0; i < rekordy.size(); ++i ) { vector < string > & wiersz = rekordy[ i ]; getline( sstream, linia ); istringstream linia_strumien; linia_strumien.str( linia ); linia_strumien >> wiersz[ 0 ]; linia_strumien >> wiersz[ 1 ]; linia_strumien >> wiersz[ 2 ]; } sort( rekordy.begin(), rekordy.end(), sort_predykat ); for( size_t i = 0; i < rekordy.size(); ++i ) { vector < string >& vec = rekordy[ i ]; cout << vec[ 1 ] << " ma zwierzątko o nazwie " << vec[ 2 ] << " które kosztuje " << vec[ 0 ] << "PLN.\n"; } }
|
|
« 1 » 2 |