Skocz do zawartości

Zarchiwizowany

Ten temat jest archiwizowany i nie można dodawać nowych odpowiedzi.

RIP

[Java] Dopisanie obiektu klasy do pliku - na końcu lub chronologicznie

Polecane posty

Cześć!

od razu wrzucę treść zadania:

"Plik binarny zawiera pewną liczbę rekordów ? danych z pomiarów temperatury dokonywanych o określonych godzinach w pewnej lokalizacji X. Każdy pomiar ma format wynikający ze struktury klasy Pomiar oraz Czas:

class Pomiar { Czas czas; double temperatura; }

class Czas { int rok, miesiąc, dzien, godzina, minuta; }

Napisać metody:

- dopisania pomiaru do pliku (na końcu pliku);

- dopisania pomiaru w sposób zapewniający porządek chronologiczny pomiarów w pliku,

- wypisywania na ekranie pomiarów z określonego miesiąca wskazanego roku,

- wypisywania na ekranie dni określonego miesiąca wskazanego roku, w których nastąpiło ocieplenie."

Generalnie potrafię zapisywać do pliku, serializacją się bawiłem, po prostu dopisanie na końcu też nie jest problemem, podobnie jak i z automatycznym sortowaniem dodawanych wartości. Problem mój polega na tym, że nie wiem, jak to połączyć. W zasadzie dopiero zaczynam bawić się z kolekcjami. Napisałem coś takiego:


package algorytmyz3;

import java.io.Serializable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.ObjectOutputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.util.TreeSet;

class Czas implements Serializable {
private int rok;
private int miesiac;
private int dzien;
private int godzina;
private int minuta;

public Czas(int rok, int miesiac, int dzien, int godzina, int minuta) {
this.rok = rok;
this.miesiac = miesiac;
this.dzien = dzien;
this.godzina = godzina;
this.minuta = minuta;
}

public int getRok() {
return rok;
}

public int getMiesiac() {
return miesiac;
}

public int getDzien() {
return dzien;
}

public int getGodzina() {
return godzina;
}

public int getMinuta() {
return minuta;
}
}

class Pomiar implements Serializable, Comparable<Pomiar> {
private Czas czas;
private double temperatura;

public Pomiar (Czas czas, double temperatura) {
this.czas = czas;
this.temperatura = temperatura;
}

public void Zapisz() {
try (ObjectOutputStream fout = new ObjectOutputStream(new FileOutputStream(
"BazaPomiarow.dat")))
{
fout.writeObject(this);
}
catch (IOException e) {
System.out.println("Błąd wejścia/wyjścia.");
}
}

public int compareTo(Pomiar pomiar) {
if (czas.getRok() > pomiar.czas.getRok()) return 1;
if (czas.getRok() < pomiar.czas.getRok()) return -1;
if (czas.getMiesiac() > pomiar.czas.getMiesiac()) return 1;
if (czas.getMiesiac() < pomiar.czas.getMiesiac()) return -1;
if (czas.getDzien() > pomiar.czas.getDzien()) return 1;
if (czas.getDzien() < pomiar.czas.getDzien()) return -1;
if (czas.getGodzina() > pomiar.czas.getGodzina()) return 1;
if (czas.getGodzina() < pomiar.czas.getGodzina()) return -1;
if (czas.getMinuta() > pomiar.czas.getMinuta()) return 1;
if (czas.getMinuta() < pomiar.czas.getMinuta()) return -1;
return 0;
}

public String toString() {
return "Temperatura: " + temperatura;
}
}


public class AlgorytmyZ3 {

public static void main(String[] args) {
TreeSet<Pomiar> ts = new TreeSet<Pomiar>();
Czas czas1 = new Czas(1988, 6, 15, 10, 18);
Czas czas2 = new Czas(1980, 1, 28, 15, 20);
Czas czas3 = new Czas(1980, 12, 9, 3, 4);
Pomiar p1 = new Pomiar(czas1, 10);
Pomiar p2 = new Pomiar(czas2, 20);
Pomiar p3 = new Pomiar(czas3, 30);
ts.add(p3);
ts.add(p2);
ts.add(p1);
System.out.println(ts);

try (ObjectOutputStream fout = new ObjectOutputStream(new FileOutputStream(
"BazaPomiarow.dat")))
{
fout.writeObject(ts);
}
catch (IOException e) {
System.out.println("Błąd wejścia/wyjścia.");
}
}

}

Zastosowałem TreeSet i interfejs Comparable na końcu serializując to drzewo do pliku. No tutaj każde dodanie pomiaru to dodanie z sortowaniem, a ja muszę jeszcze umożliwić wrzucenie na koniec. Prosiłbym po prostu o rady, co zastosować. Co przebudować. Może inny interfejs? Inną kolekcję? Nie chcę gotowego kodu, bo chcę sobie poczytać przykłady i się tego nauczyć smile_prosty.gif

Link do komentarza
Udostępnij na innych stronach

Nie możesz skorzystać z klasy Date zamiast samemu implementować Czas?

Proponowałbym zrobić menu użytkownika z opcjami takimi, jak wymieniłeś metody. Po wykonaniu wybranej operacji aplikacja powinna wrócić do menu.

Zamiast używać TreeSet, który sortuje dane, użyłbym jakiejś prostej listy (List? nie znam javy). To pozwoli Ci dodawać wpisy w dowolne miejsce, przy czym sam będziesz musiał zaimplementować wstawianie na odpowiednią pozycję, aby zrealizować chronologiczność. W zasadzie metody 1 i 2 się trochę gryzą, bo jeśli będziesz miał kolekcję uporządkowaną i wrzucisz do niej coś na koniec, to porządek diabli biorą i wstawianie kolejnego elementu jako uporządkowanego może nie zadziałać.

Link do komentarza
Udostępnij na innych stronach

