Nie działa dopisywanie do pliku w nowej lini

0

Mam taki kod (wrzucam kawałek) i za każdym razem dopisuje mi w obydwu plikach na końcu linii, a ja chciałbym żeby dopisywał mi tekst w nowej linii. Dlaczego nie działa skoro mam ios::app ?

 void Pierwsza::zapiszDoPliku()
{
	string tekstZapis;
	ofstream zapis;
	zapis.open("text.txt", ios::out | ios::app );
	cout << "Podaj tekst do wprowadzenia do pliku" << endl;
	getline(cin, tekstZapis);
	zapis << tekstZapis;
	zapis.close();

	//drugi zapis
	string zapisanyWyraz;
	ofstream zapis2("zapis2.txt", ios::app );
	cout << "Podaj kolejny tekst do zapisania" << endl;
	getline(cin, zapisanyWyraz);
	zapis2 << zapisanyWyraz;
	zapis2.close();
}



}
0

zapis<<tekstZapis<<endl;

3

Działa, po prostu musisz zapisać również znak nowej linii (z definicji getline usuwa znak nowej linii z oczytanych danych i NIE umieszcza go w docelowym stringu).
Tj. musisz zrobić:

...
zapis2 << zapisanyWyraz << '\n';
// nie warto robić <<endl, bo nie ma potrzeby flushować tuż przed close
zapis2.close()
0
> ##### [Gynvael Coldwind napisał(a)](http://4programmers.net/Forum/1209819): > ```cpp ... zapis2 < zapisanyWyraz < '\n'; // nie warto robić <endl, bo="bo" nie="nie" ma="ma" potrzeby="potrzeby" flushować="flushować" tuż="tuż" przed="przed" close="close" ```="```" warto="warto" robić="robić" '\n'="'\n'" na="na" innym="innym" systemie="systemie" operacyjnym="operacyjnym" będzie="będzie" to="to" '\r'.="'\r'." `flusz`'owanie="`flusz`'owanie" wcale="wcale" wpływa="wpływa" nić,="nić," oprócz="oprócz" tego="tego" że="że" jeżeli="jeżeli" ten="ten" plik="plik" jest="jest" otwarty="otwarty" do="do" czytania="czytania" przez="przez" inny="inny" proces="proces" tamten="tamten" już="już" miał="miał" co="co" czytać,="czytać," czyli="czyli" niejako="niejako" coś="coś" tam="tam" przyśpieszy.</del="przyśpieszy.&lt;/del" zaś="Zaś"> W wyniku dalszej dyskusji wyjaśniło się że jednak @{Gynvael Coldwind} ma racje.
1

Zgodzę się z Tobą, ale tylko trochę ;)
'\r' jako koniec linii ostatni raz był widziany na MacOS 9, który przestał być produkowany 15 lat temu.
Obecnie nie ma popularnego systemu, który używałby innego końca linii niż \n, z wyjątkiem Windows, który, przy otwarciu pliku w trybie tekstowym, sam przetłumaczy \n na \r\n.
Niemniej jednak teoretycznie masz rację, że endl jest fajniejszy, bo jest przenośny, choć w praktyce nie ma to znaczenia.

Co do flushowania, to jak wiesz close() i tak flushuje, więc w tym konkretnym wypadku zrobienie go przed samym close() jest niepotrzebne.
Oczywiście nic to też nie zepsuje, ponieważ wtedy flush wykonywany w ramach close jest bardzo szybkim no-opem.
W innych przypadkach zbyt częste flushowanie (tj. np. gdyby zapisywać 100 linii do pliku) niepotrzebnie spowolni działanie, a jest bardzo mało sytuacji, w który jest potrzebne. Więc powiedziałbym, że użycie endl byłoby złym nawykiem w tym wypadku.

Więc, imo oboje mamy rację, a reszta jest kwestią przyjętych założeń ;)

1

Brakuje tu tylko drobnych konkretów, np o ile promili zwiększy się czas zapisu przy stosowaniu endl względem stosowania \n, jak to sprawdzisz - to wróćmy do tej dyskusji.

3

Nie za bardzo rozumiem - chcesz, żebym Ci udowodnił, że buforowanie w implementacji standardowej biblioteki nie zostało dodane bez powodu? ;)

