Pętla kopiująca plik wiersz po wierszu - ucina ostatni wiersz

0

Witam

Proszę oświećcie mnie co zrobiłem nie tak bo nie mam pomysłu...
Potrzebuję pętli która będzie odczytywać kolejne wiersze z pliku "wejście.txt" i zapisywać je do pliku "wyjście.txt" (później przy okazji wiersze będą obrabiane).
Kod wydaje się banalny :

 
begin
  AssignFile(Plik,'wejscie.txt');
  AssignFile(Plikw,'wyjscie.txt');
  Reset(Plik);
  Rewrite(Plikw);
  while not eof(Plik) do
    begin
      ReadLn(Plik,Bufor);
      Writeln(plikw, Bufor);
    end;
end;

Jednak efekt jest taki że ostatni wiersz jest ucięty w połowie.... dlaczego ?

Pozdrawiam

1
hipekk napisał(a):

Jednak efekt jest taki że ostatni wiersz jest ucięty w połowie.... dlaczego ?

Buforowany zapis.

pascal kopiowanie pliku

0

Patrząc na ten kod:

begin
  AssignFile(Plik,'wejscie.txt');
  AssignFile(Plikw,'wyjscie.txt');
  Reset(Plik);
  Rewrite(Plikw);
  while not eof(Plik) do
    begin
      ReadLn(Plik,Bufor);
      Writeln(plikw, Bufor);
    end;
end;

widać, że uczysz się nieuważnie;


Po pierwsze
nie podałeś bloku deklaracji zmiennych stąd nie wiadomo co to ten Bufor;

Po drugie
jak otwierasz plik to bądź go łaskaw na koniec zamknąć bo powodujesz wycieki pamięci (nie zamknąłeś ani jednego);

Po trzecie
korzystaj w takich przypadkach z bloku try .. finally .. end - będzie łatwiej zorganizować kod i polepszy to jego czytelność;

Po czwarte
nazewnictwo identyfikatorów - stosuj angielskie nazwy trafnie dopasowane do przeznaczenia danej zmiennej (bo takie jak Plik i Plikw kompletnie nic nie mówią);

Po piąte
jeżeli pliki dość sporo ważą to jest duża szansa, że przepełnisz bufor i zawartość zostanie ucięta; Aby temu uniknąć wystarczy zastosować w odpowiednim miejscu procedurę Flush czyszczącą bufor pliku;

1

jeżeli pliki dość sporo ważą to jest duża szansa, że przepełnisz bufor i zawartość zostanie ucięta; Aby temu uniknąć wystarczy zastosować w odpowiednim miejscu procedurę Flush czyszczącą bufor pliku;

Wszyscy lubimy bajki. Ja jednak proponuję trzymać się faktów.

Po czwarte
nazewnictwo identyfikatorów - stosuj angielskie nazwy trafnie dopasowane do przeznaczenia danej zmiennej

Jak ktoś chce to niech stosuje polskie, byle oddawały przeznaczenie zmiennej. I tutaj akurat oddaje znośnie.

nie podałeś bloku deklaracji zmiennych stąd nie wiadomo co to ten Bufor;

I na co Ci to... Przecież do przedstawienia problemu nie jest potrzebne.

0
-123oho napisał(a)

Wszyscy lubimy bajki. Ja jednak proponuję trzymać się faktów.

A co, bufor pewnie nigdy się nie przepełni, a procedura Flush jest pewnie zbędna...?

Przy większych plikach owszem, że może się przepełnić i zawartość zostanie ucięta; Przerabiałem to już kilkakrotnie i wiem co pisze;

Tak samo skąd mam wiedzieć jakiego typu jest Bufor? Może to być praktycznie dowolny typ, niekoniecznie String;

0

niekoniecznie String;

Newbie raczej nie korzystają z tablic dynamicznych czy wskaźników (PChar itp.).

1

A co, bufor pewnie nigdy się nie przepełni, a procedura Flush jest pewnie zbędna...?

Zgadłeś.

Przy większych plikach owszem, że może się przepełnić i zawartość zostanie ucięta; Przerabiałem to już kilkakrotnie i wiem co pisze;

Proof or it didn't happen.

Tak samo skąd mam wiedzieć jakiego typu jest Bufor? Może to być praktycznie dowolny typ, niekoniecznie String;

Hm, a mi się wydawało że akurat ReadLn działa ze stringami?