No właśnie struktura danych ma wyglądać dokładnie tak, więc Date odpada. Ech... wstyd aż, że na obecną chwilę mnie to zadanko przerosło. Spróbuję pokombinować coś zgodnie z Twoją propozycją, acz z kolekcjami na Ty jeszcze nie jestem i o ile stosowanie metod wbudowanych, jak proste add() to bajka, to dodać coś od siebie do istniejącej kolekcji (metoda dodająca elementy w porządku), to dla mnie teraz kwestia wczytania się w jakieś szczegóły. Poczytam, popatrzę i spróbuję, bo z tym TreeSet to faktycznie niewiele da się zrobić, bo on porządkuje mi to zawsze.

W ogóle to się zastanawiam, czy dobrze rozumuję, bo skoro w treści zadania mam napisane coś o dopisywaniu coś na końcu pliku binarnego, a ja dopisuję do listy, a potem dopiero zapisuję listę do pliku binarnego to to samo, czy jednak nie o to chodzi?

Link do komentarza
Udostępnij na innych stronach

Zobacz http://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#add(int, E)

Metoda add posiada przeładowanie dodające na określoną pozycję. Bierzesz więc swoją listę i lecisz po kolei porównując elementy czy są większe od tego, który chcesz wstawić. Tak długo jak są mniejsze lecisz dalej. Jak się trafi większy, to dostajesz indeks, na jaką pozycję wstawić nowy element. Możesz też dojść do końca listy, a wtedy po prostu ładujesz na koniec.

Z tym zapisem to w zasadzie zależy od prowadzącego, czy chciałby mieć chamskie czytanie pliku do stringa, jakieś parsowanie tego tekstu do danych, a zapis tak samo, tylko w drugą stronę, czy może kulturalniej, tak jak to zrobiłeś, z serializacją/deserializacją.

A i jeszcze jedna rzecz: nie podoba mi się Twoja metoda compareTo. Zrobiłbym w klasie Czas metodę, która zwracać będzie minutową wartość bieżącej daty, powiedzmy od roku 0. Zaimplementowałbym interfejs Comparable w klasie czas i porównywałbym czasy właśnie na podstawie minut. Stąd już prosta droga do porównania w klasie Pomiar, gdzie po prostu robiłbyś return pomiar.czas.compareTo(czas).

Link do komentarza
Udostępnij na innych stronach

Właściwie masz napisać osobne metody, które wykonają wskazane funkcjonalności, więc chyba nie ma sensu na siłę ich agregować. W dwóch pierwszych zadaniach jest mowa o operacjach na pliku, więc metody powinny same go w całości obsłużyć (od otwarcia do zamknięcia).

Pierwsza metoda rzeczywiście mogłaby dopisywać rekord na końcu pliku bez zabawy w odczytywanie i przepisywanie całości.

W drugiej metodzie możesz wczytać zawartość pliku do listy (dokładniej ArrayListy, powinna sobie lepiej poradzić z sortowaniem), dodać rekord i skorzystać z metody Collections.sort (złożoność obliczeniowa sortowania nie powinna być gorsza, niż dodawanie wszystkich rekordów do TreeSet'a, a pamięciowa lepsza), no i na koniec zapisać wszystko do pliku.

Gdybyś chciał trzymać dane w pamięci między wywołaniami metod, to możesz do tego celu użyć TreeSet'a (w odróżnieniu od wcześniejszego rozwiązania tu wszystkie obiekty dodajesz tylko raz, więc użycie go jest uzasadnione) - w pierwszej metodzie zapisujesz rekord do pliku i dodajesz go do set'a, w drugiej dodajesz rekord do set'a i wpisujesz go (set'a) w całości do pliku.

PS bardziej elegancko by było, gdyby ciało metody Pomiar.compareTo (ten wielki zestaw if'ów) znajdowało się w klasie Czas (która też by musiała implementować Comparable).

EDIT:

@wies.niak

rozwiązanie z Twojego pierwszego akapitu już zostało napisane i będzie miało dużo gorszą wydajność od używania TreeSet'a (przy okazji, lepiej by się chyba sprawdził LinkedList, w nim taka metoda add powinna mieć lepszą złożoność), a sensu trzymania listy w pamięci nie ma (co mam nadzieję wyżej wyjaśniłem).

Link do komentarza
Udostępnij na innych stronach

hmm... to, co piszesz, webtom, zdaje mi się ciekawe i dość bliskie mojemu wyobrażeniu tego, co ten gość chce (studiuję, więc oni oczywiście wolą jak najmniej bibliotek, a jak najwięcej własnej inwencji i chamskiej roboty, jak to Wiesiu określił). Przepisałem więc trochę swój kod na bardziej paździerzowy (elegancją zajmę się później, jeśli to zagra - robotę mam wykonać do soboty, a bawię się z tym strasznie :) ). Wygląda to teraz tak:


package algorytmyz3v3;
import java.io.Serializable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.util.List;
import java.util.ArrayList;
class Czas implements Serializable {
private int rok;
private int miesiac;
private int dzien;
private int godzina;
private int minuta;

public Czas(int rok, int miesiac, int dzien, int godzina, int minuta) {
this.rok = rok;
this.miesiac = miesiac;
this.dzien = dzien;
this.godzina = godzina;
this.minuta = minuta;
}

public int getRok() {
return rok;
}

public int getMiesiac() {
return miesiac;
}

public int getDzien() {
return dzien;
}

public int getGodzina() {
return godzina;
}

public int getMinuta() {
return minuta;
}
}
class Pomiar implements Serializable /*,Comparable<Pomiar>*/ {
private Czas czas;
private double temperatura;

public Pomiar (Czas czas, double temperatura) {
this.czas = czas;
this.temperatura = temperatura;
}

public void Zapisz(ObjectOutputStream fout) throws IOException{
fout.writeObject(this);
}

public void ZapisChrono(ObjectOutputStream fout) throws IOException {

}


public String toString() {
return "Temperatura: " + temperatura;
}
}