Test pierwszy - stdout przekierowany na /dev/null - chciałem, aby jak najmniejszy był udział FS był w teście (a więc test sprawdza na ile samo flushowanie bufora, syscall write i dojście do implementacji handlera z /dev/null zajmuje, a raczej, czy jest jakaś różnica z flushowaniem/bez):

#include <iostream>

int main() {
  for (int i = 0; i < 10 * 1000 * 1000; ++i) {
    std::cout << "Jakis tam losowy tekst udajacy prawdziwe dane w niewielkiej "
                 "ilosci, ale takiej, ktora przynajmniej udaje jakies logi."
#ifdef NOFLUSH
              << "\n";
#else
              << std::endl;
#endif
  }
}

I odpalenie na Ubuntu (po 3 razy per test):

21:35:04 gynvael:haven-linux> g++ -Wall -Wextra -O3 test.cc -o test_flush
21:35:15 gynvael:haven-linux> g++ -Wall -Wextra -O3 test.cc -o test_noflush -DNOFLUSH
21:35:17 gynvael:haven-linux> time ./test_flush > /dev/null

real	0m1.940s
user	0m0.877s
sys	0m1.053s
21:35:24 gynvael:haven-linux> time ./test_flush > /dev/null

real	0m1.913s
user	0m0.947s
sys	0m0.963s
21:35:27 gynvael:haven-linux> time ./test_flush > /dev/null

real	0m1.942s
user	0m0.898s
sys	0m1.033s
21:35:29 gynvael:haven-linux> time ./test_noflush > /dev/null

real	0m0.599s
user	0m0.526s
sys	0m0.067s
21:35:34 gynvael:haven-linux> time ./test_noflush > /dev/null

real	0m0.596s
user	0m0.539s
sys	0m0.056s
21:35:36 gynvael:haven-linux> time ./test_noflush > /dev/null

real	0m0.596s
user	0m0.552s
sys	0m0.036s
21:35:37 gynvael:haven-linux> 

Średnia różnica (real):

(1.942+1.940+1.913) / (0.599+0.596+0.596)
3.235...
A więc endl jest ponad 3 razy wolniejsze.

Test drugi - to samo, tylko na plikach (do pliku w tmp fs piszę):

#include <fstream>

int main() {
  std::ofstream f("/tmp/test");
  for (int i = 0; i < 10 * 1000 * 1000; ++i) {
    f << "Jakis tam losowy tekst udajacy prawdziwe dane w niewielkiej "
         "ilosci, ale takiej, ktora przynajmniej udaje jakies logi."
#ifdef NOFLUSH
      << "\n";
#else
      << std::endl;
#endif
  }
}

I uruchomienie:

21:39:56 gynvael:haven-linux> g++ -Wall -Wextra -O3 testf.cc -o testf_flush
21:40:13 gynvael:haven-linux> g++ -Wall -Wextra -O3 testf.cc -o testf_noflush -DNOFLUSH
21:40:22 gynvael:haven-linux> ./testf_flush 
21:40:39 gynvael:haven-linux> time ./testf_flush 

real	0m18.493s
user	0m1.576s
sys	0m11.438s
21:41:06 gynvael:haven-linux> time ./testf_flush 

real	0m19.596s
user	0m1.291s
sys	0m11.510s
21:41:27 gynvael:haven-linux> time ./testf_flush 

real	0m18.137s
user	0m1.526s
sys	0m11.560s
21:41:47 gynvael:haven-linux> time ./testf_noflush 

real	0m11.679s
user	0m0.064s
sys	0m1.590s
21:42:02 gynvael:haven-linux> time ./testf_noflush 

real	0m12.601s
user	0m0.042s
sys	0m1.533s
21:42:16 gynvael:haven-linux> time ./testf_noflush 

real	0m12.665s
user	0m0.056s
sys	0m1.494s

Średnio:

(18.493+19.596+18.137) / (11.679+12.601+12.665)
1.521
Lepiej, choć nadal widocznie wolniejsze. I nadal nie są to promile ;)

Podsumowując, nadal jestem zdania, że dawanie bez powodu endl jest złym nawykiem i warto go używać wtedy, kiedy faktycznie chcemy coś zflushować.
I, że się powtórzę, nadal uważam, że w przypadku tego topicu jest to zupełnie bez znaczenia, bo tam flush jest w ilości: jeden, bezpośrednio przed close.

