Книга е още в много ранна фаза на написване


Отделен: RandomAccessFile



страница60/73
Дата25.07.2016
Размер13.53 Mb.
#6732
1   ...   56   57   58   59   60   61   62   63   ...   73

Отделен:
RandomAccessFile


RandomAccessFile се използва за файлове състоящи се от записи с известна дъл­жина, така че може да се придвижвате от някакъв запис на друг чрез seek( ), да го четете и презаписване. Записите не трябва непременно да са с еднаква дъл­жина; само трябва да се знае колко са големи и къде във файла се намират.

Отначало е малко трудно да се повярва че RandomAccessFile не е част от InputStream или OutputStream йерархията. Той няма отношения с тези йе­рар­хии освен че се случва да използва DataInput и DataOutput интерфейсите (кои­то също са реализирани от DataInputStream и DataOutputStream). Даже не из­полз­ва нищо от функционалността на InputStream или OutputStream кла­со­ве­те – това е напълно отделен клас, написан от нулата, с изцяло собствени (най-ве­че native) методи. Причината за това може да е че RandomAccessFile има изоснови различно от другите IO типове поведение, доколкото може да се дви­жи­те напред-назад във файла. Във всеки случай, той стои самостоятелно, като ди­ректен наследник на Object.

Основно, RandomAccessFile работи подобно на DataInputStream заедно с DataOutputStream и методите getFilePointer( ) за да намери къде сте във фай­ла, seek( ) за придвижване към нова точка от файла, length( ) за определяне на мак­сималната дължина на файла. Освен това конструкторите изискват втори аргументи (като fopen( ) в C) показващ дали четете (“r”) или четете и пишете (“rw”). Няма поддръжка за файлове само за четене, което означава, че RandomAccessFile би могъл да работи добре ако беше наследен от DataInputStream.

Още по-разочароващото е, че лесно може да се въобразим произволен достъп в раз­лични потоци, като ByteArrayInputStream например, но съответните ме­то­ди са налични само в RandomAccessFile, който работи само с файлове. BufferedInputStream не позволява да mark( ) мястото (чиято стойност се пази във вътрешна променлива) и reset( ) към това място, но това е ограничено и не мно­го полезно.


Класът File


Класът File има измамно име – може да помислите, че е за файлове, но не е. Той мо­же да представи или име на конкретен файл или имена на множество от фай­ло­ве в директория. Ако това е множество от файлове, може да питате за него с ме­тода list( ), а той връща масив от String. Има смисъл да се връща масив на­ме­сто някоя колекция, цащото броят на елементите е фиксиран, а ако искате друга ди­ректория просто създавате друг File обект. Фактически “FilePath” щеше да бъ­де по-добро име. Тази секция привежда пример с пълно използване на класа, вклю­чително асоциирания FilenameFilter interface.

A directory lister


Да предположим, че трябва списък на файловете в директория. Обектът File мо­же да го направи по два начина. Ако извикате list( ) без аргументи, ще по­лучи­те пълен списък на имената съдържани във File обекта. Обаче ако искате спи­сък с ограничения, например на всички файлове с разширение .java, из­полз­ва­те “директорен филтър,” което е клас, казващ как да се изберат за изо­бра­зя­ва­не имената във File обектите.

Ето сорса на примера: (Виж стр. 89 при проблеми с пускането на програмата.)

//: c10:DirList.java

// Листинг на директорията

package c10;

import java.io.*;


public class DirList {

public static void main(String[] args) {

try {

File path = new File(".");



String[] list;

if(args.length == 0)

list = path.list();

else


list = path.list(new DirFilter(args[0]));

for(int i = 0; i < list.length; i++)

System.out.println(list[i]);

} catch(Exception e) {

e.printStackTrace();

}

}



}
class DirFilter implements FilenameFilter {

String afn;

DirFilter(String afn) { this.afn = afn; }

public boolean accept(File dir, String name) {

// Strip path information:

String f = new File(name).getName();

return f.indexOf(afn) != -1;

}

} ///:~



Класът DirFilter “реализира” interface FilenameFilter. (Интерфейсите бяха раз­гле­дани в глава 7.) Полезно е да се види колко прост е FilenameFilter interface:

public interface FilenameFilter {

boolean accept(File dir, String name);

}

Той казва, че всичко което прави този клас е да даде метода accept( ). Цялата при­чина за създаването на този клас е да се даде методът accept( ) на list( ) ме­то­да така че list( ) да може да извика обратно accept( ) за да определи кои фай­ло­ви имена ще се включват в списъка. Тази техника често се нарича callback или понякога functor (тоест, DirFilter е функтор понеже единствената му за­да­ча е да съдържа метод). Понеже list( ) взима FilenameFilter обект за аргумент, то­ва значи че може да дадете произволен обект от тип FilenameFilter за избор (да­же по време на изпълнение) поведението на метода list( ). Предназначението на обратното извикване е да се даде гъвкавост откъм поведението на коде.