Co za idiotyzm, poprosiłem o więcej informacji a tu zaraz minusują, jakbym nie wiadomo co napisał... Ja obstawiam przy swoim i wiem, że się nie mylę;

Witam w świecie -123oho. Moim zdaniem i tak obstawiasz źle.

0
-123oho napisał(a):

Tak samo skąd mam wiedzieć jakiego typu jest Bufor? Może to być praktycznie dowolny typ, niekoniecznie String;

Hm, a mi się wydawało że akurat ReadLn działa ze stringami?

Var S: Pointer;
Begin
 GetMem(S, 1024);
 Readln(String(S));
 Writeln(String(S));
 Readln;
End.

:P

2

Pliki się zamyka. Koniec filozofii.

2

Nie wiem jak jest w pascalowcach, ale w C/ C++/ Javie nigdzie nie trzeba wywoływać flush() jeśli nie ma takiej potrzeby. Tzn flush() jest po to, żeby natychmiast zrzucić zawartość bufora na dysk i zwrócić sterowanie dopiero po potwierdzeniu zapisu. Jeśli zapisujemy do pliku, obojętnie czy buforowanego czy nie, nie dostaniemy żadnego przepełnienia bufora z d**y, bo biblioteka standardowa sama sobie będzie zrzucać dane na dysk z bufora (o ile taki bufor będzie). O ile nie będziemy robić flush() za każdym razem to zapis może się odbywać z pewnym, nieznanym dla nas, opóźnieniem, przez co np mimo iż nasz program wrzucił wszystkie dane do procedury zapisującej to przy brutalnym przerwaniu programu (np kill -9) może się zdarzyć, że nie wszystkie dane zostaną zapisane. Zamykanie strumienia/ pliku/ etc do zapisu chyba zawsze robi w środku flush.

0
Azarien napisał(a):

Pliki się zamyka. Koniec filozofii.

Dokładne, CloseFile()

0

-123oho dziękuję za trafną odpowiedź. Tak myślałem że to będzie jakaś głupota... wychodzą efekty tygodnia pracy, na szczęście już piątek :).

Furious Programming zauważ że:
po pierwsze: koledze powyżej tyle danych wystarczyło by zlokalizować błąd
po drugie: nazewnictwo zmiennych to raczej sprawa piszącego, polskie zmienne działają równie dobrze jak Twoje angielskie
po trzecie: stosowana metoda to też sprawa piszącego, Ty zapewne robisz to lepiej ale użyta tutaj metoda też zdaje egzamin
po czwarte: "bądź łaskaw" trzymać się tematu a nie wnikać w zbędne szczegóły (tak jak nazewnictwo zmiennych w tym przypadku).

0

ale użyta tutaj metoda też zdaje egzamin

Who cares, skoro jest ona niepoprawna?
Jak będziesz łatał bugi, to będziesz robił miliardy if'ów zamiast znaleźć źródło błędu?


To rozwiązanie może być co najwyżej **póki co** dobre...
0

Nie wiem jak jest w pascalowcach, ale w C/ C++/ Javie nigdzie nie trzeba wywoływać flush() jeśli nie ma takiej potrzeby. Tzn flush() jest po to, żeby natychmiast zrzucić zawartość bufora na dysk i zwrócić sterowanie dopiero po potwierdzeniu zapisu. Jeśli zapisujemy do pliku, obojętnie czy buforowanego czy nie, nie dostaniemy żadnego przepełnienia bufora z d**y, bo biblioteka standardowa sama sobie będzie zrzucać dane na dysk z bufora (o ile taki bufor będzie). O ile nie będziemy robić flush() za każdym razem to zapis może się odbywać z pewnym, nieznanym dla nas, opóźnieniem, przez co np mimo iż nasz program wrzucił wszystkie dane do procedury zapisującej to przy brutalnym przerwaniu programu (np kill -9) może się zdarzyć, że nie wszystkie dane zostaną zapisane. Zamykanie strumienia/ pliku/ etc do zapisu chyba zawsze robi w środku flush.

w Pascalu jest podobnie, bufor jest sprawdzany przy procedurze plikowej (tj. zapis danych etc.). Zresztą, wydaje mi się że bufor raczej istnieje w samym win32, nie w RTL.
Flusha używam tylko gdy wymagam żeby dane trafiły do pliku zanim przejdę dalej (np. gdy loguję coś a następne procedury potencjalnie wywalą program). NIE ZDARZY SIĘ tak że dane 'znikają' w buforze, tylko Furious ma jakieś zwidy... No chyba że mi pokaże demko które zaprezentuje to. Czekam...