Edit: miałem drobny błąd w testach ('\n' zamiast "\n"), który jednak na nic nie wpłynął.
Edit2: niepoprawnie napisałem wcześniej, że stdout jest przekierowany "przez pipe" do /dev/null - ofc /dev/null jest pseudo-urządzeniem z własnym interfejsem pseudo-plikowym, więc nie ma tam żadnych potoków ;)

0

Super, może teraz ja podsumuję twoje testy.
O testach z konsolą zapomnij bo wszystko zależy od tego jaki masz terminal.
Poza tym użytkownik i tak nie zdąży przetrawić tego w tym tempie.

Natomiast co do plików, widzę że maksymalna różnica jest prawie 7 sek na 10 mln endl/'\n' podzielmy to, mamy 7E-7 sek, czyli 0.7 mikrosekundy oszczędności?
Przypominam autor zapisuje jeden wiersz.
Radzę też sprawdzić jak to wygląda pod windows, bo podając testy tylko pod linuksa podajesz tylko 2% prawdy: http://www.ranking.pl/pl/rankings/operating-systems.html

0
_13th_Dragon napisał(a):

Nie warto robić '\n' bo na innym systemie operacyjnym będzie to '\r'. (...)

Gynvael Coldwind napisał(a):

(...) Niemniej jednak teoretycznie masz rację, że endl jest fajniejszy, bo jest przenośny, choć w praktyce nie ma to znaczenia. (...)

Nie. '\n' to uniwersalny znak końca linii. Implementacja zastosuje odpowiedni znak/znaki (np. \r\n na Windowsie) w zależności czego używa. Takiego zachowania wymaga standard i '\n' jest przenośne, a std::endl nie robi niczego więcej w tym względzie. (Oczywiście mówimy o plikach tekstowych)

Nie ma więc żadnego zysku na "przenośności" z używania std::endl. Skro nie ma tego zysku a jest ewentualna strata szybkości, używanie std::endl w celu osiągnięcia tego samego co \n wydaje się być pozbawione logiki.

0
Endrju napisał(a):

... a std::endl nie robi niczego więcej w tym względzie ...
To też jest w standardzie?
Gdyby nie robił nic więcej to nie powinno być różnicy czasowej (w obu przypadkach sprowadza się do wywołania funkcji), czyż nie?

2
_13th_Dragon napisał(a)

O testach z konsolą zapomnij bo wszystko zależy od tego jaki masz terminal.
Poza tym użytkownik i tak nie zdąży przetrawić tego w tym tempie.

Ehm, jakie testy z konsolą? ^_-
Testy, które wkleiłem nie dotykają w ogóle konsoli - w pierwszym przypadku, tak jak napisałem, stdout jest przekierowany do /dev/null.

W ogóle mam wrażenie, że nie czytasz od A do Z moich postów, tylko rzucasz na nie okiem i biegniesz odpowiedzieć.
Np. dwa posty temu napisałem:

Gynvael Coldwind napisał(a)

Co do flushowania, to jak wiesz close() i tak flushuje, więc w tym konkretnym wypadku zrobienie go przed samym close() jest niepotrzebne.
Oczywiście nic to też nie zepsuje, ponieważ wtedy flush wykonywany w ramach close jest bardzo szybkim no-opem.

Post temu napisałem:

Gynvael Coldwind napisał(a)

I, że się powtórzę, nadal uważam, że w przypadku tego topicu jest to zupełnie bez znaczenia, bo tam flush jest w ilości: jeden, bezpośrednio przed close.

Po czym w swojej odpowiedzi znowu adresujesz przypadek jednego flusha, o którym ja zupełnie nie wspominam i o nim nie piszę.

_13th_Dragon napisał(a)

Natomiast co do plików, widzę że maksymalna różnica jest prawie 7 sek na 10 mln endl/'\n' podzielmy to, mamy 7E-7 sek, czyli 0.7 mikrosekundy oszczędności?
Przypominam autor zapisuje jeden wiersz.

