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

Zastępowanie fragmentu tekstu w pliku (struktura danych)

Object Storage Arubacloud
0 głosów
936 wizyt
pytanie zadane 26 czerwca 2015 w C i C++ przez bather Nowicjusz (160 p.)

Cześć, 

wymyśliłem sobie funkcję w programie, dzięki której będzie można edytować wprowadzonego pracownika. Całość opiera się na dynamicznie zaalokowaniej strukturze danych. Pesel jest swego rodzaju ID - po nim rozpoznaję użytkownika i przechodzę do następnych pól. Ogólnie jakoś to działa :) 

Problem jest właśnie ze zmienianiem danej wartości... Jeśli otworzę plik w trybie 'out' to jak się domyślacie - nowe wpisy zastępują mi cały plik a nie tylko wybraną linię. Jeśli z kolei otworzę dodatkowo w trybie 'app' - również niespodzianki nie ma - wszystko dopisuje się na końcu. 

Czy jest jakiś sposób żeby zastąpić linię tekstu (string) innym wprowadzonym stringiem? 

Kod całej funkcji: 

void edytuj ()
{
    cout << "Podaj nr PESEL pracownika, ktorego dane chcesz edytowac: " << endl;
    string pesel_tmp;
    cin >> pesel_tmp;
    cout << endl;

    fstream plik;
    plik.open("pracownicy.txt", ios::in); 

    if (plik.good()==false)
    {
        cout << "Blad! Plik nie istnieje!" << endl;
        exit(0);
    }

    string linia;
    bool flag=0;
    int nr_linii=1;

    while (getline(plik, linia))
    {
        //if(nr_linii%1 == 0 && nr_linii!=0)
        if(nr_linii!=0)
        {
            if (linia == pesel_tmp)
            {
                flag=1;
                break;
            }
        }
        nr_linii++;
    }

    //cout << "Flaga: "<< flag << endl << endl;

    if (flag==1)
    {
       cout << "Ponizej dane pracownika o numerze PESEL: " << linia << endl;
    }
    if (flag==0)
        cout << "Nie znaleziono pracownika o numerze PESEL: " << pesel_tmp << endl;


    string imie_buf, nazwisko_buf, stanowisko_buf, stawka_buf;
    int aktualny_nr=nr_linii; //tu chyba mozna sie usmiechnac :)

    while (getline(plik, linia))
    {
        if (aktualny_nr==nr_linii)      imie_buf = linia;
        if (aktualny_nr==nr_linii+1)    nazwisko_buf = linia;
        if (aktualny_nr==nr_linii+2)    stanowisko_buf = linia;
        if (aktualny_nr==nr_linii+3)    stawka_buf = linia;
        ++aktualny_nr;
    }

    if (flag==1)
    {
        cout << "1. Imie: " << imie_buf << endl;
        cout << "2  Nazwisko: " << nazwisko_buf << endl;
        cout << "3. Stanowsko: " << stanowisko_buf << endl;
        cout << "4. Stawka godzinowa brutto (w PLN): " << stawka_buf << endl;


        int licz; 
        bool flaga=1;

        plik.close();

        fstream plik;
        plik.open("pracownicy.txt", ios::out | ios::app); //do zapisu
        for (licz=1; flaga==1; licz++)
        {
            cin.ignore(); 
            cout << endl << "Podaj numer pozycji, ktora chcesz edytowac: ";
            int pozycja;
            cin >> pozycja;
            cin.ignore(); 
            if (pozycja==1)
            {
                cout << "Podaj nowa wartosc dla pola Imie: ";
                getline (cin, wsk -> imie);
                plik << (wsk -> imie) << endl;
            }
            if (pozycja==2)
            {
                cout << "Podaj nowa wartosc dla pola Nazwisko: ";
                getline (cin, wsk -> nazwisko);
                plik << (wsk -> nazwisko) << endl;
            }
            if (pozycja==3)
            {
                cout << "Podaj nowa wartosc dla pola Stanowisko: ";
                getline (cin, wsk -> stanowisko);
                plik << (wsk -> stanowisko) << endl;
            }
            if (pozycja==4)
            {
                cout << "Podaj nowa wartosc dla pola Stawka: ";
                getline (cin, wsk -> stawka);
                plik << (wsk -> stawka) << endl;
            }

            HANDLE uchwyt;
            uchwyt = GetStdHandle(STD_OUTPUT_HANDLE);
            SetConsoleTextAttribute(uchwyt, FOREGROUND_RED | FOREGROUND_INTENSITY  );
            cout << endl << "Jesli chcesz edytowac kolejne pole nacisnij  T  a nastepnie  ENTER." << endl;
            cout << "Aby zakonczyc nacisnij  N  a nastepnie ENTER." << endl;
            cout << "Wybor: ";
            SetConsoleTextAttribute(uchwyt, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY );
            char decyzja;
            cin >> decyzja;
            cout << endl;

            if ((decyzja == 'T') || (decyzja == 't'))
                flaga=1;
            else
            {
                cout << "Dziekujemy. Zmieniono " << licz << " pola." << endl;
                flaga=0;
            }
        }
    }

    if (flag==0)
        cout << endl << "Upewnij sie, ze wpisales prawidlowy numer." << endl;
    plik.close();


    HANDLE uchwyt;
    uchwyt = GetStdHandle(STD_OUTPUT_HANDLE); 
    SetConsoleTextAttribute(uchwyt, FOREGROUND_RED | FOREGROUND_INTENSITY  );
    cout << endl << "Aby powrocic do menu nacisnij dowolny klawisz...";
    SetConsoleTextAttribute(uchwyt, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY );
    getch();
}

 