public class AlgorytmyZ3V3 {
public static void main(String[] args) {
try (ObjectOutputStream fout = new ObjectOutputStream(new FileOutputStream(
"BazaPomiarow.dat")))
{

Czas czas1 = new Czas(1988, 6, 15, 10, 18);
Czas czas2 = new Czas(1980, 1, 28, 15, 20);
Czas czas3 = new Czas(1980, 12, 9, 3, 4);
Pomiar p1 = new Pomiar(czas1, 30);
Pomiar p2 = new Pomiar(czas2, 20);
Pomiar p3 = new Pomiar(czas3, 30);
p1.Zapisz(fout);
p2.Zapisz(fout);
p3.Zapisz(fout);
}

catch (IOException e) {
System.out.println("Błąd wejścia/wyjścia.");
}
try {
ObjectInputStream fin = new ObjectInputStream(new FileInputStream("BazaPomiarow.dat"));
ArrayList<Pomiar> lista = new ArrayList();
}
catch(Exception e) {
System.out.println("Wyjątek w czasie deserializacji: " + e);
}
}

}

A więc zapisuję sobie 3 pomiary do pliku, żeby mieć tam już jakieś rekordy. Oczywiście teraz jeśli będę chciał zapisać coś na końcu pliku, po prostu wywołam jeszcze raz metodę Zapisz(ObjectOutputStream fout). Jednak tutaj stanąłem, bo...jak mam to sczytać do ArrayList? Jakaś pętla, żeby mi wczytało ten cały plik do końca rekord za rekordem? Nie mam pomysłu, jak to ruszyć. Wybaczcie noobowi :) Oczywiście wystarczą ogólniki - postaram się wszystko ładnie poczytać dalej.

Link do komentarza
Udostępnij na innych stronach

U mnie z kolei jeden pan profesor mawiał, że "dobry programista to leniwy programista" smile_prosty.gif

Co prawda zbytnio się nie znam na serializacji, ale zakładam, że jeżeli ma być możliwe dopisywanie kolejnych obiektów do pliku, to serializacja całej listy nie wchodzi w grę, więc odczyt i zapis powinien się odbywać w pętli.

Metoda ZapisChrono jest albo źle usytuowana, albo źle zdefiniowana, bo musisz odczytać rekordy z pliku, dodać aktualny rekord, posortować i zapisać posortowane rekordy, a dane dostępne w tej metodzie pozwalają jedynie na ostatni krok.

Właściwie całą logikę umieściłbym chyba w klasie AlgorytmyZ3V3, a klasa Pomiar zawierałaby tylko metody do operacji na pliku, no i oczywiście compareTo (którego przywróć, nawet w poprzedniej postaci, bo jest potrzebny do sortowania/TreeSet'a). Tak przy okazji, to trzymając się konwencji javowych nazwy metod rozpoczynają się z małej litery.

Klasa Pomiar zawierałaby takie metody:

static ArrayList<Pomiar> readFromFile(ObjectInputStream) - do odczytu danych z pliku (z pętlą)

static void writeToFile(ObjectOutputStream, Collection<Pomiar>) - do zapisu danych do pliku (z pętlą)

static Pomiar read(ObjectInputStream) - do odczytu pojedynczego pomiaru

void write(ObjectOutputStream) - do zapisu pojedynczego pomiaru

Jeżeli założymy pierwszą opcję opisaną w moim poprzednim poście, to klasa AlgorytmyZ3V3 zawierałaby po jednej statycznej metodzie na jedno zadanie (jeżeli skupimy się póki co na dwóch pierwszych, to jako parametry powinny przyjmować nazwę pliku i nowy pomiar), przy drugiej opcji zmieni się chyba tylko tyle, że metody nie będą statyczne (i ew. mogą nie otrzymywać nazwy pliku, gdyby nie miały od razu zapisywać zmian).

Link do komentarza
Udostępnij na innych stronach

No właśnie mój podstawowy problem z tym, żeby pójść dalej polega na tym, że w Javie orientuję się trochę gorzej niż w C++ np. i tu nie za bardzo wiem...jak zatrzymać tę pętlę wczytującą tudzież zapisującą? jaki warunek jej wstawić? Potem to już może z górki pójdzie.

Link do komentarza
Udostępnij na innych stronach

Jeżeli chcesz przeiterować po wszystkich elementach kolekcji (np. listy), to pętla wygląda w stylu:


for (Pomiar pomiar : kolekcja){
pomiar.cośtam(); // np. zapis pomiaru
}

Natomiast jeżeli chodzi o odczytywanie, to w tym strumieniu brakuje mi jakiejś metody peek, ale available wygląda nienajgorzej, więc pewnie sprawdzi się coś w stylu:


while (fin.available() != 0){
Pomiar pomiar = Pomiar.read(fin);
// przetworzenie pomiaru (dodanie do kolekcji wynikowej)
}

Chociaż pewnie metoda ObjectInputStream.readObject() rzuciłaby jakimś wyjątkiem na końcu pliku (mógłbyś ten wyjątek złapać w Pomiar.read i zwrócić null'a i odczytywać dane, dopóki byś na tego null'a nie natrafił).

A do zatrzymania pętli może też posłużyć instrukcja break, ale zakładam, że ją z C++ znasz.

Link do komentarza
Udostępnij na innych stronach

No właśnie niestety available się chyba nie sprawdza confused_prosty.gif Napisałem coś takiego:


package algorytmyz3v3;
import java.io.Serializable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Collections;
class Czas implements Serializable {
private int rok;
private int miesiac;
private int dzien;
private int godzina;
private int minuta;

public Czas(int rok, int miesiac, int dzien, int godzina, int minuta) {
this.rok = rok;
this.miesiac = miesiac;
this.dzien = dzien;
this.godzina = godzina;
this.minuta = minuta;
}

public int getRok() {
return rok;
}

public int getMiesiac() {
return miesiac;
}

public int getDzien() {
return dzien;
}

public int getGodzina() {
return godzina;
}

public int getMinuta() {
return minuta;
}
}
class Pomiar implements Serializable, Comparable<Pomiar> {
private Czas czas;
private double temperatura;

public Pomiar (Czas czas, double temperatura) {
this.czas = czas;
this.temperatura = temperatura;
}

public void zapisz(ObjectOutputStream fout) throws IOException{
fout.writeObject(this);
}

public void wyswietl (int rok, int miesiac, ObjectInputStream fin) {

}

public void ocieplenie (int rok, int miesiac, ObjectInputStream fin) {

}

public int compareTo(Pomiar pomiar) {
if (czas.getRok() > pomiar.czas.getRok()) return 1;
if (czas.getRok() < pomiar.czas.getRok()) return -1;
if (czas.getMiesiac() > pomiar.czas.getMiesiac()) return 1;
if (czas.getMiesiac() < pomiar.czas.getMiesiac()) return -1;
if (czas.getDzien() > pomiar.czas.getDzien()) return 1;
if (czas.getDzien() < pomiar.czas.getDzien()) return -1;
if (czas.getGodzina() > pomiar.czas.getGodzina()) return 1;
if (czas.getGodzina() < pomiar.czas.getGodzina()) return -1;
if (czas.getMinuta() > pomiar.czas.getMinuta()) return 1;
if (czas.getMinuta() < pomiar.czas.getMinuta()) return -1;
return 0;
}


public String toString() {
return "Temperatura: " + temperatura;
}
}


public class AlgorytmyZ3V3 {
public static void main(String[] args) {

ArrayList<Pomiar> lista = new ArrayList();

try (ObjectOutputStream fout = new ObjectOutputStream(new FileOutputStream(
"BazaPomiarow.dat")))
{

Czas czas1 = new Czas(1988, 6, 15, 10, 18);
Czas czas2 = new Czas(1980, 1, 28, 15, 20);
Czas czas3 = new Czas(1980, 12, 9, 3, 4);
Pomiar p1 = new Pomiar(czas1, 30);
Pomiar p2 = new Pomiar(czas2, 20);
Pomiar p3 = new Pomiar(czas3, 30);
p1.zapisz(fout);
p2.zapisz(fout);
p3.zapisz(fout);
}

catch (IOException e) {
System.out.println("Błąd wejścia/wyjścia.");
}

try {
ObjectInputStream fin = new ObjectInputStream(new FileInputStream("BazaPomiarow.dat"));
while (fin.available() != 0)
{
lista.add((Pomiar)fin.readObject());
Collections.sort(lista);
wyswietlListe(lista);
}

}
catch(Exception e) {
System.out.println("Wyjątek w czasie deserializacji: " + e);
}
}

public static void wyswietlListe(ArrayList<Pomiar> list) {
for (Pomiar pomiar: list)
{
System.out.println(pomiar);
}
}

}

I wynikiem działania programu jest dokładnie nic. Gdy oglądam to w debuggerze krok po kroku ten while w ogóle się nie wykonuje i od razu przeskakuje cały blok, a więc ani nie pobierają się kolejne pomiary do tablicy, ani toString() tych pomiarów nie działa, więc nic się nie wyświetla. Te metody wyswietl i oswietlenie na razie robią tylko za słupy i nie zastanawiałem się jeszcze nad szczegółami, więc nie zwracajcie uwagi :)