Przyznaję, że zastanawiałem się post wcześniej, czy chodzi Ci o ten konkretny przypadek jednego flusha (mimo, iż napisałem, że w tym wypadku dyskusja jest nieistotna), czy o mój komentarz, że zbyt częste flushowanie (tj. "np. gdyby zapisywać 100 linii do pliku") niepotrzebnie spowolni działanie (ofc 100 w tym wypadku to dość mało i raczej chodziło mi o sensowną większą ilość). Założyłem, że jednak przeczytałeś mój post i pytasz o to drugie - stąd moje testy.

Napisz więc plz z którymi fragmentami moich dotychczasowych wypowiedzi się zgadzasz, a z którymi nie i dlaczego - wtedy będzie jasne o czym dyskutujemy i będzie sens kontynuować.

Ad "bo podając testy tylko pod linuksa podajesz tylko 2% prawdy"
To jest 100% prawdy dla GNU/Linux, a nie 2% prawdy ;)
Co więcej, GNU/Linux jest na przynajmniej 30% serwerów.

Natomiast z ciekawości i tak zrobiłem testy na Windowsie:

#include <iostream>
#include <fstream>
#include <time.h>

int main() {
  clock_t bench = clock();

  std::ofstream f("/tmp/test");
  for (int i = 0; i < 10 * 1000 * 1000; ++i) {
    f << "Jakis tam losowy tekst udajacy prawdziwe dane w niewielkiej "
         "ilosci, ale takiej, ktora przynajmniej udaje jakies logi."
#ifdef NOFLUSH
      << '\n';
#else
      << std::endl;
#endif
  }

  bench = clock() - bench;
  std::cerr << "Bench: " << double(bench) / 1000. << "\n";  
}

clock() ma rozdzielczość 1ms i update time 1ms na Windows 7 (vide http://gynvael.coldwind.pl/n/time_functions) więc wystarczy.

Wyniki:

gynvael:haven-windows> testf_flush
Bench: 24.952

gynvael:haven-windows> testf_flush
Bench: 24.937

gynvael:haven-windows> testf_flush
Bench: 24.991

gynvael:haven-windows> testf_noflush
Bench: 12.224

gynvael:haven-windows> testf_noflush
Bench: 12.382

gynvael:haven-windows> testf_noflush
Bench: 12.288

Czyli jest jeszcze gorzej, q.e.d. ;)

Z ciekawostek dodam, że >nul jest jakoś dziwnie na Windowsie zaimplementowane i w jego przypadku wyniki są w zasadzie identyczne. Ciekawe, ale nieistotne dla dyskusji ;)

0
_13th_Dragon napisał(a):
Endrju napisał(a):

... a std::endl nie robi niczego więcej w tym względzie ...
To też jest w standardzie?
Gdyby nie robił nic więcej to nie powinno być różnicy czasowej (w obu przypadkach sprowadza się do wywołania funkcji), czyż nie?

W tym względzie tj. odnośnie znaku nowej linii. Odnośnie tego właśnie znaku nie robi niczego więcej, bo dokładnie tak wygląda definicja std::endl:

Effects: Calls os.put(os.widen(’\n’)), then os.flush().

(C++14 27.7.3.8/1)

Czyli normalnie wstawia '\n' a potem wywołuje flush. Nie różni się to (koncepcyjnie) od zrobienia s << '\n'; s.flush();. To z tej drugiej operacji wynika ewentualna różnica w czasie, natomiast wstawiane jest samo '\n' niezależnie od tego, czy to Windows czy Linux czy cokolwiek.

Gdyby ktoś jeszcze miał wątpliwości to tutaj C++ Core Guidelines na ten temat: https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#sl50-avoid-endl (To taki dokument tworzony przez "możnych" C++, m.in. Bjarne)

1

@Gynvael Coldwind, kiedy ostatnio robiłem taki test (a było to sporo lat temu) to różnica pod windows'ami stanowiła tyle ile wywołanie funkcji.
Czyli jedynie fout<<"abc"<<endl; przegrywało z fout<<"abc\n"; ale z fout<<"abc"<<'\n'; już nie.
Widocznie sporo się zmieniło od tamtego czasu.

Jedyne co, to może dla logów jednak należało by wybrać opcję z endl ponieważ w przypadku wywalenia się systemu wiersz już nie zginie.

1 użytkowników online, w tym zalogowanych: 0, gości: 1