Z góry dziękuję za wszelkie pomysły :) 

3 odpowiedzi

+2 głosów
odpowiedź 26 czerwca 2015 przez draghan VIP (106,230 p.)

Jest na pewno sposób, żeby ustawić "kursor pisania" na konkretną pozycję w pliku. Z tym, że musisz znać numer znaku, bo metoda która umożliwia przesunięcie kursora, pobiera w argumencie ilość bajtów przesunięcia.

Ta metoda to :

istream::seekp(streamoff ile_bajtow, ios_base::seekdir wzgledem_czego);

Żeby poznać numer znaku, musiałbyś najpierw wczytać wcześniejsze linie, pobierać ich rozmiar i sumować... No niewygodnie, niefajnie. W dodatku musiałbyś zadbać o to, żeby nie nadpisać znaku nowej linii oraz żeby nie pozostawić żadnego z dotychczasowych znaków.

Powiem Ci, że wygodniej Ci będzie postąpić w taki sposób:

  1. Wczytujesz całą zawartość pliku do zmiennej std::string.
  2. Znajdujesz w niej odpowiednią linię (np. szukając w pętli wystąpienia znaku '\n' i zliczając obiegi pętli).
  3. Wycinasz zawartość danej linii (pomocą do 2 i 3 niech Ci będą metody std::string::find() oraz std::string::erase()).
  4. Za pomocą metody std::string::insert() wstawiasz swoją nową linię.
  5. Nadpisujesz plik swoim zmienionym stringiem.

Radzę poćwiczyć taką operację w osobnym porjekcie, bez plików, tylko ze sztywno zakodowanym stringiem. :)

0 głosów
odpowiedź 26 czerwca 2015 przez rafal.budzis Szeryf (85,260 p.)
Kolega draghan dobrze mówi ale jak bym na twoim miejscu zrobił stałą długość lini zakładajmy ze każda linia bedzie miała 255 znaków tablica char powinna zapisać się w takiej postaci

(twoje dane) (znak null mówiący o końcu stringa) (losowe znaki wypełniające tablice do 255 znaku)

wtedy łatwo obliczyć przeskok chcesz 20 linijke mnozysz 255 przez 20 i bedziesz mógł użyć funkcji seek

Przetestuj obie metody ocenisz która lepiej się prezentuje :)
komentarz 26 czerwca 2015 przez draghan VIP (106,230 p.)

Jak na moje oko, to rozwiązanie ma dwie wady i jedną zaletę.

Zaleta jest taka, że - jak piszesz - łatwo wtedy dostaniesz się do każdego fragmentu pliku, który Cię interesuje.

Dwie wspomniane wady, to:

  1. Sztuczne ograniczanie długości danych (w przypadku imion, nazwisk i tym podobnych to akurat ma małe znaczenie, ale tym od razu ograniczasz skalowalność projektu).
  2. Duża nadmiarowość zapisanych danych.

Dla małych projektów to jeszcze jest okej.

komentarz 26 czerwca 2015 przez rafal.budzis Szeryf (85,260 p.)
draghan ja widze jeszcze kilka plusów wzgldem twojej metody

- nie musze wczytywać całego pliku zeby odczytać pojedynczą wartość (oszczedność RAM)
- nie musze ciać pliku po znakach enter a wykonywanie takiej operacji jest złożone stringi to najbardziej czasochłonne zadania komputer znacznie lepiej radzi sobei z liczbami (oszczednośc mocy obliczeniowej)