Link do komentarza
Udostępnij na innych stronach

Działa coś takiego:


FileInputStream s = new FileInputStream("BazaPomiarow.dat");
ObjectInputStream fin = new ObjectInputStream(s);
while (s.available() > 0) {
(...)

I da się też zrobić tak:


while (true) {
try {
lista.add((Pomiar) fin.readObject());
} catch (Exception e) {
break;
}
}

Sortowanie i wypisywanie listy można przesunąć nieco niżej.

I jeszcze ważna rzecz - strumienie wypada zamknąć, najlepiej bezpośrednio po użyciu.

EDIT:

Druga metoda, mimo że ponoć średnio elegancka (while (true)), jest poprawniejsza - available służy do czegoś innego.

Link do komentarza
Udostępnij na innych stronach

Faktycznie:) Więc cały problem polegał na tym, że wpisywałem w jednej linijce ObjectInputStream i wiązałem go z FileInputStream. W takim przypadku available() używałem z obiektem OOS i nie działało. Z FOS już działa ładnie i metoda sortująca mi nawet kilkanaście pomiarów ładnie wysortowała i zwróciła w porządku :) Teraz może być już tylko lepiej. Co prawda jeszcze nie rozumiem, dlaczego z OOS mi to available nie chciało działać, ale cóż :)

Link do komentarza
Udostępnij na innych stronach

Zrobiłem w poprzednim poście drobnego edita.

Te obiekty to raczej OIS i FIS.

Tak czysto teoretycznie, to nawet zwrócenie przez FIS.available() wartości >0 nie oznacza, że OIS będzie w stanie zwrócić obiekt.

A OIS.available() może zwracać 0, bo sam strumień nie operuje na bajtach (których ilość zwraca funkcja).

A moja uwaga o zamykaniu strumieni jest oczywiście zbędna.

Link do komentarza
Udostępnij na innych stronach

Z tym warunkiem true jest taki problem, że jak już wiadomo ja nie jestem mistrzem programowania i przy wyborze tego warunku niezależnie co napiszę po bloku while() {} mam tam błąd nieosiągalnego kodu. Nie znam do końca tej mechaniki, więc nie wiem, z czego on wynika. Z available() nie ma problemu.

Mam za to pytanie:

Jak zrobić, żeby przy kolejnych zapisach nie czyściło mi zawartości pliku?

Mam teraz taki kod:


package algorytmyz3v3;
import java.io.Serializable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Collections;
class Czas implements Serializable {
private int rok;
private int miesiac;
private int dzien;
private int godzina;
private int minuta;

public Czas(int rok, int miesiac, int dzien, int godzina, int minuta) {
this.rok = rok;
this.miesiac = miesiac;
this.dzien = dzien;
this.godzina = godzina;
this.minuta = minuta;
}

public int getRok() {
return rok;
}

public int getMiesiac() {
return miesiac;
}

public int getDzien() {
return dzien;
}

public int getGodzina() {
return godzina;
}

public int getMinuta() {
return minuta;
}
}
class Pomiar implements Serializable, Comparable<Pomiar> {
private Czas czas;
private double temperatura;

public Pomiar (Czas czas, double temperatura) {
this.czas = czas;
this.temperatura = temperatura;
}

public void zapisz(ObjectOutputStream fout) throws IOException{
fout.writeObject(this);
}

public void zapisChrono(ObjectOutputStream fout, ObjectInputStream fin, FileInputStream s) throws IOException, Exception {
ArrayList<Pomiar> lista = new ArrayList();
while (s.available() > 0) {
lista.add((Pomiar) fin.readObject());}
lista.add(this);
Collections.sort(lista);
for (Pomiar pomiar : lista)
fout.writeObject(pomiar);
}

public void wyswietl (ObjectInputStream fin, FileInputStream s) throws IOException, Exception {
while (s.available() > 0)
{
System.out.println((Pomiar)fin.readObject());
}

System.out.println();
System.out.println();
}

public void ocieplenie (int rok, int miesiac, ObjectInputStream fin) {

}

public int compareTo(Pomiar pomiar) {
if (czas.getRok() > pomiar.czas.getRok()) return 1;
if (czas.getRok() < pomiar.czas.getRok()) return -1;
if (czas.getMiesiac() > pomiar.czas.getMiesiac()) return 1;
if (czas.getMiesiac() < pomiar.czas.getMiesiac()) return -1;
if (czas.getDzien() > pomiar.czas.getDzien()) return 1;
if (czas.getDzien() < pomiar.czas.getDzien()) return -1;
if (czas.getGodzina() > pomiar.czas.getGodzina()) return 1;
if (czas.getGodzina() < pomiar.czas.getGodzina()) return -1;
if (czas.getMinuta() > pomiar.czas.getMinuta()) return 1;
if (czas.getMinuta() < pomiar.czas.getMinuta()) return -1;
return 0;
}


public String toString() {
return "Temperatura w dniu: " + czas.getRok() + "." + czas.getMiesiac() +
"." + czas.getDzien() + " godzina " + czas.getGodzina() + ":" + czas.getMinuta() + " - " + temperatura + " stopni Celsjusza";
}
}

public class AlgorytmyZ3V3 {
public static void main(String[] args) {



try
{
ObjectOutputStream fout = new ObjectOutputStream(new FileOutputStream(
"BazaPomiarow.dat"));
FileInputStream s = new FileInputStream("BazaPomiarow.dat");
ObjectInputStream fin = new ObjectInputStream(s);
ArrayList<Pomiar> lista = new ArrayList();

Czas czas1 = new Czas(1988, 6, 15, 10, 18);
Czas czas2 = new Czas(1980, 1, 28, 15, 20);
Czas czas3 = new Czas(1980, 12, 9, 3, 20);
Czas czas4 = new Czas(1984, 12, 9, 18, 25);
Czas czas5 = new Czas(1990, 12, 9, 12, 55);
Czas czas6 = new Czas(1984, 9, 9, 4, 40);
Czas czas7 = new Czas(1970, 12, 9, 4, 25);

Pomiar p1 = new Pomiar(czas1, 30);
Pomiar p2 = new Pomiar(czas2, 20);
Pomiar p3 = new Pomiar(czas3, 30);
Pomiar p4 = new Pomiar(czas4, 25);
Pomiar p5 = new Pomiar(czas5, 22);
Pomiar p6 = new Pomiar(czas6, 12);
Pomiar p7 = new Pomiar(czas7, -6);

p1.zapisz(fout);
p2.zapisz(fout);
p3.zapisz(fout);
p4.zapisz(fout);
p5.zapisz(fout);
p6.zapisz(fout);



p6.wyswietl(fin, s);

p7.zapisChrono(fout, fin, s);
p7.wyswietl(fin, s);
}

catch (IOException e) {
System.out.println("Błąd wejścia/wyjścia.");
}

catch(Exception e) {
System.out.println("Wyjątek w czasie deserializacji: " + e);
}

}
}

Ta metoda wyświetl() jest mocno kulawa, bo niby wywoływana jest przez obiekt, a odnosi się do całej zawartości pliku, ale nieistotne to teraz. Chodzi o to, że po zapisaniu sześciu pomiarów i wyświetleniu mam wszystko dobrze w kolejności dopisywania. Po wywołaniu zapisChrono(argumenty) i ponownym wywołaniu wyswietlenia mam tylko ten ostatni pomiar. Myslałem, że się zabezpieczyłem na taki wypadek wczytując w tej metodzie wszystko do listy, potem sortując tę listę i zapisując wszystkie jej elementy do pliku. No ale nie - jest tylko to jedno. W czym tkwi ten malutki szczególik?

Link do komentarza
Udostępnij na innych stronach

Chyba tego while'a musiałeś jakoś wybiórczo przepisywać, zapewne zabrakło jakiegokolwiek break'a.

Żeby plik nie był czyszczony, musisz użyć innego konstruktora FOS'a, słowo klucz - append.

I tym razem mogę już napisać, że nie zamykasz Stream'ów, co wypadałoby robić.

Program za drugim razem nie odczytuje całego pliku, bo nie otwierasz InputStreamów od nowa (a więc czytasz od miejsca, w którym poprzednim razem skończyłeś). Rozwiązanie pozbawione tego problemu proponowałem już jakiś czas temu:

(...) klasa AlgorytmyZ3V3 zawierałaby po jednej statycznej metodzie na jedno zadanie (jeżeli skupimy się póki co na dwóch pierwszych, to jako parametry powinny przyjmować nazwę pliku i nowy pomiar)

PS Jak już wspominałem jakiś czas temu, logika w klasie Pomiar jest średnim pomysłem - obecnie niemal wszystkie obiekty, z którymi pracujesz, musisz przekazać jako parametry.

Link do komentarza
Udostępnij na innych stronach

uff :) No dobra...w kwestii pytań tutaj się poddaje, bo to by był żenujący festiwal pytania o co drugą linijkę kodu, bo nie ogarniam zupełnie. Poczytam sobie trochę jakieś stackoverflow, API itd. i może coś wykombinuję. A jak nie, to się do soboty po prostu nie wyrobię. Tak, czy inaczej, dziękuję za pomoc :)

Link do komentarza
Udostępnij na innych stronach