DirFilter показва че тъй като interface съдържа само множество от методи, не сте ограничени да работите само с тях. (Трябва най-малкото да дадете де­фи­ни­ции за всичките методи на интерфейса, обаче.) В този случай се създава също и кон­структор на DirFilter.

Методът accept( ) трябва да приеме File обект представящ къде даден файл е на­мерен, а String съдържа името на този файл. Бихте могли да изберете дали да из­ползвате всеки от рагументите, но най-вероятно ще използвате поне фай­ло­во­то име. Запомнете че метода list( ) вика accept( ) за всяко от фойловите имена в спи­съка за да види дали то ще бъде включено – това се индицира чрез boolean ре­зултата връщан от accept( ).

За да се работи само с имената, без информацията за пътя, трябва само да се взе­ме String обект и да се създаде File обект от него, тогава да се извика getName( ) което изрязва цялата информация за пътя по независим от плат­фор­ма­та начин). Тогава accept( ) използва String класовия метод indexOf( ) да види да­ли стрингът afn се появява някъде във файловото име. Ако afn е намерен в стрин­га, върнатата стойност е началният индекс в afn, а ако не се намери вър­на­та­та стойност е -1. Помнете че това е просто търсене в стринг и няма търсене по съвпадения като в регулярни изрази с “wildcard” знаци като в “fo?.b?r*” кое­то е много по-трудно за реализиране.

Методът list( ) връща масив. Може да проверите дължината му и после да се дви­жите по него. Тази възможност непосредствено да се дават масиви като ар­гу­мент и да се връщат е огромно придвижване напред в сравнение с C и C++.


Анонимни вътрешни класове


Този пример е идеален за пренаписване с анонимни вътрешни класове (описани в глава 7). Като за начало се създава методът filter( ) който връща манипулатор към FilenameFilter:

//: c10:DirList2.java

// Uses Java 1.1 anonymous inner classes

import java.io.*;


public class DirList2 {

public static FilenameFilter

filter(final String afn) {

// Creation of anonymous inner class:

return new FilenameFilter() {

String fn = afn;

public boolean accept(File dir, String n) {

// Strip path information:

String f = new File(n).getName();

return f.indexOf(fn) != -1;

}

}; // End of anonymous inner class



}

public static void main(String[] args) {

try {

File path = new File(".");



String[] list;

if(args.length == 0)

list = path.list();

else


list = path.list(filter(args[0]));

for(int i = 0; i < list.length; i++)

System.out.println(list[i]);

} catch(Exception e) {

e.printStackTrace();

}

}



} ///:~

Забележете че аргументът към filter( ) трябва да бъде final. Това се изисква от въ­трешния клас така че той да може да използва променливите на обекта извън об­хвата си.

Този дизайн е усъвършенстван, понеже класът FilenameFilter сега е тясно свър­зан с DirList2. Обаче може да се придвижите стъпка напред с този подход и да де­финирате анонимен вътрешен клас за аргумент на list( ), в който случай той е да­же по-малък:

//: c10:DirList3.java

// Building the anonymous inner class "in-place"

import java.io.*;


public class DirList3 {

public static void main(final String[] args) {

try {

File path = new File(".");



String[] list;

if(args.length == 0)

list = path.list();

else


list = path.list(

new FilenameFilter() {

public boolean

accept(File dir, String n) {

String f = new File(n).getName();

return f.indexOf(args[0]) != -1;

}

});


for(int i = 0; i < list.length; i++)

System.out.println(list[i]);

} catch(Exception e) {

e.printStackTrace();

}

}

} ///:~



Аргументът на main( ) е сега final, понеже анонимният вътрешен клас използва директно args[0].

Това показва как анонимните вътрешни класове позволяват да се създадат бързо и лесно класове. Тъй като всичко в Java се върти около класовете, това би мо­гло да бъде полезна техника. Една полза е че кодът, който решава даден про­блем остава тясно свързан и в една точка. От друга страна, не винаги е лесен за че­тене, така че трябва да се използва разумно.


Сортиран списък на директорията


А-а, искате имената сортирани? Тъй като нява поддръжка за сортиране в Java 1.0 и Java 1.1 (макар и сортирането да е включено в Java 2), ще трябва да го вклю­чим директно в програмата използвайки SortList създаден в глава 8:

//: c10:SortedDirList.java

