Obsługa plików w Javie jest wg. mnie rozwiązana trochę dziwnie. Ale ja programowałem głównie w C++ (w innych językach również, ale bez grzebania się w plikach), więc może się czepiam.
Tak czy siak, muszę się tego nauczyć i zanim wejdzie mi to w krew, postanowiłem sobie to zapisać.
Po pierwsze, z jakiegoś jeszcze dla mnie niejasnego powodu, operacje na plikach muszą zawierać się w bloku try-catch:
try { //operacje na plikach } //można użyć bardziej specyficznego kodu //w szczególności przechwytywać konkretne wyjątki //to jest tylko tak orientacyjnie catch(Exception e) { e.printStackTrace(); }
Następnie musimy otworzyć jakiś plik. Ogólna rada jest taka:
FileReader / FileWriter - dla plików tekstowych
FileInputStream / FileOutputStream - dla innych (binarnych, z niestandardowym kodowaniem...)
Pliki tekstowe
FileReader / FileWriter używamy głównie dla plików tekstowych, więc najlepiej od razu obudować je w BufferedReader / BufferedWriter:
BufferedReader br=new BufferedReader(new FileReader(nazwa_pliku));
i podobnie Writer.
Dane odczytujemy z takiego strumienia linia po linii:
String line; while( (line=bufferedReader.readLine()) != null ) { //operacje na wczytanej linii }
Po skończeniu operacji na plikach zamykamy plik (ale tylko ten strumień, który jest „na zewnątrz” - w przypadku plików tekstowych obudowanych strumieniem Buffered* będzie to właśnie ów strumień Buffered*):
br.close();
Pliki binarne
Dla plików binarnych, jeśli chcemy zapisywać/odczytywać same bajty (najlepiej w tablicach), możemy używać „gołego” File*Stream:
byte buffer[]=new byte[ROZMIAR]; FileOutputStream outputStream=new FileOutputStream(nazwa_pliku); outputStream.write(buffer); outputStream.close();
Tablice w Javie mają wbudowaną informację o wielkości (buffer.length), więc nie trzeba podawać tego parametru. Po dokładniejsze informacje odsyłam do dokumentacji.
Jeśli jednak chcielibyśmy zapisywać zmienne innego rodzaju, warto obudować taki strumień strumieniami odpowiednio DataInputStream i DataOutputStream:
... float zmienna; DataOutputStream outputData=new DataOutputStream(outputStream); outputData.writeFloat(zmienna); outputData.close();
I to właściwie wszystko (na razie). Jeszcze zapiszę tu parę specyficznych użyć takich strumieni.
Odczyt plików z internetu
Pliki z internetu, a konkretnie strony internetowe, odczytujemy praktycznie tak samo, jak pliki tekstowe. Jedyna różnica jest taka, że musimy stworzyć połączenie z danym adresem (podobnie jak zwykłe plik, także i te operacje muszą znajdować się w bloku try-catch):
URL strona=new URL(adres_strony); URLConnection polaczenie=strona.openConnection(); InputStreamReader inputStream=new InputStreamReader(polaczenie); BufferedReader input=new BufferedReader(inputStream); //wczytujemy plik html input.close();
Otwieranie plików za pomocą interfejsu graficznego
Jeśli mamy jakiś ładny programik z interfejsem graficznym i chcielibyśmy dodać tam jakiś komponent, dzięki któremu moglibyśmy otwierać okienko wyboru plików (coś na kształt SaveFileDialog i OpenFileDialog w C++ Builderze), to musimy zadeklarować zmienną typu JFileChooser (najlepiej jako final i/lub static):
final JFileChooser fileDialog=new JFileChooser();
W takim FileChooserze możemy ustawiać odpowiednie filtry, katalogi startowe itp., ale muszę powiedzieć, że jest to o wiele bardziej niewygodne od tego, z czym miałem do czynienia w C++ Builderze. Po konkretne informacje odsyłam do dokumentacji, ew. do googli.
Potrzebna nam też będzie zmienna typu File, która będzie przechowywała informacje o wybranym w ten sposób pliku:
File plik;
W jakimś evencie (najczęściej w naciśnięciu buttona) wpisujemy kod:
int returnVal; returnVal=fileDialog.showOpenDialog(this); //albo SaveDialog if(returnVal==JFileChooser.APPROVE_OPTION) { plik=fileDialog.getSelectedFile(); if(plik.exists()) { //jeśli chcemy, możemy coś z tym zrobić } else { //i ewentualnie dać znać, że pliku nie ma } }
Informacji przechowywanych w zmiennej typu File nie musimy wykorzystywać od razu - dany plik można otworzyć gdzieś indziej w programie. Nazwę pliku pobieramy w następujący sposób:
nazwaPliku=plik.getPath();
i dalej już otwieramy i dokonujemy odpowiednich operacji na danym pliku wg. poznanych wcześniej metod.
Strony, z których korzystałem:
- Dokumentacja Javy: Java™ Platform, Standard Edition 7, API Specification
- Ogólnie o plikach: Java File: Reading and Writing Files in Java
- Pliki binarne: Working with binary files
"Po pierwsze, z jakiegoś jeszcze dla mnie niejasnego powodu, operacje na plikach muszą zawierać się w bloku try-catch: "
OdpowiedzUsuńTo jest spowodowane tym że możemy mieć wiele różnych błędów na operacjach plikowych (np. FileNotFoundExceltion, itp), które w Javie powinny być obsłużone. Zawsze można zostawić blok catch z nieobsłużonym wyjątkiem (ale to nie jest zalecane, bo wówczas po co wyjątki) lub wyrzucić wyjątek na funkcję która wywołała naszą metodę. Wówczas obsługa wyjątku będzie w funkcji wołającej, a tam przez printStackTrace można zobaczyć w której klasie+lini kodu błąd został wyrzucony.
Swoją drogą zainteresowanym polecam książkę "Thinking in Java": http://www.mindview.net/Books/TIJ/
Jest ona również dostępna darmowo do pobrania w wersji elektronicznej
"...które w Javie powinny być [wyjątki] obsłużone."
UsuńChodzi mi bardziej o to, że nie mogę tych operacji zapisać poza blokiem try-catch. Wywala mi błąd (piszę w NetBeansie).
Jak robię np. Integer.ParseInt to mogę dać w bloku try-catch, żeby wyłapać błąd, ale nie muszę, jeśli jestem leniwy, albo piszę coś na szybkiego.
Jak robię operacje na plikach, to bezwarunkowo muszę umieścić je w bloku try-catch. W przeciwnym wypadku pokazuje mi błąd "unreported exception FileNotFoundException; must be caught or declared to be thrown"
PS. Dzięki za linka. Już sobie ściągnąłem (i parę innych też, chociaż pewnie i tak na razie z nich nie będę korzystał).
Usuń