Poniższy artykuł powstał wiele lat temu z intencją publikacji w "Magazynie Amiga". Nie pamiętam już, czy w końcu go wysłałem do redakcji, czy też nie. Zapewne to pierwsze, a redakcja uznała rzecz za mało godną uwagi. Czy dziś, współcześnie, proponowane rozwiązanie może komuś się przydać? Być może. Retro jest wszak modne, programowanie w "wyklętym" Amosie proste i przyjemne (ah, niechaj zatem ten, komu podoba się Python pierwszy rzuci kamieniem w nas, amosowców...!), a jak ktoś potrzebuje akurat umieścić większą ilość danych na dysku... Oto gotowe rozwiązanie. Oryginalny tekst prezentuję właściwie bez zmian, tylko z drobnymi, kosmetyczno-historycznymi poprawkami.
Dawno, dawno temu, gdy wszyscy byli młodzi, a jabłka zawsze kwaśne i zielone, w "Magazynie Amiga" numer 12/96 ukazał się artykuł Łukasza Szeląga pt. "Oszczędzanie miejsca na dyskietkach". Dotyczył on genialnego pomysłu - łączenia wielu małych pliczków w jeden, za to duży plik. Zalety takiego rozwiązania? Głównie, by tak rzec z perspektywy czasu, historyczne... Przede wszystkim oszczędzanie miejsca na dysku, szybszy dostęp do danych (głównie na dyskietkach). Zapewne w dobie dzisiejszych wielkich dysków twardych, uzyskana oszczędność miejsca, jak i czas dostępu, jest niewielka, za to estetyka rozwiązania niewątpliwie lepsza. Zamiast mnóstwa plików - jeden duży. Niestety Łukasz podał rozwiązanie w snobistycznym "c" (wymawia się to jak sepleniące "ś", dziwne nie?). Chciałbym przedstawić to od strony Amosa. Rzecz jest bardzo prosta do wykonania.
Czas na przykład. Załóżmy, iż mamy 100000 pliczków z danymi (np. tekstem programujemy własny zin, co jest bardzo miłe, ponieważ pozwala na bezpośrednie głoszenie swoich poglądów na różne tematy), nasze pliki są bardzo małe, a jednak taka ilość może nie wejść na dyskietkę. Każdy wpis do katalogu dysku zabiera nam kawałeczek pojemności, poza tym, to o czym mowa była wcześniej - czas dostępu!
Zatem, 100000 plików o nazwie "tekst" plus jego numer (np. tekst1, tekst2 aż do tekst100000). Oczywiście mogą one mieć różne nazwy, lecz w takim wypadku nie da się zautomatyzować czynności łączenia ich w jeden duży plik.
Pierwsza procedura może być taka:
Procedure JOIN[SKAD$,NE$,ILE] ' SKAD$ - urządzenie bądź katalog, gdzie są nasze pliki przeznaczone do łączenia, np. "ram:" albo "Work:amos/zin/luty/artykuly" ' NE$ - nazwa bazowa plików np. "tekst" ' ILE - ile tego jest, np. 1000000 For A=1 To ILE : Rem - liczba plików A$=Right$(Str$(A),Len(Str$(A))-1) : Rem usuwa spacje sprzed liczby If Exist(SKAD$+NE$+A$) : Rem dla bezpieczeństwa, sprawdza czy plik jest. Print At(0,0),"Pracuje nad - ";NE$+A$;" " : Rem ot, bajer... Load SKAD$+NE$+A$,3 XSAVE[NE$,A,3] : Rem Bardzo Ważna procedura! Erase 3 End If Next A End Proc
To tylko przykład. Dla uproszczenia przyjąłem, iż dane są już w formacie banku Amosa (komenda "Load" akceptuje tylko takie). Jeżeli tak nie jest, należy skorzystać z innych instrukcji (np. "Open" i "Input#", bądź "Sload", "Bload") tak, aby "zwykłe, nieamosowe" dane wrzucić do banku Amosa. Procedura robi właściwie jedną rzecz - wczytuje do pamięci kolejne pliki ("tekst1", "tekst2" itp. - po to one mają jedną nazwą bazową, aby komputer za nas wszystko robił), a następnie wywołuje główną procedurę:
Procedure XSAVE[X$,NR,BANK] ' X$ - nazwa naszego pliku, który chcemy nagrać ' NR - jego numer ' BANK - gdzie on jest (w jakim banku) AD=Start(BANK) A$=Right$(Str$(NR),Len(Str$(NR))-1) If Length(AD)<>0 Append 1,"Ram:Indeks"+".xbf" : Rem dołączamy dane do pliku indeksu Append 2,"Ram:Baza" : Rem dołączamy dane do pliku bazy POZ=Pof(2) PLEN=Length(AD) Print #1,X$+A$+"$"+Right$(Str$(POZ),Len(Str$(POZ))-1)+"!"+Right$(Str$(PLEN),Len(Str$(PLEN))-1)+"&" Ssave 2,Start(AD) To Start(AD)+Length(AD) Close 1 : Close 2 : Rem tylko dla przykładu! End If End Proc
Mała uwaga - oba rozkazy "Close" powinny być oczywiście poza tą procedurą, bo powodują one za każdym razem "zamknięcie" pliku. A to należy uczynić dopiero pod koniec całej operacji. W wyniku tej procedury otrzymujemy dwa twory na dysku: jeden to plik indeksowy (z końcówką.xbf) a drugi (ten duży) to baza danych zawierająca wszystkie małe pliczki. Plik indeksowy jest zwykłym plikiem tekstowym i wygląda tak:
tekst1$0!23832&
tekst2$12!3984934&
tekst3$459!487&
i tak aż do:
tekst100000$383102439!340430585543&
Gdzie "tekst1" to nazwa pliku, potem jego pozycja (w pliku bazy jego początek liczony jest od początku tego pliku) oraz długość. Znaki "$", "!" i "&" są na użytek procedury, która odczytuje małe pliki z dużego. Musi ona wszak znać położenie każdego małego pliku umieszczonego w dużym (bazowym).
Komenda "Print#" dogrywa kolejną linę do pliku indeksowego, a komenda "Ssave" dane do pliku bazy. Polecenie "Append" jest właśnie po to, aby dogrywać do końca istniejącego już pliku nowe dane.
A skąd dokładnie wiemy gdzie w dużym pliku jest jakiś konkretny mały plik? Dobre pytanie. Odczytujemy poleceniem "Pof" miejsce, gdzie "Append" dołącza dane. Teraz, aby odczytać je z powrotem, musimy dodać do tego długość pliku, który nas interesuje. Wszystko to mamy już zapisane w indeksie. Jak go użyć (i jak wczytać) taki plik? Można tak:
Procedure XINIT[NE$] Open In 1,NE$+".xbf" Reserve As Data 6000,Lof(1) Close 1 Bload NE$+".xbf",Start(6000) End Proc
Numer banku jest obojętny. Mając indeks w pamięci, jesteśmy gotowi do głównego "dania" - odczytania danych z pliku bazowego. Oto propozycja procedury odczytującej.
Procedure XLOAD[X$,NR] ' X$ - nazwa pliku, który chcemy odczytać ' NR - jego numer A$=Right$(Str$(NR),Len(Str$(NR))-1) NE$="Ram:Baza" : Rem nazwa pliku bazy ' Wcześniej należy załadować plik indeksowy do banku Amosa ' Tutaj jest on w banku numer 60000 (procedura XINIT) XS=Start(6000) : XL=XS+Length(6000) AD=100 : Rem AD to numer banku, gdzie chcemy umieścić rozpakowany plik ' Oczywiście można rozpakować za jednym zamachem wiele plików, trzeba tylko dodać jedną linijkę: ' AD=100+NR ' Nowe pliki będą umieszczane sukcesywnie jeden za drugim. ARK=Hunt(XS To XL,X$+A$+"$") : Rem szukamy konkretnego pliku! ' np. "tekst1" ' Można szukać wedle nazwy pliku wraz z jego numerem, po to właśnie jest zmienna "X$" ' Zmienna "ARK" podaje nam adres, pod którym znaleziono dany tekst (konkretną nazwę pliku np. "tekst666") ' teraz wystarczy odczytać pozycję i długość: POZ=Val(Peek$(ARK+Len(A$)+2,999999,"!")) : Rem pozycja PLEN=Val(Peek$(ARK+Len(Str$(POZ))+Len(A$)+2,999999,"&")) : Rem długość Reserve As Data AD,PLEN : Rem rezerwujemy nowy bank dla naszych danych Open In 1,NE$ Pof(1)=POZ : Rem ustawiamy głowicę dysku na początek danych Sload 1 To Start(AD),PLEN : Rem i odczytujemy tyle ile nam potrzeba... Close 1 End Proc
W wyniku działania tej procedury mamy we wskazanym banku nasz mały pliczek. Możemy odczytywać za jej pomocą pojedyncze pliki (do wskazanego banku, trzeba tylko dodać nową zmienną, która by przekazywała procedurze "Xload" numer banku), jak i dowolną ich ilość (w pętli albo dodać jeszcze jedną zmienną mówiącą ile małych plików chcemy odczytać za jednym uruchomieniem procedury).
Zasada zostaje ta sama - odczyt danych z dużego pliku za pomocą danych z pliku indeksowego. Najwygodniej umieścić go sobie w pamięci, aby nie wczytywać go za każdym razem (oszczędność czasu!). Dalej, można dodać rozpoznawanie czy wczytujemy bank danych, rysunek czy moduł – ponieważ nie da się tak bezpośrednio wczytać modułu czy obrazka. Najlepiej (i ten sposób wykorzystałem pisząc grę "Ciemna Strona"), spakować wcześniej rysunki i moduły muzyczne - są one wtedy w postaci banku Amosa (komendy "squash" i "unsquash" - mogą być też użyte rozszerzenia programowe Amosa umożliwiające obsługę Xpk czy Crunchmanii) i nie ma problemów z ich wczytaniem (potem oczywiście się je rozpakowuje). Zysk podwójny - nasze dane są spakowane i umieszczone w jednym dużym pliku.