// Displays sorted directory listing

import java.io.*;

import c08.*;
public class SortedDirList {

private File path;

private String[] list;

public SortedDirList(final String afn) {

path = new File(".");

if(afn == null)

list = path.list();

else


list = path.list(

new FilenameFilter() {

public boolean

accept(File dir, String n) {

String f = new File(n).getName();

return f.indexOf(afn) != -1;

}

});


sort();

}

void print() {



for(int i = 0; i < list.length; i++)

System.out.println(list[i]);

}

private void sort() {



StrSortList sv = new StrSortList();

for(int i = 0; i < list.length; i++)

sv.add(list[i]);

// The first time an element is pulled from

// the StrSortList the list is sorted:

for(int i = 0; i < list.length; i++)

list[i] = sv.get(i);

}

// Test it:



public static void main(String[] args) {

SortedDirList sd;

if(args.length == 0)

sd = new SortedDirList(null);

else

sd = new SortedDirList(args[0]);



sd.print();

}

} ///:~



Няколко други подобрения бяха направени. Вместо да се създават path и list ка­то локални променливи на main( ), те са членове на клас, така че техните стой­ности са достъпни по времето на живот на класа. Фактически main( ) сега е про­сто начин да се тества класа. Може да видите, че конструктора на класа ав­то­матично сортира имената щом списъка се създаде.

Сортирането е независимо от големи или малки букви, така че не получавате сор­тиран списък на думите с главни букви следван от тези с малки. Може обаче да забележите, че когато има две имена с еднакви бекви първо се изобразява то­ва с голяма, което все още не е точно желаното повезение за сортирането. Този про­блем ще се реши в Java 2.


Проверка за и създаване на директории


Класът File е повече от просто представяне на път, файл или група файлове. Съ­що може да използвате File обект за създаване на нова директория или ця­ло­стен път, който не съществува. Може също да гледате характеристиките на фай­ло­вете (дължина, дата на последната промяна, четене/писане), дали File обекта пред­ставя файл или директория и да изтриете файл. Тази програма показва оста­налите методи налични в класа File:

//: c10:MakeDirectories.java

// Demonstrates the use of the File class to

// create directories and manipulate files.

import java.io.*;
public class MakeDirectories {

private final static String usage =

"Usage:MakeDirectories path1 ...\n" +

"Creates each path\n" +

"Usage:MakeDirectories -d path1 ...\n" +

"Deletes each path\n" +

"Usage:MakeDirectories -r path1 path2\n" +

"Renames from path1 to path2\n";

private static void usage() {

System.err.println(usage);

System.exit(1);

}

private static void fileData(File f) {



System.out.println(

"Absolute path: " + f.getAbsolutePath() +

"\n Can read: " + f.canRead() +

"\n Can write: " + f.canWrite() +

"\n getName: " + f.getName() +

"\n getParent: " + f.getParent() +

"\n getPath: " + f.getPath() +

"\n length: " + f.length() +

"\n lastModified: " + f.lastModified());

if(f.isFile())

System.out.println("it's a file");

else if(f.isDirectory())

System.out.println("it's a directory");

}

public static void main(String[] args) {



if(args.length < 1) usage();

if(args[0].equals("-r")) {

if(args.length != 3) usage();

File


old = new File(args[1]),

rname = new File(args[2]);

old.renameTo(rname);

fileData(old);

fileData(rname);

return; // Exit main

}

int count = 0;



boolean del = false;

if(args[0].equals("-d")) {

count++;

del = true;

}

for( ; count < args.length; count++) {



File f = new File(args[count]);

if(f.exists()) {

System.out.println(f + " exists");

if(del) {

System.out.println("deleting..." + f);

f.delete();

}

}

else { // Doesn't exist



if(!del) {

f.mkdirs();

System.out.println("created " + f);

}

}



fileData(f);

}

}



} ///:~

Във fileData( ) може да видите различни методи за файловете, използвани по под­ходящ начин.

Първият метод изпитан в main( ) е renameTo( ), който позволява да се преи­ме­ну­ва (или премести) файл към напълно нов пъът, представен от аргумента, който е друг File обект. Това също работи за директории с всякаква дължина.

Ако експериментирате с горната програма, може да видите че е възможно да се на­прави път с всякаква сложност, понеже mkdirs( ) ще направи нужното. В Java 1.0, флагът -d показва, че директорията е изтрита, но все още е там (т.е. е беля­за­на за изтриване - б.пр.); в Java 1.1 директорията е изтрита фактически.





Сподели с приятели:
1   ...   56   57   58   59   60   61   62   63   ...   73




©obuch.info 2024
отнасят до администрацията

    Начална страница