• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Przenoszenie obiektu z jednego pliku do drugiego

VPS Starter Arubacloud
+1 głos
284 wizyt
pytanie zadane 3 kwietnia 2021 w C i C++ przez DzikieHarce Użytkownik (690 p.)
Staram się napisać bazę danych w c++, dla biblioteki. Chcę napisać funkcję która będzie przepisywała jedną osobę z pliku Wolny.txt do pliku Dluznik.txt. Chodzi o obsługę wypożyczeń. Program pyta o id osoby która chce wypożyczyć książkę, potem przepisuje tą osobę z jednego pliku do drugiego, ale zostawia w poprzednim całą resztę. W programie są dwie klasy Wolny i Dluznik dziedziczące po klasie Osoby. Wszystkie klasy mają trzy zmienne, zapisanie w tej kolejności: nazwisko, imię, id. Moim pomysłem jest włożyć wszystkie osoby z pliku Wolny.txt do listy, a do drugiej dać tylko tę osobę która ma odpowiednie id. Potem ponownie zapisać do pliku Wolny.txt wszystkie dane z pierwszej listy, a z drugiej dopisać jedynie tę jedną osobę. Ewentualnie wczytać wszystko z pliku Wolny.txt do listy, potem ją przeszukać i ew. przepisać do innej, jeśli znajdzie się id o właściwym numerze, a potem usunąć z pierwszej listy tylko ten jeden obiekt. Nie mam jednak pojęcia jak zabrać się do pisania tych funkcji.
komentarz 3 kwietnia 2021 przez Jakub 0 Pasjonat (23,120 p.)
edycja 3 kwietnia 2021 przez Jakub 0
Słyszałaś może o czymś takim jak JSON? Myślę, że może Ci się to przydać.
komentarz 3 kwietnia 2021 przez DzikieHarce Użytkownik (690 p.)

Na razie zrobiłam tyle, jednak mam problemy z zapisywaniem ;D Ale z JSON'em zamierzam się zapoznać, dzięki za radę!

void przepisz(int idSzuk)
{
	list<string> listaW;
	list<string> listaD;
	ifstream plikW;
	fstream plikD;
	string linia;
	string imie, nazwisko, id;
	plikW.open("Wolny.txt");
	plikD.open("Dluznik.txt", ios::app);
	int nrOsoby = 1;
	int nrLinii;
	int i = 1, szuk;
	int licznik = 1;
	while (getline(plikW, linia))
	{
		nrLinii = (nrOsoby - 1) * 3 + 1;
		nrOsoby++;
		if (licznik == nrLinii)
		{
			listaW.push_back(nazwisko);

		}
		if (licznik == nrLinii + 1)
		{
			listaW.push_back(imie);

		}
		if (licznik == nrLinii + 2)
		{

			stringstream geek(linia);
			geek >> szuk;
			id = to_string(szuk);
			if (szuk == idSzuk)
			{
				listaD.push_back(nazwisko);
				listaD.push_back(imie);
				listaD.push_back(id);
			}
			else
			{
				listaW.push_back(id);
			}
		}
		licznik++;
	}
	plikW.close();
	//Wpisywanie do plików
	ofstream plikW;
	plikW.open("Wolny.txt");
	string zmienna;
	while (!listaW.empty())
	{
	
		plikW << listaW;
	}
	while (!listaD.empty())
	{

	}

}

 

komentarz 3 kwietnia 2021 przez tkz Nałogowiec (42,000 p.)
JSON jest zbędny. Parsowanie go jest zbędnym kosztem. Lepiej nada się CSV, jeżeli chcemy w ogóle ustandaryzować format pliku. Co IMO jest nadal zbędne...

Twoja funkcja jest za długa. Stwórz strukturę jako pojemnik na dane o użytkowniku, czytając plik twórz obiekt zawierający dane z pliku, dodaj do tablicy obiektów(lista wszystkich użytkowników). Przy wypożyczaniu przenieś z tablicy wolny do dłużnik.
komentarz 3 kwietnia 2021 przez DzikieHarce Użytkownik (690 p.)
Z tablicą też można zrobić, jednak chciałabym przy pomocy listy. Poprzednie funkcje robiłam właśnie z tablicami dynamicznymi, i teraz chciałabym wytestować działanie list. Dlatego chciałabym wiedzieć czy idę dobrym tropem( jeśli chodzi o listy) i czy w ten sposób mogłabym zapisać dane z list do pliku?
komentarz 3 kwietnia 2021 przez tkz Nałogowiec (42,000 p.)
Czy wczytywanie i zapisywanie pliku za każdym razem ma sens? Operację na dysku są chorobliwie wolne.
Dlaczego std::list? Szukanie jest O(n), co jest stosunkowo wolne. Lepiej skorzystać z mapy, konkretnie z std::unorderd_map z czasem O(1) przy wstawianiu i szukaniu(oczywiście czas zamortyzowany). Twoim "key" będzie id, a "value" będzie obiekt użytkownik, który będzie przechowywał dwie wartości imię i nazwisko.

