Міністерство освіти і науки України
Національний університет “Львівська політехніка”
/
Звіт
До Лабораторної роботи №6
З дисципліни: «Кросплатформені засоби програмування»
Мета: оволодіти навиками використання засобів мови Java для роботи з потоками і файлами.
ТЕОРЕТИЧНІ ВІДОМОСТІ
Бібліотека класів мови Java має більше 60 класів для роботи з потоками. Потаками у мові Java називаються об’єкти з якими можна здійснювати обмін даними. Цими об’єктами найчастіше є файли, проте ними можуть бути стандартні пристрої вводу/виводу, блоки пам’яті і мережеві підключення тощо. Класи по роботі з потоками об’єднані у кілька ієрархій, що призначені для роботи з різними видами даних, або забезпечувати додаткову корисну функціональність, наприклад, підтримку ZIP архівів.
Класи, що спадкуються від абстрактних класів InputStream і OutputStream призначені для здійснення байтового обміну інформацією. Підтримка мовою Java одиниць Unicode, де кожна одиниця має кілька байт, зумовлює необхідність у іншій ієрархії класів, що спадкується від абстрактних класів Reader і Writer. Ці класи дозволяють виконувати операції читання/запису не байтних даних, а двобайтних одиниць Unicode.
Принцип здійснення читання/запису даних нічим не відрізняється від такого принципу у інших мовах програмування. Все починається з створення потоку на запис або читання після чого викликаються методи, що здійснюють обмін інформацією. Після завершення обміну даними потоки необхідно закрити щоб звільнити ресурси.
Принципи роботи з файловими потоками
Для створення файлових потоків і роботи з ними у Java є 2 класи, що успадковані від InputStream і OutputStream це - FileInputStream і FileOutputStream. Як і їх суперкласи вони мають методи лише для байтового небуферизованого блокуючого читання/запису даних та керуванням потоками. На відміну від, наприклад, мови програмування С, де для виконання усіх можливих операцій з файлами необхідно мати один вказівник на FILE у мові Java реалізовано інший набагато складніший і гнучкіший підхід, який дозволяє формувати такі властивості потоку, які найкраще відповідають потребам рішення конкретної задачі. Так у Java розділено окремі функціональні можливості потоків на різні класи. Компонуючи ці класи між собою і досягається необхідна кінцева функціональність потоку. Так одні класи, як FileInputStream, забезпечують елементарний доступ до файлів, інші, як PrintWriter, надають додаткової функціональності по високорівневій обробці даних, що пишуться у файл. Ще інші, наприклад, BufferedInputStream забезпечують буферизацію. Таким чином, наприклад, щоб отримати буферизований файловий потік для читання інформації у форматі примітивних типів (char, int, double,…) слід створити потік з одночасним сумісним використанням функціональності класів FileInputStream,
BufferedInputStream і DataInputStream. Для цього слід здійснити наступний виклик:
DataInputStream din = new DataInputStream( new BufferedInputStream(
new FileInputStream)));
Класи типу BufferedInputStream, DataInputStream, PushbackInputStream (дозволяє читати з потоку дані і повертати їх назад у потік) успадковані від класу FilterInputStream. Вони виступають так званими фільтрами, що своїм комбінуванням забезпечують додаткову лише необхідну функціональність при читанні даних з файлу. Аналогічний підхід застосовано і при реалізації класів для обробки текстових даних, що успадковані від Reader і Writer.
Читання з текстових потоків
Для читання текстових потоків найкраще підходить клас Scanner. На відміну від InputStreamReader і FileReader, що дозволяють лише читати текст, він має велику кількість методів, які здатні читати як рядки, так і окремі примітивні типи з подальшим їх перекодуванням до цих типів, робити шаблонний аналіз текстового потоку, здатний працювати без потоку даних та ще багато іншого. Приклад читання даних за допомогою класу Scanner з стандартного потоку вводу:
Scanner sc = new Scanner(System.in); int i = sc.nextInt();
Приклад читання даних за допомогою класу Scanner з текстового файлу:
Scanner sc = new Scanner(new File("myNumbers")); while (sc.hasNextLong()) {
long aLong = sc.nextLong();
}
До виходу Java 5.0 єдиним класом для обробки вхідних текстових даних був клас
BufferedReader, який мав метод readLine для читання одного рядку тексту.
Запис у текстові потоки
Для буферизованого запису у текстовий потік найкраще використовувати клас PrintWriter. Цей клас має методи для виводу рядків і чисел у текстовому форматі: print, println, printf, - принцип роботи яких співпадає з аналогічними методами Systen.out.
Приклад використання класу PrintWriter:
PrintWriter out = new PrintWriter (“file.txt”); out.print(“Hello ”);
out.print(1070); out.println(“! I’m World.”); out.close();
Читання і запис двійкових даних
Читання двійкових даних примітивних типів з потоків здійснюється за допомогою класів, що реалізують інтерфейс DataInput, наприклад класом DataInputStream. Інтерфейс DataInput визначає такі методи для читання двійкових даних:
readByte;
readInt;
readShort;
readLong;
readFloat;
readDouble;
readChar;
readBoolean;
readUTF.
Приклад читання двійкових даних з файлу:
DataInputStream in = new DataInputStream(new FileInputStream (“binarydata.dat”));
int n = in.readInt();
Запис двійкових даних примітивних типів у потоки здійснюється за допомогою класів, що реалізують інтерфейс DataOutput, наприклад класом DataOutputStream. Інтерфейс DataOutput визначає такі методи для запису двійкових даних:
writeByte;
writeInt;
writeShort;
writeLong;
writeFloat;
writeDouble;
writeChar;
writeChars;
writeBoolean;
writeUTF.
Метод writeUTF здійснює запис рядка у модифікованому машиннонезалежному форматі UTF-8, який крім Java мало хто використовує. Тому для потоків, які не призначені суто для Java, слід використовувати метод writeChars.
Приклад запису двійкових даних у файл:
DataOutputStream out = new DataOutputStream(new FileOutputStream (“binarydata.dat”));
out.writeInt(5);
Файли з довільним доступом
Керування файлами з можливістю довільного доступу до них здійснюється за допомогою класу RandomAccessFile. Відкривання файлу в режимі запису і читання/запису здійснюється за допомогою конструктора, що приймає 2 параметри – посилання на файл (File file) або його адресу (String name) та режим відкривання файлу (String mode):
RandomAccessFile(File file, String mode); RandomAccessFile(String name, String mode).
Параметр mode може приймати такі значення:
"r" – читання;
"rw" – читання/запис;
"rws" – читання/запис даних з негайним синхронним записом змін у файл або метадані файлу;
"rwd" – читання/запис даних з негайним синхронним записом змін у файл, метадані файлу не міняються одразу.
Файли, що керуються класом RandomAccessFile, оснащені вказівником на позицію наступного байту, що має читатися або записуватися. Для того, щоб перемістити даний вказівник на довільну позицію в межах файлу використовується метод void seek(long pos). Параметр long pos визначає номер байту, що має читатися або записуватися.
Щоб дізнатися поточну позицію вказівника на позицію наступного байту слід використати метод long getFilePointer().
Для визначення довжини файлу використовується метод long length(), а для закриття файлу – void close().
Для читання і запис даних клас RandomAccessFile реалізує методи інтерфейсів
DataInput і DataOutput, які описані у попередньому пункті.
Довільний доступ до файлу є найповільнішим способом доступу до файлів. Трішки швидшим є звичайний потік даних і набагато швидшим є буферизований потік даних.
Виконання лабораторної роботи
Завдання:
Варіант – 14 - y=cos(x)/tg(2x)
Створити клас, що реалізує методи читання/запису у текстовому і двійковому форматах результатів роботи класу, що розроблений у лабораторній роботі №5. Написати програму для тестування коректності роботи розробленого класу.
Код програми:
package ki43.karabas.Lab6;
import java.io.IOException;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws IOException {
CalcWFio obj = new CalcWFio();
Scanner s = new Scanner(System.in);
System.out.print("Enter data: ");
double data = s.nextDouble();
obj.calculate(data);
System.out.println("Result is: " + obj.getResult());
obj.writeResTxt("textRes.txt");
obj.writeResBin("BinRes.bin");
obj.readResBin("BinRes.bin");
System.out.println("Result is: " + obj.getResult());
obj.readResTxt("textRes.txt");
System.out.println("Result is: " + obj.getResult());
}
}
package ki43.karabas.Lab6;
class CalculationException extends ArithmeticException {
public CalculationException() {
}
public CalculationException(String cause) {
super(cause);
}
}
public class ExpressionCalculator {
public double calculate(double x) {
double y;
try {
double rad1 = x * Math.PI / 180.0;
double rad2 = 2 * x * Math.PI / 180.0;
y = Math.cos(rad1) / Math.tan(rad2);
if (y == Double.NEGATIVE_INFINITY || y == Double.POSITIVE_INFINITY || x == 90 || x == -90)
throw new ArithmeticException();
} catch (ArithmeticException e) {
throw new CalculationException("Can't calculate value for " + x);
}
return y;
}
}
package ki43.karabas.Lab6;
import java.io.*;
import java.util.*;
class CalcWFio {
private static final ExpressionCalculator expressionCalculator = new ExpressionCalculator();
public void writeResTxt(String fName) throws FileNotFoundException {
PrintWriter f = new PrintWriter(fName);
f.printf("%f ", result);
f.close();
}
public void readResTxt(String fName) {
try {
File f = new File(fName);
if (f.exists()) {
Scanner s = new Scanner(f);
result = s.nextDouble();
s.close();
} else
throw new FileNotFoundException("File " + fName + "not found");
} catch (FileNotFoundException ex) {
System.out.print(ex.getMessage());
}
}
public void writeResBin(String fName) throws IOException {
DataOutputStream f = new DataOutputStream(new FileOutputStream(fName));
f.writeDouble(result);
f.close();
}
public void readResBin(String fName) throws IOException {
DataInputStream f = new DataInputStream(new FileInputStream(fName));
result = f.readDouble();
f.close();
}
public void calculate(double x) {
result = expressionCalculator.calculate(x);
}
public double getResult() {
return result;
}
private double result;
}
Результат виконання програми:
Enter data: 1
Result is: 28.63189184283781
Result is: 28.63189184283781
Result is: 28.631892
Висновок: на даній лабораторній роботі я оволодів навиками використання засобів мови Java для роботи з потоками і файлами.