Troche się jeszcze pobawiłem. OOS wrzuciłem do try-with-resources, a OIS starałem się zamykać na bieżąco. I generalnie nie wliczając tego, że program może nie jest specjalnie elegancki, osiągnąłem 2 z założeń, czyli skutecznie dopisałem pomiar na koniec, a potem kolejny już tak, by była chronologia. Całość wygląda tak na razie:


package algorytmyz3v3;
import java.io.Serializable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.EOFException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Collections;
class Time implements Serializable {
private int year;
private int month;
private int day;
private int hour;
private int minute;

public Time(int rok, int miesiac, int dzien, int godzina, int minuta) {
this.year = rok;
this.month = miesiac;
this.day = dzien;
this.hour = godzina;
this.minute = minuta;
}

public int getYear() {
return year;
}

public int getMonth() {
return month;
}

public int getDay() {
return day;
}

public int getHour() {
return hour;
}

public int getMinute() {
return minute;
}
}
class Pomiar implements Serializable, Comparable<Pomiar> {
private Time time;
private double temperature;

public Pomiar (Time czas, double temperatura) {
this.time = czas;
this.temperature = temperatura;
}

public void save(ObjectOutputStream fout) throws IOException{
fout.writeObject(this);
}

public void saveChrono(ObjectOutputStream fout, ObjectInputStream fin) throws IOException, Exception {
ArrayList<Pomiar> lista = new ArrayList();
while (true) {
try{

lista.add((Pomiar) fin.readObject());}
catch (EOFException e) {
break;} }
lista.add(this);
Collections.sort(lista);
for (Pomiar pomiar : lista)
fout.writeObject(pomiar);

}

public void output (ObjectInputStream fin) throws IOException, Exception {
while (true)
try {
{
System.out.println((Pomiar)fin.readObject());
}
}
catch (Exception e) {
break;}

System.out.println();
System.out.println();
}

public void heat (int rok, int miesiac, ObjectInputStream fin) {

}

public int compareTo(Pomiar pomiar) {
if (time.getYear() > pomiar.time.getYear()) return 1;
if (time.getYear() < pomiar.time.getYear()) return -1;
if (time.getMonth() > pomiar.time.getMonth()) return 1;
if (time.getMonth() < pomiar.time.getMonth()) return -1;
if (time.getDay() > pomiar.time.getDay()) return 1;
if (time.getDay() < pomiar.time.getDay()) return -1;
if (time.getHour() > pomiar.time.getHour()) return 1;
if (time.getHour() < pomiar.time.getHour()) return -1;
if (time.getMinute() > pomiar.time.getMinute()) return 1;
if (time.getMinute() < pomiar.time.getMinute()) return -1;
return 0;
}


public String toString() {
return "Temperatura w dniu: " + time.getYear() + "." + time.getMonth() +
"." + time.getDay() + " godzina " + time.getHour() + ":" + time.getMinute() + " - " + temperature + " stopni Celsjusza";
}
}


public class AlgorytmyZ3V3 {
public static void main(String[] args) {

try (ObjectOutputStream fout = new ObjectOutputStream(new FileOutputStream(
"BazaPomiarow.dat")))
{

Time time1 = new Time(1988, 6, 15, 10, 18);
Time time2 = new Time(1980, 1, 28, 15, 20);
Time time3 = new Time(1980, 12, 9, 3, 20);
Time time4 = new Time(1984, 12, 9, 18, 25);
Time time5 = new Time(1990, 12, 9, 12, 55);
Time time6 = new Time(1984, 9, 9, 4, 40);
Time time7 = new Time(1970, 12, 9, 4, 25);
Time time8 = new Time(1966, 11, 10, 7, 44);

Pomiar p1 = new Pomiar(time1, 30);
Pomiar p2 = new Pomiar(time2, 20);
Pomiar p3 = new Pomiar(time3, 30);
Pomiar p4 = new Pomiar(time4, 25);
Pomiar p5 = new Pomiar(time5, 22);
Pomiar p6 = new Pomiar(time6, 12);
Pomiar p7 = new Pomiar(time7, -6);
Pomiar p8 = new Pomiar(time8, 3);

p1.save(fout);
p2.save(fout);
p3.save(fout);
p4.save(fout);
p5.save(fout);
p6.save(fout);


ObjectInputStream fin = new ObjectInputStream(new FileInputStream("BazaPomiarow.dat"));

p6.output(fin);
fin.close();


p7.save(fout);

fin = new ObjectInputStream(new FileInputStream("BazaPomiarow.dat"));
p7.output(fin);
fin.close();

fin = new ObjectInputStream(new FileInputStream("BazaPomiarow.dat"));
p8.saveChrono(fout, fin);
p8.output(fin);
fin.close();


}

catch (IOException e) {
System.out.println("Błąd wejścia/wyjścia.");
}

catch(Exception e) {
System.out.println("Wyjątek w czasie deserializacji: " + e);
}

}

}

Ale mam pytanie natury technicznej, bo to, że się udało, to w mojej głowie bardziej przypadek, niż zrozumienie :P Chodzi mi o to...dlaczego po zastosowaniu metody save dla p7 dopisuje mi go do tego, co już jest w plikach, a przy savechrono wywołanego dla p8 wszystko zapisuje się jakby od nowa? Tzn. gdyby savechrono działało tak jak save, powinienem mieć z 15 pomiarów w pliku, bo było 7, które odczytałem, dodałem do ArrayList jeszcze ósmy i zapisałem znowu do pliku, gdzie 7 już było. A tu wszystko elegancko chodzi. Skąd ta różnica w działaniu?

Link do komentarza
Udostępnij na innych stronach

Stwórz jeszcze raz fin'a (właściwie to jest oos) przed linijką


p8.output(fin);

to się dowiesz biggrin_prosty.gif

I czemu wolisz babranie się w OIS'y i OOS'y w całym kodzie zamiast statycznych metod, które same obsłużą plik znając jego nazwę (funkcja obsługująca OOS znałaby jeszcze nowy Pomiar, a OIS zwracałaby kolekcję)?

Link do komentarza
Udostępnij na innych stronach