Drobne podsumowanie. Twoja funkcja jest skrajnie nieoptymalna pod względem czasu i pamięci.

Korzystając z obiektu fstream, masz dwie "specjalizację". ifstream do do czytania i ofstream do zapisu. Korzystaj również z konstruktora jaki zapewniają. Rozbicie tego na osobne otwarcie pliku jest bezsensu.

Nie piszesz w C, że musisz deklarować wszystko na samym początku. Zmienne deklarujesz najbliżej użycia jak tylko się da.

Jeżeli dane są uporządkowane, wychodzę z założenia, że są. To tę linię while (getline(plikW, linia)) zastąp while (plikW>>id>>imie>>nazwisko)

W mojej ocenie cała funkcja jest do przerobienia.
komentarz 3 kwietnia 2021 przez DzikieHarce Użytkownik (690 p.)
Pewnie masz rację, wybacz dopiero się uczę ;D Mógłbyś pokazać mniej więcej jak powinnam to przerobić?
1
komentarz 3 kwietnia 2021 przez tkz Nałogowiec (42,000 p.)
#include <fstream>
#include <iostream>
#include <string>
#include <unordered_map>

struct user
{
    long id;
    std::string name;
    std::string surname;
};

std::unordered_map<long, user> readFile(std::ifstream &file)
{
    auto users = std::unordered_map<long, user>(1000);
    //akurat moj plik ma 1000 uzytkownikow
    //poszukaj jak pobrac wielkosc pliku
    long id;
    std::string name;
    std::string surname;
    while (file >> id >> name >> surname)
    {
        //jedyna wada jest taka, ze przy nazwiskach wieloczlonowych
        //sie wysypie
        //kwestia, czy chcesz to ogarnac
        users.emplace(id, user{id, name, surname});
    }
    return users;
}

int main()
{
    std::ifstream file("data.csv");
    auto users = readFile(file);
    for (const auto &i : users)
    {
        std::cout << i.first << ' '
                  << i.second.name << ' '
                  << i.second.surname
                  << '\n';
    }
    {
        //szukanie po id
        auto findUser = users.find(54); //użytkoniwk o id 54
        std::cout << findUser->second.name << ' '
                  << findUser->second.surname << '\n';
    }
    {
        //mozna tez tak, jezeli nie ma takiego uzytkowniak, to bedziesz miala problem
        //lepszym wyborem na start jest gorne rozwiazanie
        auto user = users[54];
        std::cout << user.name << ' ' << user.surname << '\n';
    }
}

 

komentarz 4 kwietnia 2021 przez DzikieHarce Użytkownik (690 p.)
Dzięki, chyba rozumiem. Istnieje możliwość że takiego użytkownika nie będzie, bo miał on już wypożyczone książki, więc już będzie w pliku Dluznik.txt, więc drugi sposób nie za bardzo, ale warto wiedzieć że i tak się da ;D Dzięki wielkie, powinnam sobie już poradzić.
1
komentarz 4 kwietnia 2021 przez tkz Nałogowiec (42,000 p.)
std::optional<user> findById(long id, const std::unordered_map<long, user> &users)
{
    if (auto findUser = users.find(id); findUser != users.end())
        return findUser->second;
    return std::nullopt;
}

int main()
{
    {
        auto user = findById(5, users);
        std::cout << "optional-------------\n";
        if (user)
        {
            std::cout << user.value().name << '\n';
            std::cout << user.value().surname << '\n';
        }
        std::cout << "optional-------------\n";
        user = findById(1001, users);
        if (user)
        {
            std::cout << user.value().name << '\n';
            std::cout << user.value().surname << '\n';
        }
    }
}

Można też tak, w mojej opinii bardziej zgodnie ze sztuką. 
Co do linków: https://en.cppreference.com/w/cpp/utility/optional/operator_bool dlaczego if(user) - może wydawać się dziwne na początku. 
I sam optional https://en.cppreference.com/w/cpp/utility/optional

komentarz 4 kwietnia 2021 przez DzikieHarce Użytkownik (690 p.)