Var S: Pointer;
Begin
GetMem(S, 1024);
Readln(String(S));
Writeln(String(S));
Readln;
End.

Nadal wykonujesz na Stringu i musiałeś jawnie rzutować. Wiem że można kombinować, ale i tak wychodzi na string, który symulujesz innymi rzeczami...

Ad.3 - (taka forma ciekawostki :P) kiedyś można było robić Readln(PChar), lecz zostało to wyłączone dla uniknięcia buffer overflow

2005... To zanim zacząłem używać FPC, więc można powiedzieć że archeologia której nikt nie pamięta (zakładając że jestem tutaj osobą która najdłużej używa FPC). Ciekawostka nieaktualna :P .

Dokładne, CloseFile()

Albo Close... CloseFile to tylko wrapper, o czym wiele osób zapomina...

-123oho dziękuję za trafną odpowiedź. Tak myślałem że to będzie jakaś głupota... wychodzą efekty tygodnia pracy, na szczęście już piątek

Takie podstawowe błędy raczej powinieneś wykryć. Słabe wytłumaczenie... Zwłaszcza że obcięte pliki to typowy objaw, no ale powiedzmy że zdarzyło ci się to pierwszy raz.

po pierwsze: koledze powyżej tyle danych wystarczyło by zlokalizować błąd

To NIE ZNACZY że masz podawać mniej niż więcej. Podawaj więcej danych, bo nie lubię grać w 100 pytań do pytacza. To że tutaj miałeś farta nie znaczy że tak będzie zawsze.

Who cares, skoro jest ona niepoprawna?
Jak będziesz łatał bugi, to będziesz robił miliardy if'ów zamiast znaleźć źródło błędu?

Akurat try finally jest niepotrzebne jeżeli o to chodzi. Sam często tego nie stosuje bo po prostu procedura nie jest Exception aware i nie widzę sensu utrudniać rzeczy na siłę. I tak większość wyjątków kieruję bezpośrednio do aplication-wide handlera który wywala aplikację... Nie ma sensu przejmować się wyciekiem pamięci w takim wypadku...
Co do łatania bugów, to przecież gdy każda druga operacja czytania z plików crashuje, to lepiej zamykać i otwierać plik, rozwiązanie typowo z 4programmers. No ale ja nie wiem jak prawdziwi programiści łatają bugi ;) .

To rozwiązanie może być co najwyżej póki co dobre...

No tak, bo trzeba zakładać że exceptiony będą lecieć ze wsząd... Jeżeli aplikacja ma działać po wyjątku to tak, trzeba o tym pamiętać, ale jeżeli nie to nie widzę sensu się tym przejmować.

0
-123oho napisał(a)

Akurat try finally jest niepotrzebne jeżeli o to chodzi.

O ile się nie mylę, jeżeli plik jest zajęty (czyli np.otwarty) przez jakiś program, AssignFile się powiedzie, a wycrashuje się na Reset/Append/Rewrite i ewentualnie dalszych instrukcjach.

-123oho napisał(a)

Nie ma sensu przejmować się wyciekiem pamięci w takim wypadku...

imho, zawsze warto przejmować się wyciekami pamięci, zwłaszcza takimi oczywistymi i prostymi do naprawienia.
Poza tym, newbie to nie zaszkodzi.

0

O ile się nie mylę, jeżeli plik jest zajęty (czyli np.otwarty) przez jakiś program, AssignFile się powiedzie, a wycrashuje się na Reset/Append/Rewrite i ewentualnie dalszych instrukcjach.

{$I-} Nie lubię używać wyjątków tylko dla tego że plik nie istnieje. Potem mnie coś trafia jak używam debuggera (wyjątek tu, wyjątek tam). Dlatego często preferuję wyjątki tylko jako pad aplikacji, ew. jako jakiś poważny problem który nigdy nie powinien występować w normalnej pracy.

imho, zawsze warto przejmować się wyciekami pamięci, zwłaszcza takimi oczywistymi i prostymi do naprawienia.
Poza tym, newbie to nie zaszkodzi.

IMO nie ma sensu naprawiać tego co nie zmienia nic. Mogę zająć się masą poprawek które w teorii coś zmienią, a w praktyce nie wpłyną na nic. Natomiast wolę zająć się bugami które crashują aplikacje/dodawać nowe ficzury.
A że newbie nie zaszkodzi to akurat się zgodzę :P .