Może dlatego, że jestem jeszcze za słaby z Javy i nie wiem, jak się do tego zabrać? smile_prosty.gif nie oszukujmy się, ale krótko w tym siedzę i obecnie mechanika działania tych strumieni to dla mnie czarna magia. Teraz np. spróbowałem sobie wywalić z całego kodu fin.close() zamiast tego wstawiając OIS do try-with-resources, gdzie już był OOS i wyszło mi 6 pierwszym pomiarów na wyjściu, 2 linijki niżej samo p7 i 2 linijki niżej samo p8. Dlaczego? - nie wiem smile_prosty.gif Potem do OOS dodałem w konstruktorze true, bo ponoć to pozwala dopisywać na końcu i wyskoczył mi złapany błąd wejścia/wyjścia tongue_prosty.gif To jest taka magiczna skrzynka smile_prosty.gif Co wpiszę coś, to co innego wychodzi, ale nie wiem, dlaczego biggrin_prosty.gif

ech...do tego jestem w tak durnowatej ślepej uliczce...owszem, za każdym razem, gdy sobie coś wczytuję z pliku, to potem robię fin.close() i następne wczytywanie poprzedzone oczywiście znowu


fin = new ObjectInputStream(new FileInputStream("BazaPomiarow.dat"));

zdaje się działać ok, ale już metody zapisujące nie działają, bo jeśli nie zamykam fouta, to dopisuje mi do końca i np. przy saveChrono mam, tak jak pisałem 15 pomiarów tam zamiast ośmiu (wtedy nie zamknąłem fina, więc sczytało mi końcówkę i myląco wyglądało to dobrze - doszedłem już do tego). A jeżeli tego fouta zamknę i przy następnej metodzie save() lub saveChrono go otworzę przez


fout = new ObjectOutputStream(new FileOutputStream(
"BazaPomiarow.dat"));

to jeśli się dobrze domyślam,tracę plik poprzedni i faktycznie te metody w swoim ciele niczego do listy nie wczytują prócz dodania nowego elementu i na końcu metody mam zawsze w pliku zapisany tylko jeden pomiar. Więc tak źle i tak niedobrze confused_prosty.gif

a te metody zapisujące wyglądają u mnie obecnie tak:


public void save(ObjectOutputStream fout, ObjectInputStream fin) throws IOException, Exception{
ArrayList<Pomiar> lista = new ArrayList();
while (true) {
try{
lista.add((Pomiar) fin.readObject());}
catch (EOFException e) {
break;} }
lista.add(this);
for (Pomiar pomiar : lista)
fout.writeObject(pomiar);
}

public void saveChrono(ObjectOutputStream fout, ObjectInputStream fin) throws IOException, Exception {
ArrayList<Pomiar> lista = new ArrayList();
while (true) {
try{

lista.add((Pomiar) fin.readObject());}
catch (EOFException e) {
break;} }
lista.add(this);
Collections.sort(lista);
for (Pomiar pomiar : lista)
fout.writeObject(pomiar);

}

I nie wiem. Chciałbym mieć metodę. która mi wczyta do listy z pliku pomiary, wyczyści ten plik i zapisze tą listę do tego pliku. Wtedy wystarczy mi jeden OOS otwarty w try-with-resources i wszystko powinno śmigać. Pewnie to możliwe, ale ja nie widzę, niby jak confused_prosty.gif No i nawet jakbym chciał zrobić metody statyczne, to miałbym analogiczny problem i albo bym dublował rzeczy w pliku, albo co rusz tworzyłbym OOS dla pustego pliku. ech...

EDIT:

Ja cię...ale jestem z siebie dumny smile_prosty.gif troszkę podumałem na temat tego, jak to cuś działa i mam teraz taaaki program smile_prosty.gif :


package algorytmyz3v3;
import java.io.Serializable;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.EOFException;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Collections;
class Time implements Serializable {
private int year;
private int month;
private int day;
private int hour;
private int minute;

public Time(int rok, int miesiac, int dzien, int godzina, int minuta) {
this.year = rok;
this.month = miesiac;
this.day = dzien;
this.hour = godzina;
this.minute = minuta;
}

public int getYear() {
return year;
}

public int getMonth() {
return month;
}

public int getDay() {
return day;
}

public int getHour() {
return hour;
}

public int getMinute() {
return minute;
}
}
class Pomiar implements Serializable, Comparable<Pomiar> {
private Time time;
private double temperature;

public Pomiar (Time czas, double temperatura) {
this.time = czas;
this.temperature = temperatura;
}

public void save() throws IOException, Exception{
ObjectInputStream fin = new ObjectInputStream(new FileInputStream("BazaPomiarow.dat"));
ArrayList<Pomiar> lista = new ArrayList();
while (true) {
try{
lista.add((Pomiar) fin.readObject());}
catch (EOFException e) {
break;} }
fin.close();
lista.add(this);

ObjectOutputStream fout = new ObjectOutputStream(new FileOutputStream(
"BazaPomiarow.dat"));
for (Pomiar pomiar : lista)
fout.writeObject(pomiar);
fout.close();
}

public void saveChrono() throws IOException, Exception {
ObjectInputStream fin = new ObjectInputStream(new FileInputStream("BazaPomiarow.dat"));
ArrayList<Pomiar> lista = new ArrayList();
while (true) {
try{

lista.add((Pomiar) fin.readObject());}
catch (EOFException e) {
break;} }
fin.close();
lista.add(this);
Collections.sort(lista);

ObjectOutputStream fout = new ObjectOutputStream(new FileOutputStream(
"BazaPomiarow.dat"));
for (Pomiar pomiar : lista)
fout.writeObject(pomiar);
fout.close();

}

public void output () throws IOException, Exception {
ObjectInputStream fin = new ObjectInputStream(new FileInputStream("BazaPomiarow.dat"));
ArrayList<Pomiar> lista = new ArrayList();
while (true)
try {
{
lista.add((Pomiar) fin.readObject());
}
}
catch (Exception e) {
break;}

fin.close();

for (Pomiar pomiar : lista) {
System.out.println(pomiar);
}

System.out.println();
System.out.println();
}

public void outputParticularMonth (int year, int month) throws IOException, Exception {

ArrayList<Pomiar> lista = new ArrayList();
ObjectInputStream fin = new ObjectInputStream(new FileInputStream("BazaPomiarow.dat"));
while (true) {
try{
lista.add((Pomiar) fin.readObject());}
catch (EOFException e) {
break;} }
fin.close();

for (Pomiar pomiar : lista) {
if (pomiar.time.getYear()==year && pomiar.time.getMonth()==month) {
System.out.println(pomiar);
}
}

System.out.println();
System.out.println();
}

public void heat (int rok, int miesiac, ObjectInputStream fin) {

}

public int compareTo(Pomiar pomiar) {
if (time.getYear() > pomiar.time.getYear()) return 1;
if (time.getYear() < pomiar.time.getYear()) return -1;
if (time.getMonth() > pomiar.time.getMonth()) return 1;
if (time.getMonth() < pomiar.time.getMonth()) return -1;
if (time.getDay() > pomiar.time.getDay()) return 1;
if (time.getDay() < pomiar.time.getDay()) return -1;
if (time.getHour() > pomiar.time.getHour()) return 1;
if (time.getHour() < pomiar.time.getHour()) return -1;
if (time.getMinute() > pomiar.time.getMinute()) return 1;
if (time.getMinute() < pomiar.time.getMinute()) return -1;
return 0;
}


public String toString() {
return "Temperatura w dniu: " + time.getYear() + "." + time.getMonth() +
"." + time.getDay() + " godzina " + time.getHour() + ":" + time.getMinute() + " - " + temperature + " stopni Celsjusza";
}
}