Chyba rozumiem ;D Na razie udało mi się na listach, teraz biorę się za mapy, warto przećwiczyć obie opcje. Bardzo mi się przydała twoja pomoc, dozgonna wdzięczność ^^ A jeśli ktoś by był ciekawy, tutaj mój kod z listami:

void przepisz(int idSzuk)
{
	list<string> listaW;
	list<string> listaD;
	ifstream plikW;
	fstream plikD;
	string linia;
	string imie, nazwisko, id;
	plikW.open("Wolny.txt");
	plikD.open("Dluznik.txt", ios::out | ios::app);
	int nrOsoby = 1;
	int nrLinii;
	int i = 1, szuk;
	int licznik = 1;
	while (plikW>>nazwisko>>imie>>szuk)
	{
			if (szuk == idSzuk)
			{
				id = to_string(szuk);
				listaD.push_back(nazwisko);
				listaD.push_back(imie);
				listaD.push_back(id);
			}
			else
			{
				id = to_string(szuk);
				listaW.push_back(nazwisko);
				listaW.push_back(imie);
				listaW.push_back(id);
			}
			//}
			licznik++;
	}
		plikW.close();
		//Wpisywanie do plików
		ofstream plikW2;
		plikW2.open("Wolny.txt");
		string zmienna;
		for (list<string>::iterator it = listaW.begin(); it != listaW.end(); it++)
		{
			zmienna = *it;
			plikW2 <<endl << zmienna;
			plikW2.flush();
		}
		for (list<string>::iterator it = listaD.begin(); it != listaD.end(); it++)
		{
			zmienna = *it;
			plikD << endl << zmienna;
			plikD.flush();
		}
		plikW.close();
		plikD.close();
}

Pomysł z while(plikW>>nazwisko>>imie>>id) o wiele lepszy, nie wiem czemu na to nie wpadłam wcześniej.

1
komentarz 4 kwietnia 2021 przez tkz Nałogowiec (42,000 p.)
void przepisz(int idSzuk)
{
    using namespace std;
    list<string> listaW;
    list<string> listaD;
    fstream plikW("Wolny.txt", ios::in | ios::out /*| ios::app o ile chcesz*/);
    fstream plikD("Dluznik.txt", ios::out | ios::app);
    int szuk;
    string imie, nazwisko;
    while (plikW >> nazwisko >> imie >> szuk)
    {
        if (szuk == idSzuk)
        {
            listaD.push_back(nazwisko);
            listaD.push_back(imie);
            listaD.push_back(to_string(szuk));
        }
        else
        {
            listaW.push_back(nazwisko);
            listaW.push_back(imie);
            listaW.push_back(to_string(szuk););
        }
    }
    for (const auto &i : listaW)
    {
        plikW << endl
              << i;
    }
    for (const auto &i : listaD)
    {
        plikD << endl
              << i;
    }
}

Nieco uproszczone, nie wiem, czy się kompiluje, nie miałem jak sprawdzić. 

komentarz 4 kwietnia 2021 przez DzikieHarce Użytkownik (690 p.)
Twoje wygląda znacznie lepiej, nie rozumiem tylko jak działają te pętle for. Pobierają osobno dane z listy? To czemu tam jest auto, skoro w liście są tylko stringi?
1
komentarz 4 kwietnia 2021 przez tkz Nałogowiec (42,000 p.)
Słówko auto "domyśla" się typu, nie muszę pisać go ręcznie. Co do pętli: https://en.cppreference.com/w/cpp/language/range-for i https://www.geeksforgeeks.org/range-based-loop-c/ drugi link jest przystępniejszy.
https://cppinsights.io/ wrzuć gotowy kod, pokaże dokładne rozbicie pętli range for.

Zaloguj lub zarejestruj się, aby odpowiedzieć na to pytanie.

Podobne pytania

0 głosów
2 odpowiedzi 221 wizyt
0 głosów
2 odpowiedzi 376 wizyt
0 głosów
0 odpowiedzi 574 wizyt

92,451 zapytań

141,261 odpowiedzi

319,073 komentarzy

61,853 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Akademia Sekuraka 2024 zapewnia dostęp do minimum 15 szkoleń online z bezpieczeństwa IT oraz dostęp także do materiałów z edycji Sekurak Academy z roku 2023!

Przy zakupie możecie skorzystać z kodu: pasja-akademia - użyjcie go w koszyku, a uzyskacie rabat -30% na bilety w wersji "Standard"! Więcej informacji na temat akademii 2024 znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...