o wiele bardziej wole nadużywać dysk twardy niż RAM i Procesor

chociaż najlepiej to ja bym własny system plików napisał :D

----- nagłówek pliku ----

(długość nagłówka)(ilość lini)(pozycja lini 20n)

----- dane ---

(długość stringa)(string)
(długość stringa)(string)
(długość stringa)(string)
(długość stringa)(string)
(długość stringa)(string)
[...]

 

Dzięki wprowadzeniu nagłówka mógłbym przeskoczyć o 20,40,60,... linijek zeby szybciej odszukac własciwą i nie miał bym już takiego nadmiaru zapisanego.
komentarz 26 czerwca 2015 przez draghan VIP (106,230 p.)

- nie musze wczytywać całego pliku zeby odczytać pojedynczą wartość (oszczedność RAM)

No okej, ale chcąc przejrzeć wszystkie dane i tak trzeba wczytać... wszystkie dane. :) A co jeśli chcesz przeszukać plik dwa razy? Trzy? Za każdym razem będziesz pracował bezpośrednio na pliku? Czy może lepiej plik wczytać raz i operować na wartościach w pamięci?

- nie musze ciać pliku po znakach enter a wykonywanie takiej operacji jest złożone stringi to najbardziej czasochłonne zadania komputer znacznie lepiej radzi sobei z liczbami (oszczednośc mocy obliczeniowej)

Stringi to przecież (dynamiczne) tablice char, wyszukanie znaku nowej linii to proste przejrzenie tablicy znakowej. Przecież char to też jest liczba. Ewentualny dodatkowy narzut związany jest z wycięciem i wstawieniem fragmentu... Co i tak musisz zrobić w pliku i będzie to operacja wolniejsza, niż w RAM.

komentarz 26 czerwca 2015 przez rafal.budzis Szeryf (85,260 p.)
draghan mając 100 wierszy nie warto wyświetlać wszystkich lepiej zrobić stronnicowanie xD z resztą nie bronie zapisać całosći do RAM ale zróbmy to na fladze bool i wczytujmy tylko jesli chcemy pokazać całoś a nie cały czas.

co do stringów własnie dynamika czyni je wolnymi porównajmy operacje dla pokazania tylko 5 wiersza :D
u ciebie

1) czytany jest cały plik a string się powieksza dynamicznie z kilka razy co powoduje szukanie odpowiedniego miejsca na RAM jak wiesz tablice mają to do ciebie ze ich indeksy są jeden po drugim jesli miałeś stringa miedzy jakimiś danymi może on przestać się miescić i bedzie musiał byc przeniesiony w inne miejsce pamieci.

2) leci for po tablicy char wyszukuje "\n" ostatni napotkany i przed ostatni napotkany znak nowej lini

3) tniesz według tych 2 zmiennych

4) wyświetlasz
 

u mnie

1) obliczasz 5 * wielkość wiersza

2) odczytujesz z dysku tylko fragment pliku od 5* wilekośc wiersza do 6* wielkosć wiersza

3) wyświetlam

 

Co jest szybsze mnożenie dwoch liczb czy szukanie 5 wystąpienia nowej lini w tablicy ? :D
–1 głos
odpowiedź 26 czerwca 2015 przez Gariw Użytkownik (920 p.)
Daj samo out w linii 71: plik.open("pracownicy.txt", ios::out | ios::app);

Czyli tak: plik.open("pracownicy.txt",ios::out);
komentarz 26 czerwca 2015 przez bather Nowicjusz (160 p.)
Oczywiście próbowałem tego na samym początku ;) Ale tak jak napisałem w treści pytania, wtedy nadpisywany jest cały plik, więc tracę pozostałe dane...
komentarz 26 czerwca 2015 przez Gariw Użytkownik (920 p.)
To zobacz sobie seekg i tyle w temacie.

Podobne pytania

0 głosów
2 odpowiedzi 258 wizyt
pytanie zadane 30 stycznia 2017 w C i C++ przez czujek22 Dyskutant (7,670 p.)
0 głosów
1 odpowiedź 572 wizyt
pytanie zadane 30 lipca 2020 w PHP przez Bakkit Dyskutant (7,600 p.)
+1 głos
3 odpowiedzi 1,469 wizyt

92,568 zapytań

141,424 odpowiedzi

319,634 komentarzy

61,956 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

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy 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!

...