public class AlgorytmyZ3V3 {
public static void main(String[] args) {


try
{
ObjectOutputStream fout = new ObjectOutputStream(new FileOutputStream(
"BazaPomiarow.dat"));

Time time1 = new Time(1988, 6, 15, 10, 18);
Time time2 = new Time(1980, 1, 28, 15, 20);
Time time3 = new Time(1980, 12, 9, 3, 20);
Time time4 = new Time(1984, 12, 9, 18, 25);
Time time5 = new Time(1990, 12, 9, 12, 55);
Time time6 = new Time(1984, 9, 9, 4, 40);
Time time7 = new Time(1970, 12, 9, 4, 25);
Time time8 = new Time(1966, 11, 10, 7, 44);

Pomiar p1 = new Pomiar(time1, 30);
Pomiar p2 = new Pomiar(time2, 20);
Pomiar p3 = new Pomiar(time3, 30);
Pomiar p4 = new Pomiar(time4, 25);
Pomiar p5 = new Pomiar(time5, 22);
Pomiar p6 = new Pomiar(time6, 12);
Pomiar p7 = new Pomiar(time7, -6);
Pomiar p8 = new Pomiar(time8, 3);

p1.save();
p2.save();
p3.save();
p4.save();
p5.save();
p6.save();

p6.output();

p7.save();
p7.output();

p8.saveChrono();
p8.output();

p8.outputParticularMonth(1980, 12);
}

catch (IOException e) {
System.out.println("Błąd wejścia/wyjścia.");
}

catch(Exception e) {
System.out.println("Wyjątek w czasie deserializacji: " + e);
}

}

}

Może i wciąż niezbyt elegancki, ale nic się nie dubluje, chronologia ładnie działa. Wyszukiwanie konkretnego miesiąca też. Został ostatni podpunkcik smile_prosty.gif

Link do komentarza
Udostępnij na innych stronach

Właśnie zauważyłem, że już naskrobałeś sporego edita, ale masz tu moje uwagi sprzed niego:

Z tego, co doczytałem, do tradycyjnego ObjectOutputStream'a nie można appendować. Tu masz rozwiązanie (uważaj, bo ten obiekt można tworzyć tylko w przypadku dopisywania do już istniejącego pliku, inaczej ).

Proponuję w klasie Pomiar takie metody do obsługi plików:


Klasa Pomiar zawierałaby takie metody:
static ArrayList<Pomiar> readFromFile(String) - odczytuje wszystkie pomiary z pliku
static void writeToFile(String, Collection<Pomiar>, boolean) - zapisuje kolekcję pomiarów do pliku, ostatni parametr oznacza appendowanie (do tego powinna albo rzucać ewentualnymi wyjątkami, albo zwracać booleana z informacją o ich wystąpieniu)

Ta druga w połączeniu z powyższą uwagą może się zaczynać tak:


ObjectOutputStream out = null;
if (append && new File(filename).exists()) {
out = new AppendingObjectOutputStream(new FileOutputStream(filename, true));
} else {
out = new ObjectOutputStream(new FileOutputStream(filename));
}

Metoda saveChrono mogłaby wyglądać jakoś tak (zauważ, że nie ma tu żadnych streamów):


static void saveChrono(String filename, Pomiar pomiar) {
ArrayList<Pomiar> list = readFromFile(filename);
list.add(pomiar);
Collectoins.sort(list);
writeToFile(filename, list, false);
}

Link do komentarza
Udostępnij na innych stronach

Biorąc pod uwagę, że klasa Pomiar służy przede wszystkim do przenoszenia danych, wrzucanie tam logiki zapisu do pliku to fatalny pomysł. Sugeruję wyrzucić te metody do osobnej klasy PomiarHelper albo coś w tmy guście i z tego korzystać.

Metody zapisu robią trzy rzeczy: odczyt z pliku, dodanie nowego elementu, zapis pliku. To jest gwałt na zasadzie pojedynczej odpowiedzialności. Tym bardziej, że kod odczytujący powtarza się trzy razy - po co, skoro można to wyciągnąć do osobnej metody? To samo dotyczy zapisu listy - to powinna być samodzielna metoda przyjmująca kolekcję i nie robiąca nic więcej poza zapisaniem do pliku. Natomiast nazwa pliku powinna wylądować w jakiejś stałej, skoro powtarza się parę razy.

Link do komentarza
Udostępnij na innych stronach



  • Kto przegląda   0 użytkowników

    • Brak zalogowanych użytkowników przeglądających tę stronę.
×
×
  • Utwórz nowe...