0
-123oho napisał(a)

{$I-} Nie lubię używać wyjątków tylko dla tego że plik nie istnieje.

O ile mi wiadomo, po użyciu tego przełącznika, nie zostaniesz także poinformowany o tym, że np.otwarcie pliku się nie powiodło, przez co kod radośnie będzie szedł dalej, aż dojdzie do Read czy Write i zacznie odczytywać/pisać bzdury.

-123oho napisał(a)

IMO nie ma sensu naprawiać tego co nie zmienia nic.

Memory leak rozmiaru 100 MB nie zmienia praktycznie nic, ale jednak wypadałoby go naprawić, więc czemu nie uczyć się dobrych nawyków i nie naprawiać także tych mniejszych?

0

O ile mi wiadomo, po użyciu tego przełącznika, nie zostaniesz także poinformowany o tym, że np.otwarcie pliku się nie powiodło, przez co kod radośnie będzie szedł dalej, aż dojdzie do Read czy Write i zacznie odczytywać/pisać bzdury.

IOResult. Mało wiesz o plikach, bo akurat bzdury to piszesz Ty ;)

Memory leak rozmiaru 100 MB nie zmienia praktycznie nic, ale jednak wypadałoby go naprawić, więc czemu nie uczyć się dobrych nawyków i nie naprawiać także tych mniejszych?

Pytasz "czemu nie naprawić małych memleaków?". Odpowiem: Bo twoja praca jest niewspółmiernie wysoka w porównaniu do zysków.
Pewna historia która się komuś przydarzyła: Jego klient używał Windows 95 na komputerze z Pentium II na 640x480. Pewnego razu aplikacja przestała mu działać z powodu pewnej niezgodności (np. okienko było za duże żeby zmieścić się na ekranie). Czy lepiej jest przeprojektować okienko (osoby z większym monitorem będą narzekać), czy lepiej dodawać nowe ficzury (dadzą nowych klientów a ten z win95 odejdzie)? Moim zdaniem lepiej dodawać nowe ficzury. Perfekcjonizm nie jest możliwy w IT.

0
-123oho napisał(a)

Mało wiesz o plikach, bo akurat bzdury to piszesz Ty

Cóż, zazwyczaj w ten sposób nie operuję na plikach; wolę TFileStream/TStringList i pochodne ;)

-123oho napisał(a)

Bo twoja praca jest niewspółmiernie wysoka w porównaniu do zysków.

Dodanie 5 linijek do kodu nie wymaga chyba wysokiej pracy? ;)
Podałeś przykład, gdzie rzeczywiście praca jest niewspółmiernie wysoka do zysków, lecz dodanie try..finally w zamian za naprawę tego banalnego błędu nie stanowi chyba niczego trudnego?
Oczywiste jest, że bicie się o kilobajty w dzisiejszych czasach nie ma sensu, ale skoro naprawa ogranicza się do poświęcenia tej biednej minuty, co szkodzi?

0

Cóż, zazwyczaj w ten sposób nie operuję na plikach; wolę TFileStream/TStringList i pochodne

Tja, pewnie są też wygodniejsze, ale cóż, przyzwyczajenia :D .

Podałeś przykład, gdzie rzeczywiście praca jest niewspółmiernie wysoka do zysków, lecz dodanie try..finally w zamian za naprawę tego banalnego błędu nie stanowi chyba niczego trudnego?

Masz jedno takie miejsce czy masę. I znalezienie wszystkich takich rzeczy w dużym kodzie będzie dużo trudniejsze...

Oczywiste jest, że bicie się o kilobajty w dzisiejszych czasach nie ma sensu, ale skoro naprawa ogranicza się do poświęcenia tej biednej minuty, co szkodzi?

Ty mówisz o małych kodach gdzie to zajmie mało czasu, ja mówię o podejściu praktycznym: kodzie napisanym źle, nieuporządkowanym i ograniczonym czasie na development. Zresztą i tak mówię tylko o zezwalanie na wycieki w ograniczonym wymiarze tzn. tylko jednokrotnym, a wiele aplikacji ma więcej wycieków...

Tak czy siak, nasza rozmowa schodzi już do tematów czysto estetycznych, a ponieważ i tak jedziemy nieźle z offtopicem to może czas skończyć :D . Trzeba shoutbox założyć...

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