inne ruby receptury lucas carlson ebook

background image

Wydawnictwo Helion
ul. Koœciuszki 1c
44-100 Gliwice
tel. 032 230 98 63

e-mail: helion@helion.pl

Ruby. Receptury

Zbiór gotowych rozwi¹zañ dla programistów u¿ywaj¹cych jêzyka Ruby

• Jak przetwarzaæ pliki XML i HTML?
• Jak wykorzystywaæ œrodowisko Ruby on Rails?
• W jaki sposób ³¹czyæ Ruby z technologi¹ AJAX?

Korzystasz w pracy z jêzyka Ruby i zastanawiasz siê, czy niektóre zadania
programistyczne mo¿na wykonaæ szybciej? Chcesz poznaæ zasady programowania
obiektowego w Ruby? A mo¿e interesuje Ciê framework Ruby on Rails? Jêzyk Ruby
zdobywa coraz wiêksz¹ popularnoœæ, jest wykorzystywany do tworzenia aplikacji
sieciowych i sta³ siê podstaw¹ œrodowiska Ruby on Rails. Jednak nawet najlepszy
jêzyk programowania nie uwalnia programistów od ¿mudnego realizowania zadañ,
które nie maj¹ zbyt wiele wspólnego z tworzeniem aplikacji, czyli usuwania b³êdów,
implementowania typowych algorytmów, poszukiwania rozwi¹zañ mniej lub bardziej
typowych problemów i wielu innych.

Ksi¹¿ka „Ruby. Receptury” znacznie przyspieszy Twoj¹ pracê. Znajdziesz tu kilkaset
praktycznych rozwi¹zañ problemów wraz z przejrzystym komentarzem oraz tysi¹ce
wierszy proponowanego kodu, który bêdziesz móg³ wykorzystaæ w swoich projektach.
Przeczytasz o strukturach danych, algorytmach, przetwarzaniu plików XML i HTML,
tworzeniu interfejsów u¿ytkownika dla aplikacji i po³¹czeniach z bazami danych.
Nauczysz siê generowaæ i obrabiaæ pliki graficzne, korzystaæ z us³ug sieciowych,
wyszukiwaæ i usuwaæ b³êdy w aplikacjach, a tak¿e pisaæ skrypty niezwykle pomocne
w administrowaniu systemem operacyjnym Linux.

• Przetwarzanie danych tekstowych i liczbowych
• Operacje na tablicach
• Praca z systemem plików
• Programowanie obiektowe
• Przetwarzanie dokumentów XML i HTML oraz plików graficznych
• Generowanie plików PDF
• Po³¹czenie z bazami danych
• Korzystanie z poczty elektronicznej, protoko³u telnet i po³¹czeñ Torrent
• Projektowanie aplikacji internetowych za pomoc¹ Ruby on Rails
• Stosowanie us³ug sieciowych
• Optymalizacja aplikacji
• Tworzenie wersji dystrybucyjnych
• Automatyzacja zadañ z wykorzystaniem jêzyka Rake
• Budowanie interfejsów u¿ytkownika

Jeœli chcesz rozwi¹zaæ problem, skorzystaj z gotowej receptury — ko³o ju¿ wynaleziono

Autorzy: Lucas Carlson, Leonard Richardson
T³umaczenie: Andrzej Gra¿yñski, Rados³aw Meryk
ISBN: 83-246-0768-4
Tytu³ orygina³u:

Ruby Cookbook

Format: B5, stron: 888

background image

5

Spis tre

ļci

Wprowadzenie .............................................................................................................. 17

1.

Ĥaħcuchy ........................................................................................................................29

1.1. Budowanie äaþcucha z czöĈci

32

1.2. Zastöpowanie zmiennych w tworzonym äaþcuchu

34

1.3. Zastöpowanie zmiennych w istniejñcym äaþcuchu

35

1.4. Odwracanie kolejnoĈci säów lub znaków w äaþcuchu

37

1.5. Reprezentowanie znaków niedrukowalnych

39

1.6. Konwersja miödzy znakami a kodami

41

1.7. Konwersja miödzy äaþcuchami a symbolami

42

1.8. Przetwarzanie kolejnych znaków äaþcucha

43

1.9. Przetwarzanie poszczególnych säów äaþcucha

45

1.10. Zmiana wielkoĈci liter w äaþcuchu

47

1.11. Zarzñdzanie biaäymi znakami

48

1.12. Czy moĔna potraktowaè dany obiekt jak äaþcuch?

49

1.13. Wyodröbnianie czöĈci äaþcucha

51

1.14. Obsäuga miödzynarodowego kodowania

52

1.15. Zawijanie wierszy tekstu

53

1.16. Generowanie nastöpnika äaþcucha

55

1.17. Dopasowywanie äaþcuchów za pomoc

ñ wyraĔeþ regularnych

58

1.18. Zastöpowanie wielu wzorców w pojedynczym przebiegu

60

1.19. Weryfikacja poprawnoĈci adresów e-mailowych

61

1.20. Klasyfikacja tekstu za pomocñ analizatora bayesowskiego

64

2. Liczby .............................................................................................................................67

2.1. Przeksztaäcanie äaþcucha w liczbö

68

2.2. Porównywanie liczb zmiennopozycyjnych

70

2.3. Reprezentowanie liczb z dowolnñ dokäadnoĈciñ

73

background image

6

_

Spis tre

ļci

2.4. Reprezentowanie liczb wymiernych

76

2.5. Generowanie liczb pseudolosowych

77

2.6. Konwersje miödzy róĔnymi podstawami liczenia

79

2.7. Logarytmy

80

2.8. ćrednia, mediana i moda

83

2.9. Konwersja stopni na radiany i odwrotnie

85

2.10. MnoĔenie macierzy

87

2.11. Rozwiñzywanie ukäadu równaþ liniowych

91

2.12. Liczby zespolone

94

2.13. Symulowanie subklasingu klasy Fixnum

96

2.14. Arytmetyka liczb w zapisie rzymskim

100

2.15. Generowanie sekwencji liczb

105

2.16. Generowanie liczb pierwszych

107

2.17. Weryfikacja sumy kontrolnej w numerze karty kredytowej

111

3. Data i czas .....................................................................................................................113

3.1. Odczyt dzisiejszej daty

115

3.2. Dekodowanie daty, dokäadne i przybliĔone

119

3.3. Drukowanie dat

122

3.4. Iterowanie po datach

126

3.5. Arytmetyka dat

127

3.6. Obliczanie dystansu miödzy datami

129

3.7. Konwersja czasu miödzy strefami czasowymi

131

3.8. Czas letni

134

3.9. Konwersje miödzy obiektami Time i DateTime

135

3.10. Jaki to dzieþ tygodnia?

138

3.11. Obsäuga dat biznesowych

139

3.12. Periodyczne wykonywanie bloku kodu

140

3.13. Oczekiwanie przez zadany odcinek czasu

142

3.14. Przeterminowanie wykonania

145

4. Tablice .......................................................................................................................... 147

4.1. Iterowanie po elementach tablicy

149

4.2. Wymiana zawartoĈci bez uĔywania zmiennych pomocniczych

152

4.3. Eliminowanie zdublowanych wartoĈci

154

4.4. Odwracanie kolejnoĈci elementów w tablicy

155

4.5. Sortowanie tablicy

156

4.6. Sortowanie äaþcuchów bez rozróĔniania wielkoĈci liter

158

4.7. Zabezpieczanie tablic przed utratñ posortowania

159

background image

Spis tre

ļci

_

7

4.8. Sumowanie elementów tablicy

164

4.9. Sortowanie elementów tablicy wedäug czöstoĈci wystöpowania

165

4.10. Tasowanie tablicy

167

4.11. Znajdowanie N najmniejszych elementów tablicy

168

4.12. Tworzenie hasza za pomocñ iteratora inject

170

4.13. Ekstrahowanie wybranych elementów z tablicy

172

4.14. Operacje teoriomnogoĈciowe na tablicach

175

4.15. Partycjonowanie i klasyfikacja elementów zbioru

177

5. Hasze ........................................................................................................................... 183

5.1. Wykorzystywanie symboli jako kluczy

186

5.2. WartoĈci domyĈlne w haszach

187

5.3. Dodawanie elementów do hasza

189

5.4. Usuwanie elementów z hasza

191

5.5. Tablice i inne modyfikowalne obiekty w roli kluczy

193

5.6. Kojarzenie wielu wartoĈci z tym samym kluczem

195

5.7. Iterowanie po zawartoĈci hasza

196

5.8. Iterowanie po elementach hasza w kolejnoĈci ich wstawiania

200

5.9. Drukowanie hasza

201

5.10. Inwersja elementów hasza

203

5.11. Losowy wybór z listy zdarzeþ o róĔnych prawdopodobieþstwach

204

5.12. Tworzenie histogramu

207

5.13. Odwzorowanie zawartoĈci dwóch haszów

209

5.14. Ekstrakcja fragmentów zawartoĈci haszów

210

5.15. Przeszukiwanie hasza przy uĔyciu wyraĔeþ regularnych

211

6. Pliki i katalogi .............................................................................................................. 213

6.1. Czy taki plik istnieje?

216

6.2. Sprawdzanie uprawnieþ dostöpu do plików

218

6.3. Zmiana uprawnieþ dostöpu do plików

220

6.4. Sprawdzanie, kiedy plik byä ostatnio uĔywany

223

6.5. Przetwarzanie zawartoĈci katalogu

224

6.6. Odczytywanie zawartoĈci pliku

227

6.7. Zapis do pliku

230

6.8. Zapis do pliku tymczasowego

232

6.9. Losowy wybór wiersza z pliku

233

6.10. Porównywanie dwóch plików

234

6.11. Swobodne nawigowanie po „jednokrotnie odczytywalnych”

strumieniach wejĈciowych

238

background image

8

_

Spis tre

ļci

6.12. Wödrówka po drzewie katalogów

240

6.13. Szeregowanie dostöpu do pliku

242

6.14. Tworzenie wersjonowanych kopii pliku

245

6.15. ãaþcuchy udajñce pliki

248

6.16. Przekierowywanie standardowego wejĈcia i standardowego wyjĈcia

250

6.17. Przetwarzanie plików binarnych

252

6.18. Usuwanie pliku

255

6.19. Obcinanie pliku

257

6.20. Znajdowanie plików o okreĈlonej wäasnoĈci

258

6.21. Odczytywanie i zmiana bieĔñcego katalogu roboczego

260

7. Bloki kodowe i iteracje ...............................................................................................263

7.1. Tworzenie i wywoäywanie bloku kodowego

265

7.2. Tworzenie metod wykorzystujñcych bloki kodowe

267

7.3. Przypisywanie bloku kodowego do zmiennej

269

7.4. Bloki kodowe jako domkniöcia: odwoäania do zmiennych zewnötrznych

w treĈci bloku kodowego

272

7.5. Definiowanie iteratora dla struktury danych

273

7.6. Zmiana sposobu iterowania po strukturze danych

276

7.7. Nietypowe metody klasyfikujñce i kolekcjonujñce

278

7.8. Zatrzymywanie iteracji

279

7.9. Iterowanie równolegäe

281

7.10. Kod inicjujñcy i koþczñcy dla bloku kodowego

285

7.11. Tworzenie systemów luĒno powiñzanych przy uĔyciu odwoäaþ zwrotnych

287

8. Obiekty i klasy ............................................................................................................. 291

8.1. Zarzñdzanie danymi instancyjnymi

294

8.2. Zarzñdzanie danymi klasowymi

296

8.3. Weryfikacja funkcjonalnoĈci obiektu

299

8.4. Tworzenie klasy pochodnej

301

8.5. PrzeciñĔanie metod

303

8.6. Weryfikacja i modyfikowanie wartoĈci atrybutów

305

8.7. Definiowanie wirtualnych atrybutów

307

8.8. Delegowanie wywoäaþ metod do innego obiektu

308

8.9. Konwersja i koercja typów obiektów

311

8.10. Prezentowanie obiektu w postaci czytelnej dla czäowieka

315

8.11. Metody wywoäywane ze zmiennñ liczbñ argumentów

317

8.12. Symulowanie argumentów zawierajñcych säowa kluczowe

319

8.13. Wywoäywanie metod superklasy

321

background image

Spis tre

ļci

_

9

8.14. Definiowanie metod abstrakcyjnych

323

8.15. ZamraĔanie obiektów w celu ich ochrony przed modyfikacjñ

325

8.16. Tworzenie kopii obiektu

327

8.17. Deklarowanie staäych

330

8.18. Implementowanie metod klasowych i metod-singletonów

332

8.19. Kontrolowanie dostöpu — metody prywatne, publiczne i chronione

334

9. Modu

ĥy i przestrzenie nazw .......................................................................................339

9.1. Symulowanie wielokrotnego dziedziczenia za pomocñ moduäów-domieszek

339

9.2. Rozszerzanie wybranych obiektów za pomocñ moduäów

343

9.3. Rozszerzanie repertuaru metod klasowych za pomocñ moduäów

345

9.4. Moduä Enumerable — zaimplementuj jednñ metodö, dostaniesz 22 za darmo

346

9.5. Unikanie kolizji nazw dziöki ich kwalifikowaniu

348

9.6. Automatyczne äadowanie bibliotek na Ĕñdanie

350

9.7. Importowanie przestrzeni nazw

352

9.8. Inicjowanie zmiennych instancyjnych doäñczanego moduäu

353

9.9. Automatyczne inicjowanie moduäów-domieszek

354

10. Odzwierciedlenia i metaprogramowanie .................................................................357

10.1. Identyfikacja klasy obiektu i jej superklasy

358

10.2. Zestaw metod obiektu

359

10.3. Lista metod unikalnych dla obiektu

363

10.4. Uzyskiwanie referencji do metody

364

10.5. Poprawianie bäödów w „obcych” klasach

366

10.6. ćledzenie zmian dokonywanych w danej klasie

368

10.7. Weryfikacja atrybutów obiektu

370

10.8. Reagowanie na wywoäania niezdefiniowanych metod

372

10.9. Automatyczne inicjowanie zmiennych instancyjnych

375

10.10. Oszczödne kodowanie dziöki metaprogramowaniu

377

10.11. Metaprogramowanie z uĔyciem ewaluacji äaþcuchów

380

10.12. Ewaluacja kodu we wczeĈniejszym kontekĈcie

382

10.13. Anulowanie definicji metody

383

10.14. Aliasowanie metod

386

10.15. Programowanie zorientowane aspektowo

389

10.16. Wywoäania kontraktowane

391

11. XML i HTML .................................................................................................................395

11.1. Sprawdzanie poprawnoĈci dokumentu XML

396

11.2. Ekstrakcja informacji z drzewa dokumentu

398

background image

10

_

Spis tre

ļci

11.3. Ekstrakcja informacji w trakcie analizy dokumentu XML

400

11.4. Nawigowanie po dokumencie za pomocñ XPath

401

11.5. Parsowanie bäödnych dokumentów

404

11.6. Konwertowanie dokumentu XML na hasz

406

11.7. Walidacja dokumentu XML

409

11.8. Zastöpowanie encji XML

411

11.9. Tworzenie i modyfikowanie dokumentów XML

414

11.10. Kompresowanie biaäych znaków w dokumencie XML

417

11.11. Autodetekcja standardu kodowania znaków w dokumencie

418

11.12. Konwersja dokumentu miödzy róĔnymi standardami kodowania

419

11.13. Ekstrakcja wszystkich adresów URL z dokumentu HTML

420

11.14. Transformacja tekstu otwartego na format HTML

423

11.15. Konwertowanie Ĉciñgniötego z internetu dokumentu HTML na tekst

425

11.16. Prosty czytnik kanaäów

428

12. Formaty plików graficznych i innych ........................................................................ 433

12.1. Tworzenie miniaturek

433

12.2. Dodawanie tekstu do grafiki

436

12.3. Konwersja formatów plików graficznych

439

12.4. Tworzenie wykresów

441

12.5. Wprowadzanie graficznego kontekstu za pomocñ wykresów typu Sparkline

444

12.6. Silne algorytmy szyfrowania danych

447

12.7. Przetwarzanie danych rozdzielonych przecinkami

449

12.8. Przetwarzanie plików tekstowych nie w peäni zgodnych z formatem CSV

451

12.9. Generowanie i przetwarzanie arkuszy Excela

453

12.10. Kompresowanie i archiwizowanie plików za pomocñ narzödzi Gzip i Tar

455

12.11. Czytanie i zapisywanie plików ZIP

458

12.12. Czytanie i zapisywanie plików konfiguracyjnych

460

12.13. Generowanie plików PDF

461

12.14. Reprezentowanie danych za pomocñ plików muzycznych MIDI

465

13. Bazy danych i trwa

ĥoļë obiektów ............................................................................. 469

13.1. Serializacja danych za pomocñ biblioteki YAML

472

13.2. Serializacja danych z wykorzystaniem moduäu Marshal

475

13.3. Utrwalanie obiektów z wykorzystaniem biblioteki Madeleine

476

13.4. Indeksowanie niestrukturalnego tekstu z wykorzystaniem

biblioteki SimpleSearch

479

13.5. Indeksowanie tekstu o okreĈlonej strukturze z wykorzystaniem

biblioteki Ferret

481

background image

Spis tre

ļci

_

11

13.6. Wykorzystywanie baz danych Berkeley DB

484

13.7. Zarzñdzanie bazñ danych MySQL w systemie Unix

486

13.8. Zliczanie wierszy zwracanych przez zapytanie

487

13.9. BezpoĈrednia komunikacja z bazñ danych MySQL

489

13.10. BezpoĈrednia komunikacja z bazñ danych PostgreSQL

491

13.11. Mapowanie obiektowo-relacyjne z wykorzystaniem

biblioteki ActiveRecord

493

13.12. Mapowanie obiektowo-relacyjne z wykorzystaniem

biblioteki Og

497

13.13. Programowe tworzenie zapytaþ

501

13.14. Sprawdzanie poprawnoĈci danych z wykorzystaniem

biblioteki ActiveRecord

504

13.15. Zapobieganie atakom typu SQL Injection

507

13.16. Obsäuga transakcji z wykorzystaniem biblioteki ActiveRecord

510

13.17. Definiowanie haków dotyczñcych zdarzeþ zwiñzanych z tabelami

511

13.18. Oznaczanie tabel bazy danych z wykorzystaniem moduäów-domieszek

514

14. Us

ĥugi internetowe ..................................................................................................... 519

14.1. Pobieranie zawartoĈci strony WWW

520

14.2. Obsäuga Ĕñdaþ HTTPS

522

14.3. Dostosowywanie nagäówków Ĕñdaþ HTTP

524

14.4. Wykonywanie zapytaþ DNS

526

14.5. Wysyäanie poczty elektronicznej

528

14.6. Czytanie poczty z serwera IMAP

531

14.7. Czytanie poczty z wykorzystaniem protokoäu POP3

535

14.8. Implementacja klienta FTP

538

14.9. Implementacja klienta telnet

540

14.10. Implementacja klienta SSH

543

14.11. Kopiowanie plików do innego komputera

546

14.12. Implementacja klienta BitTorrent

547

14.13. Wysyäanie sygnaäu ping do zdalnego komputera

549

14.14. Implementacja wäasnego serwera internetowego

550

14.15. Przetwarzanie adresów URL

552

14.16. Pisanie skryptów CGI

555

14.17. Ustawianie plików cookie i innych nagäówków odpowiedzi HTTP

557

14.18. Obsäuga przesyäania plików na serwer z wykorzystaniem CGI

559

14.19. Uruchamianie serwletów WEBrick

562

14.20. Wäasny klient HTTP

567

background image

12

_

Spis tre

ļci

15. Projektowanie aplikacji internetowych: Ruby on Rails ............................................ 571

15.1. Prosta aplikacja Rails wyĈwietlajñca informacje o systemie

573

15.2. Przekazywanie danych ze sterownika do widoku

576

15.3. Tworzenie ukäadu nagäówka i stopki

578

15.4. Przekierowania do innych lokalizacji

581

15.5. WyĈwietlanie szablonów za pomocñ metody render

582

15.6. Integracja baz danych z aplikacjami Rails

585

15.7. Reguäy pluralizacji

588

15.8. Tworzenie systemu logowania

590

15.9. Zapisywanie haseä uĔytkowników w bazie danych w postaci skrótów

594

15.10. Unieszkodliwianie kodu HTML i JavaScript przed wyĈwietlaniem

595

15.11. Ustawianie i odczytywanie informacji o sesji

596

15.12. Ustawianie i odczytywanie plików cookie

599

15.13. Wyodröbnianie kodu do moduäów pomocniczych

601

15.14. Rozdzielenie widoku na kilka czöĈci

602

15.15. Dodawanie efektów DHTML z wykorzystaniem

biblioteki script.aculo.us

605

15.16. Generowanie formularzy do modyfikowania obiektów modelu

607

15.17. Tworzenie formularzy Ajax

611

15.18. Udostöpnianie usäug sieciowych w witrynie WWW

614

15.19. Przesyäanie wiadomoĈci pocztowych za pomocñ aplikacji Rails

616

15.20. Automatyczne wysyäanie komunikatów o bäödach pocztñ elektronicznñ

618

15.21. Tworzenie dokumentacji witryny WWW

620

15.22. Testy moduäowe witryny WWW

621

15.23. Wykorzystywanie puäapek w aplikacjach internetowych

624

16. Us

ĥugi sieciowe i programowanie rozproszone .......................................................627

16.1. Wyszukiwanie ksiñĔek w serwisie Amazon

628

16.2. Wyszukiwanie zdjöè w serwisie Flickr

631

16.3. Jak napisaè klienta XML-RPC?

634

16.4. Jak napisaè klienta SOAP?

636

16.5. Jak napisaè serwer SOAP?

637

16.6. Wyszukiwanie w internecie z wykorzystaniem usäugi sieciowej

serwisu Google

638

16.7. Wykorzystanie pliku WSDL w celu uäatwienia wywoäaþ SOAP

640

16.8. PäatnoĈci kartami kredytowymi

642

16.9. Odczytywanie kosztów przesyäki w serwisie UPS lub FedEx

644

16.10. Wspóädzielenie haszów przez dowolnñ liczbö komputerów

645

16.11. Implementacja rozproszonej kolejki

649

background image

Spis tre

ļci

_

13

16.12. Tworzenie wspóädzielonej „tablicy ogäoszeþ”

650

16.13. Zabezpieczanie usäug DRb za pomocñ list kontroli dostöpu

653

16.14. Automatyczne wykrywanie usäug DRb z wykorzystaniem

biblioteki Rinda

654

16.15. Wykorzystanie obiektów poĈredniczñcych

656

16.16. Zapisywanie danych w rozproszonej pamiöci RAM z wykorzystaniem

systemu MemCached

659

16.17. Buforowanie kosztownych obliczeniowo wyników za pomocñ

systemu MemCached

661

16.18. Zdalnie sterowana „szafa grajñca”

664

17. Testowanie, debugowanie, optymalizacja i tworzenie dokumentacji ...................669

17.1. Uruchamianie kodu wyäñcznie w trybie debugowania

670

17.2. Generowanie wyjñtków

672

17.3. Obsäuga wyjñtków

673

17.4. Ponawianie próby wykonania kodu po wystñpieniu wyjñtku

676

17.5. Mechanizmy rejestrowania zdarzeþ w aplikacji

677

17.6. Tworzenie i interpretowanie stosu wywoäaþ

679

17.7. Jak pisaè testy moduäowe?

681

17.8. Uruchamianie testów moduäowych

684

17.9. Testowanie kodu korzystajñcego z zewnötrznych zasobów

686

17.10. Wykorzystanie puäapek do kontroli i modyfikacji stanu aplikacji

690

17.11. Tworzenie dokumentacji aplikacji

692

17.12. Profilowanie aplikacji

696

17.13. Pomiar wydajnoĈci alternatywnych rozwiñzaþ

699

17.14. Wykorzystywanie wielu narzödzi analitycznych jednoczeĈnie

701

17.15. Co wywoäuje tö metodö? Graficzny analizator wywoäaþ

702

18. Tworzenie pakietów oprogramowania i ich dystrybucja ........................................705

18.1. Wyszukiwanie bibliotek poprzez kierowanie zapytaþ

do repozytoriów gemów

706

18.2. Instalacja i korzystanie z gemów

709

18.3. Wymaganie okreĈlonej wersji gemu

711

18.4. Odinstalowywanie gemów

714

18.5. Czytanie dokumentacji zainstalowanych gemów

715

18.6. Tworzenie pakietów kodu w formacie gemów

717

18.7. Dystrybucja gemów

719

18.8. Instalacja i tworzenie samodzielnych pakietów z wykorzystaniem

skryptu setup.rb

722

background image

14

_

Spis tre

ļci

19. Automatyzacja zada

ħ z wykorzystaniem jýzyka Rake ............................................725

19.1. Automatyczne uruchamianie testów moduäowych

727

19.2. Automatyczne generowanie dokumentacji

729

19.3. Porzñdkowanie wygenerowanych plików

731

19.4. Automatyczne tworzenie gemów

733

19.5. Pobieranie informacji statystycznych dotyczñcych kodu

734

19.6. Publikowanie dokumentacji

737

19.7. Równolegäe uruchamianie wielu zadaþ

738

19.8. Uniwersalny plik Rakefile

740

20. Wielozadaniowo

ļë i wielowétkowoļë ......................................................................747

20.1. Uruchamianie procesu-demona w systemie Unix

748

20.2. Tworzenie usäug systemu Windows

751

20.3. Wykonywanie dwóch operacji jednoczeĈnie z wykorzystaniem wñtków

754

20.4. Synchronizacja dostöpu do obiektu

756

20.5. Niszczenie wñtków

758

20.6. Równolegäe uruchamianie bloku kodu dla wielu obiektów

760

20.7. Ograniczanie liczby wñtków z wykorzystaniem ich puli

763

20.8. Sterowanie zewnötrznym procesem za pomocñ metody popen

766

20.9. Przechwytywanie strumienia wyjĈciowego i informacji o bäödach

z polecenia powäoki w systemie Unix

767

20.10. Zarzñdzanie procesami w innym komputerze

768

20.11. Unikanie zakleszczeþ

770

21. Interfejs u

żytkownika .................................................................................................773

21.1. Pobieranie danych wejĈciowych wiersz po wierszu

774

21.2. Pobieranie danych wejĈciowych znak po znaku

776

21.3. Przetwarzanie argumentów wiersza polecenia

778

21.4. Sprawdzenie, czy program dziaäa w trybie interaktywnym

781

21.5. Konfiguracja i porzñdkowanie po programie wykorzystujñcym

bibliotekö Curses

782

21.6. Czyszczenie ekranu

784

21.7. OkreĈlenie rozmiaru terminala

785

21.8. Zmiana koloru tekstu

787

21.9. Odczytywanie haseä

790

21.10. Edycja danych wejĈciowych z wykorzystaniem biblioteki Readline

791

21.11. Sterowanie migotaniem diod na klawiaturze

792

21.12. Tworzenie aplikacji GUI z wykorzystaniem biblioteki Tk

795

21.13. Tworzenie aplikacji GUI z wykorzystaniem biblioteki wxRuby

798

background image

Spis tre

ļci

_

15

21.14. Tworzenie aplikacji GUI z wykorzystaniem biblioteki Ruby/GTK

802

21.15. Tworzenie aplikacji Mac OS X z wykorzystaniem biblioteki RubyCocoa

805

21.16. Wykorzystanie AppleScript do pobierania danych wejĈciowych

od uĔytkownika

812

22. Rozszerzenia j

ýzyka Ruby z wykorzystaniem innych jýzyków ...............................815

22.1. Pisanie rozszerzeþ w jözyku C dla jözyka Ruby

816

22.2. Korzystanie z bibliotek jözyka C z poziomu kodu Ruby

819

22.3. Wywoäywanie bibliotek jözyka C za pomocñ narzödzia SWIG

822

22.4. Kod w jözyku C wstawiany w kodzie Ruby

825

22.5. Korzystanie z bibliotek Javy za poĈrednictwem interpretera JRuby

827

23. Administrowanie systemem ...................................................................................... 831

23.1. Pisanie skryptów zarzñdzajñcych zewnötrznymi programami

832

23.2. Zarzñdzanie usäugami systemu Windows

833

23.3. Uruchamianie kodu w imieniu innego uĔytkownika

835

23.4. Okresowe uruchamianie zadaþ bez uĔywania mechanizmu cron lub at

836

23.5. Usuwanie plików, których nazwy speäniajñ kryteria okreĈlone

przez wyraĔenie regularne

838

23.6. Zmiana nazw grupy plików

840

23.7. Wyszukiwanie plików zdublowanych

842

23.8. Automatyczne wykonywanie kopii zapasowych

845

23.9. Ujednolicanie wäasnoĈci i uprawnieþ w katalogach uĔytkowników

846

23.10. Niszczenie wszystkich procesów wybranego uĔytkownika

849

Skorowidz ................................................................................................................... 853

background image

29

ROZDZIA

Ĥ 1.

Ĥaħcuchy

Ruby jest jözykiem przyjaznym programiĈcie. Przed programistami hoädujñcymi filozofii pro-
gramowania zorientowanego obiektowo odkryje on drugñ jego naturö; programiĈci stroniñcy
od obiektów nie powinni mieè natomiast wiökszych trudnoĈci, bowiem — w odróĔnieniu od
wielu innych jözyków — w jözyku Ruby stosuje siö zwiözäe i konsekwentne nazewnictwo me-
tod, które generalnie zachowujñ siö tak, jak (intuicyjnie) moĔna by tego oczekiwaè.

ãaþcuchy znakomicie nadajñ siö na obszar „pierwszego kontaktu” z jözykiem Ruby: sñ uĔy-
teczne, äatwo siö je tworzy i wykorzystuje, wystöpujñ w wiökszoĈci jözyków, a wiöc säuĔyè
mogñ zarówno jako materiaä porównawczy, jak i okazja do przedstawienia koncepcyjnych
nowoĈci jözyka Ruby w rodzaju duck typing (receptura 1.12), otwartych klas (receptura 1.10),
symboli (receptura 1.7), a nawet gemów (receptura 1.20).

Omawiane koncepcje ilustrujemy konsekwentnie interaktywnymi sesjami jözyka Ruby. W

Ĉro-

dowisku Uniksa i Mac OS X säuĔy do tego program

irb

, uruchamiany z wiersza poleceþ. UĔyt-

kownicy Windows mogñ takĔe wykorzystywaè w tym celu program

fxri

, dostöpny za pomo-

cñ menu Start (po zainstalowaniu Ĉrodowiska Ruby za pomocñ pakietu „one-click installer”,
który pobraè moĔna spod adresu http:/rubyforge.org/projects/rubyinstaller). Program

irb

jest rów-

nieĔ dostöpny w Windows. Wspomniane programy tworzñ po uruchomieniu interaktywnñ po-
wäokö jözyka Ruby, pod kontrolñ której wykonywaè moĔna fragmenty przykäadowego kodu.

ãaþcuchy jözyka Ruby podobne sñ do äaþcuchów w innych „dynamicznych” jözykach — Perlu,
Pythonie czy PHP. Nie róĔniñ siö zbytnio od äaþcuchów znanych z jözyków C i Java. Sñ dyna-
miczne, elastyczne i modyfikowalne.

Rozpocznijmy wiöc naszñ sesjö, wpisujñc do wiersza poleceþ powäoki nastöpujñcy tekst:

string = "To jest napis"

Spowoduje to wyĈwietlenie rezultatu wykonania polecenia:

=> "To jest napis"

Polecenie to powoduje utworzenie äaþcucha

"To jest napis"

i przypisanie go zmiennej o na-

zwie

string

. ãaþcuch ten staje siö wiöc wartoĈciñ zmiennej i jednoczeĈnie wartoĈciñ caäego wy-

raĔenia, co uwidocznione zostaje w postaci wyniku wypisywanego (po strzaäce

=>

) w ramach

interaktywnej sesji. W treĈci ksiñĔki zapisywaè bödziemy ten rodzaj interakcji w postaci

string "To jest napis" => "To jest napis"

W jözyku Ruby wszystko, co moĔna przypisaè zmiennej, jest obiektem. W powyĔszym przy-
käadzie zmienna

string

wskazuje na obiekt klasy

String

. Klasa ta definiuje ponad sto metod

background image

30

_

Rozdzia

ĥ 1. Ĥaħcuchy

— nazwanych fragmentów kodu säuĔñcych do wykonywania rozmaitych operacji na äaþcu-
chach. Wiele z tych metod wykorzystywaè bödziemy w naszej ksiñĔce, takĔe w niniejszym
rozdziale. Jedna z tych metod —

String#length

— zwraca rozmiar äaþcucha, czyli liczbö skäa-

dajñcych siö na niego bajtów:

string.length => 13

W wielu jözykach programowania wymagana (lub dopuszczalna) jest para nawiasów po na-
zwie wywoäywanej metody:

string.length() => 13

W jözyku Ruby nawiasy te sñ niemal zawsze nieobowiñzkowe, szczególnie w sytuacji, gdy
do wywoäywanej metody nie sñ przekazywane Ĕadne parametry (jak w powyĔszym przykäa-
dzie). Gdy parametry takie sñ przekazywane, uĔycie nawiasów moĔe uczyniè caäñ konstruk-
cjö bardziej czytelnñ:

string.count 's' => 2 # s wyst

Ăpuje dwukrotnie

string.count('s') => 2

WartoĈè zwracana przez metodö sama z siebie jest obiektem. W przypadku metody

String#

length

, wywoäywanej w powyĔszym przykäadzie, obiekt ten jest liczbñ

20

, czyli egzempla-

rzem (instancjñ) klasy

Fixnum

. PoniewaĔ jest obiektem, moĔna na jego rzecz takĔe wywoäy-

waè metody:

string.length.next => 14

WeĒmy teraz pod uwagö bardziej ciekawy przypadek — äaþcuch zawierajñcy znaki spoza
kodu ASCII. PoniĔszy äaþcuch reprezentuje francuskie zdanie „il était une fois” zakodowane
wedäug UTF-8

1

:

french_string = "il \xc3\xa9tait une fois" # => "il \303\251tait une fois"

Wiele jözyków programowania, miödzy innymi Java, traktuje äaþcuchy jako ciñgi znaków.
W jözyku Ruby äaþcuch postrzegany jest jako ciñg bajtów. PoniewaĔ powyĔszy äaþcuch za-
wiera 14 liter i 3 spacje, moĔna by domniemywaè, Ĕe jego däugoĈè wynosi 17; poniewaĔ jedna
z liter jest znakiem dwubajtowym, äaþcuch skäada siö z 18, nie 17 bajtów:

french_string.length # => 18

Do róĔnorodnego kodowania znaków powrócimy w recepturach 1.14 i 11.12; specyfikñ äaþ-
cuchów zawierajñcych znaki wielobajtowe zajmiemy siö takĔe w recepturze 1.8.

Znaki specjalne (tak jak binarne dane w äaþcuchu

french_string

) mogñ byè reprezentowane

za pomocñ tzw. sekwencji unikowych (escaping). Ruby udostöpnia kilka rodzajów takich se-
kwencji w zaleĔnoĈci od tego, w jaki sposób tworzony jest dany äaþcuch. JeĔeli mianowicie

äaþcuch ujöty jest w cudzysäów (

" ... "

), moĔna w nim kodowaè zarówno znaki binarne (jak

w przykäadowym äaþcuchu francuskim), jak i znak nowego wiersza

\n

znany z wielu innych

jözyków:

puts "Ten

ĪaĬcuch\nzawiera znak nowego wiersza"

# Ten

ĪaĬcuch

# zawiera znak nowego wiersza

W äaþcuchu zamkniötym znakami apostrofu (

' ... '

) jedynym dopuszczalnym znakiem

specjalnym jest odwrotny ukoĈnik

\

(backslash), umoĔliwiajñcy reprezentowanie pojedyncze-

go znaku specjalnego; para

\\

reprezentuje pojedynczy backslash.

1

"\xc3\xa9"

jest zapisem w jözyku Ruby unikodowego znaku é w reprezentacji UTF-8.

background image

1.1. Budowanie

ĥaħcucha z czýļci

_

31

puts 'Ten

ĪaĬcuch wbrew pozorom \nniezawiera znaku nowego wiersza'

# Ten

ĪaĬcuch wbrew pozorom \nniezawiera znaku nowego wiersza

puts 'To jest odwrotny uko

Łnik: \\'

# To jest odwrotny uko

Łnik: \

Do kwestii tej powrócimy w recepturze 1.5, a w recepturach 1.2 i 1.3 zajmiemy siö bardziej
spektakularnymi moĔliwoĈciami äaþcuchów ograniczonych apostrofami.

Oto inna uĔyteczna moĔliwoĈè inicjowania äaþcucha, nazywana w jözyku Ruby here documents:

long_string = <<EOF
To jest d

Īugi ĪaĬcuch

Sk

Īadajîcy siĂ z kilku akapitów

EOF
# => "To jest d

Īugi ĪaĬcuch\nSkĪadajîcy siĂ z kilku akapitów\n"

puts long_string
# To jest d

Īugi ĪaĬcuch

# Sk

Īadajîcy siĂ z kilku akapitów

Podobnie jak w przypadku wiökszoĈci wbudowanych klas jözyka Ruby, takĔe w przypadku

äaþcuchów moĔna kodowaè tö samñ funkcjonalnoĈè na wiele róĔnych sposobów („idiomów”)
i programista moĔe dokonaè wyboru tego, który odpowiada mu najbardziej. WeĒmy jako przy-
käad ekstrakcjö podäaþcucha z däugiego äaþcucha: programiĈci preferujñcy podejĈcie obiekto-
we zapewne uĔyliby do tego celu metody

String#slice

:

string # "To jest napis"
string.slice(3,4) # "jest"

ProgramiĈci wywodzñcy swe nawyki z jözyka C skäonni sñ jednak do traktowania äaþcuchów
jako tablic bajtów — im takĔe Ruby wychodzi naprzeciw, umoĔliwiajñc ekstrakcjö poszczegól-
nych bajtów äaþcucha:

string[3].chr + string[4].chr + string[5].chr + string[6].chr
# => "jest"

Podobnie proste zadanie majñ programiĈci przywykli do Pythona:

string[3, 4] # => "jest"

W przeciwieþstwie do wielu innych jözyków programowania, äaþcuchy Ruby sñ modyfiko-
walne (mutable) — moĔna je zmieniaè juĔ po zadeklarowaniu. Oto wynik dziaäania dwóch me-
tod:

String#upcase

i

String#upcase!

— zwróè uwagö na istotnñ róĔnicö miödzy nimi:

string.upcase # => "TO JEST NAPIS"
string # => "To jest napis"

string.upcase! # => "TO JEST NAPIS"
string # => "TO JEST NAPIS"

Przy okazji widoczna staje siö jedna z konwencji skäadniowych jözyka Ruby: metody „nie-
bezpieczne” — czyli gäównie te modyfikujñce obiekty „w miejscu” — opatrywane sñ nazwami
koþczñcymi siö wykrzyknikiem. Inna konwencja syntaktyczna zwiñzana jest z predykatami,
czyli metodami zwracajñcymi wartoĈè

true

albo

false

— ich nazwy koþczñ siö znakiem za-

pytania.

string.empty? # => false
string.include? "To" # => true

UĔycie znaków przestankowych charakterystycznych dla jözyka potocznego, w celu uczynie-
nia kodu bardziej czytelnym dla programisty, jest odzwierciedleniem filozofii twórcy jözyka
Ruby, Yukihiro „Matza” Matsumoto, zgodnie z którñ to filozofiñ Ruby powinien byè czytelny

background image

32

_

Rozdzia

ĥ 1. Ĥaħcuchy

przede wszystkim dla ludzi, zaĈ moĔliwoĈè wykonywania zapisanych w nim programów przez
interpreter jest kwestiñ wtórnñ.

Interaktywne sesje jözyka Ruby sñ niezastñpionym narzödziem umoĔliwiajñcym poznawanie
metod jözyka i praktyczne eksperymentowanie z nimi. Ponownie zachöcamy do osobistego
sprawdzania prezentowanego kodu w ramach sesji programu

irb

lub

fxri

, a z biegiem cza-

su tworzenia i testowania takĔe wäasnych przykäadów.

Dodatkowe informacje na temat äaþcuchów Ruby moĔna uzyskaè z nastöpujñcych Ēródeä:

x

Informacjö o dowolnej wbudowanej metodzie jözyka moĔna otrzymaè wprost w oknie
programu

fxri

, wybierajñc odnoĈnñ pozycjö w lewym panelu. W programie

irb

moĔna

uĔyè w tym celu polecenia

ri

— na przykäad informacjö o metodzie

String#upcase!

uzy-

skamy za pomocñ polecenia

ri String#upcase!

x

why the lucky stiff napisaä wspaniaäe wprowadzenie do instalacji jözyka Ruby oraz wyko-
rzystywania poleceþ

ir

i

irb

. Jest ono dostöpne pod adresem http://poignantguide.net/ruby/

expansion-pak-1.html.

x

Filozofiö projektowñ jözyka Ruby przedstawia jego autor, Yukihiro „Matz” Matsumoto,
w wywiadzie dostöpnym pod adresem http://www.artima.com/intv/ruby.html.

1.1. Budowanie

ĥaħcucha z czýļci

Problem

Iterujñc po strukturze danych, naleĔy zbudowaè äaþcuch reprezentujñcy kolejne kroki tej
iteracji.

Rozwi

ézanie

Istniejñ dwa efektywne rozwiñzania tego problemu. W najprostszym przypadku rozpoczy-
namy od äaþcucha pustego, sukcesywnie doäñczajñc do niego podäaþcuchy za pomocñ ope-
ratora

<<

:

hash = { "key1" => "val1", "key2" => "val2" }
string = ""
hash.each { |k,v| string << "#{k} is #{v}\n" }
puts string
# key1 is val1
# key2 is val2

PoniĔsza odmiana tego prostego rozwiñzania jest nieco efektywniejsza, chociaĔ mniej czytelna:

string = ""
hash.each { |k,v| string << k << " is " << v << "\n" }

JeĈli wspomnianñ strukturñ danych jest tablica, bñdĒ teĔ struktura ta daje siö äatwo przetrans-
formowaè na tablicö, zwykle bardziej efektywne rozwiñzanie moĔna uzyskaè za pomocñ me-
tody

Array#join

:

puts hash.keys.join("\n") + "\n"
# key1
# key2

background image

1.1. Budowanie

ĥaħcucha z czýļci

_

33

Dyskusja

W jözykach takich jak Pyton czy Java sukcesywne doäñczanie podäaþcuchów do pustego po-
czñtkowo äaþcucha jest rozwiñzaniem bardzo nieefektywnym. ãaþcuchy w tych jözykach sñ
niemodyfikowalne (immutable), a wiöc kaĔdorazowe doäñczenie podäaþcucha wiñĔe siö ze
stworzeniem nowego obiektu. Doäñczanie serii podäaþcuchów oznacza wiöc tworzenie duĔej
liczby obiektów poĈrednich, z których kaĔdy stanowi jedynie „pomost” do nastöpnego etapu.
W praktyce przekäada siö to na marnotrawstwo czasu i pamiöci.

W tych warunkach rozwiñzaniem najbardziej efektywnym byäoby zapisanie poszczególnych
podäaþcuchów w tablicy (lub innej modyfikowalnej strukturze) zdolnej do dynamicznego roz-
szerzania siö. Gdy wszystkie podäaþcuchy zostanñ juĔ zmagazynowane we wspomnianej ta-
blicy, moĔna poäñczyè je w pojedynczy äaþcuch za pomocñ operatora stanowiñcego odpowied-
nik metody

Array#join

jözyka Ruby. W jözyku Java zadanie to speänia klasa

StringBuffer

.

Unikamy w ten sposób tworzenia wspomnianych obiektów poĈrednich.

W jözyku Ruby sprawa ma siö zgoäa inaczej, bo äaþcuchy sñ tu modyfikowalne, podobnie jak
tablice. Mogñ wiöc byè rozszerzane w miarö potrzeby, bez zbytniego obciñĔania pamiöci lub
procesora. W najszybszym wariancie rozwiñzania moĔemy wiöc w ogóle zapomnieè o tabli-
cy poĈredniczñcej i umieszczaè poszczególne podäaþcuchy bezpoĈrednio wewnñtrz äaþcucha
docelowego. Niekiedy skorzystanie z

Array#join

okazuje siö szybsze, lecz zwykle niewiele

szybsze, ponadto konstrukcja oparta na

<<

jest generalnie äatwiejsza do zrozumienia.

W sytuacji, gdy efektywnoĈè jest czynnikiem krytycznym, nie naleĔy tworzyè nowych äaþcu-
chów, jeĔeli moĔliwe jest doäñczanie podäaþcuchów do äaþcucha istniejñcego. Konstrukcje
w rodzaju

str << 'a' + 'b'

czy

str << "#{var1} #{var2}"

powodujñ tworzenie nowych äaþcuchów, które natychmiast „podäñczane” sñ do wiökszego

äaþcucha — a tego wäaĈnie chcielibyĈmy unikaè. UmoĔliwia nam to konstrukcja

str << var1 << ' ' << var2

Z drugiej jednak strony, nie powinno siö modyfikowaè äaþcuchów nietworzonych przez sie-
bie i wzglödy bezpieczeþstwa przemawiajñ za tworzeniem nowego äaþcucha. Gdy definiujesz
metodö otrzymujñcñ äaþcuch jako parametr, metoda ta nie powinna modyfikowaè owego äaþ-
cucha przez doäñczanie podäaþcuchów na jego koþcu — chyba Ĕe wäaĈnie to jest celem meto-
dy (której nazwa powinna tym samym koþczyè siö wykrzyknikiem, dla zwrócenia szczegól-
nej uwagi osoby studiujñcej kod programu).

Przy okazji waĔna uwaga: dziaäanie metody

Array#join

nie jest dokäadnie równowaĔne do-

äñczaniu kolejnych podäaþcuchów do äaþcucha.

Array#join

akceptuje separator, który wsta-

wiany jest miödzy kaĔde dwa sñsiednie elementy tablicy; w przeciwieþstwie do sukcesywne-
go doäñczania podäaþcuchów, separator ten nie jest umieszczany po ostatnim elemencie. RóĔnicö
tö ilustruje poniĔszy przykäad:

data = ['1', '2', '3']
s = ''
data.each { |x| s << x << ' oraz '}
s # => "1 oraz 2 oraz 3 oraz "

data.join(' oraz ') # => "1 oraz 2 oraz 3"

background image

34

_

Rozdzia

ĥ 1. Ĥaħcuchy

Aby zasymulowaè dziaäanie

Array#join

za pomocñ iteracji, moĔna wykorzystaè metodö

Enumerable#each_with_index

, opuszczajñc separator dla ostatniego indeksu. Da siö to jed-

nak zrobiè tylko wówczas, gdy liczba elementów objötych enumeracjñ znana jest a priori:

s = ""
data.each_with_index { |x, i| s << x; s << "|" if i < data.length-1 }
s # => "1|2|3"

1.2. Zast

ýpowanie zmiennych w tworzonym ĥaħcuchu

Problem

NaleĔy stworzyè äaþcuch zawierajñcy reprezentacjö zmiennej lub wyraĔenia jözyka Ruby.

Rozwi

ézanie

NaleĔy wewnñtrz äaþcucha zamknñè zmiennñ lub wyraĔenie w nawiasy klamrowe i poprze-
dziè tö konstrukcjö znakiem

#

(hash).

liczba = 5
"Liczba jest równa #{liczba}." # => "Liczba jest równa 5."
"Liczba jest równa #{5}." # => "Liczba jest równa 5."
"Liczba nast

Ăpna po #{liczba} równa jest #{liczba.next}."

# => "Liczba nast

Ăpna po 5 równa jest 6."

"Liczba poprzedzaj

îca #{liczba} równa jest #{liczba-1}."

# => "Liczba poprzedzaj

îca 5 równa jest 4."

"To jest ##{number}!" # => "To jest #5!"

Dyskusja

ãaþcuch ujöty w cudzysäów (

" ... "

) jest przez interpreter skanowany pod kñtem obecnoĈci

specjalnych kodów substytucyjnych. Jednym z najbardziej elementarnych i najczöĈciej uĔywa-
nych kodów tego typu jest znak

\n

, zastöpowany znakiem nowego wiersza.

OczywiĈcie istniejñ bardziej skomplikowane kody substytucyjne. W szczególnoĈci dowolny
tekst zamkniöty w nawiasy klamrowe poprzedzone znakiem

#

(czyli konstrukcja

#{tekst}

)

interpretowany jest jako wyraĔenie jözyka Ruby, a caäa konstrukcja zastöpowana jest w äaþ-
cuchu wartoĈciñ tego wyraĔenia. JeĔeli wartoĈè ta nie jest äaþcuchem, Ruby dokonuje jej kon-
wersji na äaþcuch za pomocñ metody

to_s

. Proces ten nosi nazwö interpolacji.

Tak powstaäy äaþcuch staje siö nieodróĔnialny od äaþcucha, w którym interpolacji nie zasto-
sowano:

"#{liczba}" == '5' # => true

Za pomocñ interpolacji moĔna umieszczaè w äaþcuchu nawet spore porcje tekstu. Przypad-
kiem ekstremalnym jest definiowanie klasy wewnñtrz äaþcucha i wykorzystanie podäaþcucha
stanowiñcego wynik wykonania okreĈlonej metody tej klasy. Mimo ograniczonej raczej uĔy-
tecznoĈci tego mechanizmu, warto go zapamiötaè jako dowód wspaniaäych moĔliwoĈci jözy-
ka Ruby.

%{Tutaj jest #{class InstantClass
def bar
"pewien tekst"
end
end

background image

1.3. Zast

ýpowanie zmiennych w istniejécym ĥaħcuchu

_

35

InstantClass.new.bar
}.}
# => "Tutaj jest pewien tekst."

Kod wykonywany w ramach interpolacji funkcjonuje dokäadnie tak samo, jak kaĔdy inny kod
Ruby w tej samej lokalizacji. Definiowana w powyĔszym przykäadzie klasa

InstantClass

nie

róĔni siö od innych klas i moĔe byè uĔywana takĔe na zewnñtrz äaþcucha.

Gdy w ramach interpolacji wywoäywana jest metoda powodujñca efekty uboczne, efekty te
widoczne sñ na zewnñtrz äaþcucha. W szczególnoĈci, jeĔeli efektem ubocznym jest nadanie war-
toĈci jakiejĈ zmiennej, zmienna ta zachowuje tö wartoĈè na zewnñtrz äaþcucha. Mimo iĔ nie po-
lecamy celowego polegania na tej wäasnoĈci, naleĔy koniecznie mieè ĈwiadomoĈè jej istnienia.

"Zmiennej x nadano warto

Łð #{x = 5; x += 1}." # => "Zmiennej x nadano wartoŁð 6."

x # => 6

JeĔeli chcielibyĈmy potraktowaè wystöpujñcñ w äaþcuchu sekwencjö

#{tekst}

w sposób lite-

ralny, nie jako polecenie interpolacji, wystarczy poprzedziè jñ znakiem odwrotnego ukoĈnika
(

\

) albo zamknñè äaþcuch znakami apostrofu zamiast cudzysäowu:

"\#{foo}" # => "\#{foo}"
'#{foo}' # => "\#{foo}"

Alternatywnym dla

%{}

kodem substytucyjnym jest konstrukcja here document. Pozwala ona

na zdefiniowanie wielowierszowego äaþcucha, którego ogranicznikiem jest wiersz o wyróĔ-
nionej postaci.

name = "Mr. Lorum"
email = <<END
Szanowny #{name},

Niestety, nie mo

šemy pozytywnie rozpatrzyð PaĬskiej reklamacji w zwiîzku

z oszacowaniem szkody, gdy

š jesteŁmy piekarniî, nie firmî ubezpieczeniowî.

Podpisano,
Bu

Īa, Rogal i Precel

Piekarze Jej Królewskiej Wysoko

Łci

END

Jözyk Ruby pozostawia programiĈcie duĔñ swobodö w zakresie wyboru postaci wiersza ogra-
niczajñcego:

<<koniec_wiersza
Pewien poeta z Afryki
Pisa

Ī dwuwierszowe limeryki

koniec_wiersza
# => "Pewien poeta z Afryki\nPisa

Ī dwuwierszowe limeryki\n"

Patrz tak

że

x

Za pomocñ techniki opisanej w recepturze 1.3 moĔna definiowaè äaþcuchy i obiekty sza-
blonowe, umoĔliwiajñce „odroczonñ” interpolacjö.

1.3. Zast

ýpowanie zmiennych w istniejécym ĥaħcuchu

Problem

NaleĔy utworzyè äaþcuch umoĔliwiajñcy interpolacjö wyraĔenia jözyka Ruby, jednakĔe bez
wykonywania tej interpolacji — ta wykonana zostanie póĒniej, prawdopodobnie wtedy, gdy
bödñ znane wartoĈci zastöpowanych wyraĔeþ.

background image

36

_

Rozdzia

ĥ 1. Ĥaħcuchy

Rozwi

ézanie

Problem moĔna rozwiñzaè za pomocñ dwojakiego rodzaju Ĉrodków: äaþcuchów typu

printf

oraz szablonów ERB.

Ruby zapewnia wsparcie dla znanych z C i Pythona äaþcuchów formatujñcych typu

printf

.

Kody substytucyjne w ramach tych äaþcuchów majñ postaè dyrektyw rozpoczynajñcych siö
od znaku

%

(modulo):

template = 'Oceania zawsze by

Īa w stanie wojny z %s.'

template % 'Eurazj

î'

# => "Oceania zawsze by

Īa w stanie wojny z Eurazjî."

template % 'Antarktyd

î'

# => "Oceania zawsze by

Īa w stanie wojny z Antarktydî."

'Z dwoma miejscami dziesi

Ătnymi: %.2f' % Math::PI

# => " Z dwoma miejscami dziesi

Ătnymi: 3.14"

'Dope

Īnione zerami: %.5d' % Math::PI # => "DopeĪnione zerami: 00003"

Szablony ERB przypominajñ swñ postaciñ kod w jözyku JSP lub PHP. Zasadniczo szablon
ERB traktowany jest jako „normalny” äaþcuch, jednak pewne sekwencje sterujñce traktowane
sñ jako kod w jözyku Ruby lub aktualne wartoĈci wyraĔeþ:

template = ERB.new %q{Pyszne <%= food %>!}
food = "kie

Ībaski"

template.result(binding) # => "Pyszne kie

Ībaski!"

food = "mas

Īo orzechowe"

template.result(binding) # => "Pyszne mas

Īo orzechowe!"

Poza sesjñ

irb

moĔna pominñè wywoäania metody

Kernel#binding

:

puts template.result
# Pyszne mas

Īo orzechowe!

Szablony ERB wykorzystywane sñ wewnötrznie przez widoki Rails i äatwo moĔna rozpoznaè
je w plikach

.rhtml

.

Dyskusja

W szablonach ERB moĔna odwoäywaè siö do zmiennych (jak

food

w powyĔszym przykäa-

dzie), zanim zmienne te zostanñ zdefiniowane. Wskutek wywoäania metody

ERB#result

lub

ERB#run

szablon jest wartoĈciowany zgodnie z bieĔñcymi wartoĈciami tych zmiennych.

Podobnie jak kod w jözyku JSP i PHP, szablony ERB mogñ zawieraè pötle i rozgaäözienia wa-
runkowe. Oto przykäad rozbudowanego szablonu ERB:

template = %q{
<% if problems.empty? %>
Wygl

îda na to, še w kodzie nie ma bĪĂdów!

<% else %>
W kodzie kryj

î siĂ nastepujîce potencjalne problemy:

<% problems.each do |problem, line| %>
* <%= problem %> w wierszu <%= line %>
<% end %>
<% end %>}.gsub(/^\s+/, '')
template = ERB.new(template, nil, '<>')

problems = [["U

šyj is_a? zamiast duck typing", 23],

["eval() jest potencjalnie niebezpieczne", 44]]
template.run(binding)

background image

1.4. Odwracanie kolejno

ļci sĥów lub znaków w ĥaħcuchu

_

37

# W kodzie kryj

î siĂ nastepujîce potencjalne problemy:

# * U

šyj is_a? zamiast duck typing w wierszu 23

# * eval() jest potencjalnie niebezpieczne w wierszu 44

problems = []
template.run(binding)
# Wygl

îda na to, še w kodzie nie ma bĪĂdów!

ERB jest wyrafinowanym mechanizmem, jednak ani szablony ERB, ani äaþcuchy typu

printf

nie przypominajñ w niczym prostych podstawieþ prezentowanych w recepturze 1.2. Podsta-
wienia te nie sñ aktywowane, jeĈli äaþcuch ujöty jest w apostrofy (

' ... '

) zamiast w cudzy-

säów (

" ... "

). MoĔna wykorzystaè ten fakt do zbudowania szablonu zawierajñcego meto-

eval

:

class String
def substitute(binding=TOPLEVEL_BINDING)
eval(%{"#{self}"}, binding)
end
end

template = %q{Pyszne #{food}!} # => "Pyszne \#{food}!"

food = 'kie

Ībaski'

template.substitute(binding) # => "Pyszne kie

Ībaski!"

food = 'mas

Īo orzechowe'

template.substitute(binding) # => "Pyszne mas

Īo orzechowe!"

NaleĔy zachowaè szczególnñ ostroĔnoĈè, uĔywajñc metody

eval

, bowiem potencjalnie stwarza

ona moĔliwoĈè wykonania dowolnego kodu, z czego skwapliwie skorzystaè moĔe ewentual-
ny wäamywacz. Nie zdarzy siö to jednak w poniĔszym przykäadzie, jako Ĕe dowolna wartoĈè
zmiennej

food

wstawiona zostaje do äaþcucha jeszcze przed jego interpolacjñ:

food = '#{system("dir")}'
puts template.substitute(binding)
# Pyszne #{system("dir")}!

Patrz tak

że

x

PowyĔej prezentowaliĈmy proste przykäady szablonów ERB; przykäady bardziej skom-
plikowane znaleĒè moĔna w dokumentacji klas ERB pod adresem http://www.ruby-doc.org/
stdlib/libdoc/erb/rdoc/classes/ERB.html
.

x

Receptura 1.2, „Zastöpowanie zmiennych w tworzonym äaþcuchu”.

x

Receptura 10.12, „Ewaluacja kodu we wczeĈniejszym kontekĈcie”, zawiera informacje na
temat obiektów

Binding

.

1.4. Odwracanie kolejno

ļci sĥów lub znaków

w

ĥaħcuchu

Problem

Znaki lub säowa wystöpujñ w äaþcuchu w niewäaĈciwej kolejnoĈci.

background image

38

_

Rozdzia

ĥ 1. Ĥaħcuchy

Rozwi

ézanie

Do stworzenia nowego äaþcucha, zawierajñcego znaki äaþcucha oryginalnego w odwrotnej
kolejnoĈci, moĔna posäuĔyè siö metodñ

reverse

:

s = ".kapo an sipan tsej oT"
s.reverse # => "To jest napis na opak."
s # => ".kapo an sipan tsej oT"

s.reverse! # => "To jest napis na opak."
s # => "To jest napis na opak."

W celu odwrócenia kolejnoĈci säów w äaþcuchu, naleĔy podzieliè go najpierw na podäaþcuchy
oddzielone „biaäymi” znakami

2

(czyli poszczególne säowa), po czym wäñczyè listö tych säów

z powrotem do äaþcucha, w odwrotnej kolejnoĈci.

s = "kolei. po nie Wyrazy "
s.split(/(\s+)/).reverse!.join('') # => "Wyrazy nie po kolei."
s.split(/\b/).reverse!.join('') # => "Wyrazy nie po. kolei"

Dyskusja

Metoda

String#split

wykorzystuje wyraĔenie regularne w roli separatora. KaĔdorazowo

gdy element äaþcucha udaje siö dopasowaè do tego wyraĔenia, poprzedzajñca go czöĈè äaþcu-
cha wäñczona zostaje do listy, a metoda

split

przechodzi do skanowania dalszej czöĈci äaþ-

cucha. Efektem przeskalowania caäego äaþcucha jest lista podäaþcuchów znajdujñcych siö miö-
dzy wystñpieniami separatora. UĔyte w naszym przykäadzie wyraĔenie regularne

/(\s+)/

reprezentuje dowolny ciñg „biaäych” znaków, zatem metoda

split

dokonuje podziaäu äaþcu-

cha na poszczególne säowa (w potocznym rozumieniu).

WyraĔenie regularne

/b

reprezentuje granicö säowa; to nie to samo co „biaäy” znak, bowiem

granicö säowa moĔe takĔe wyznaczaè znak interpunkcyjny. Zwróè uwagö na konsekwencje
tej róĔnicy w powyĔszym przykäadzie.

PoniewaĔ wyraĔenie regularne

/(\s+)/

zawiera parö nawiasów, separatory takĔe sñ wäñcza-

ne do listy wynikowej. JeĔeli zatem zestawimy elementy tej listy w kolejnoĈci odwrotnej, se-
paratory oddzielajñce säowa bödñ juĔ obecne na swych miejscach. PoniĔszy przykäad ilustruje
róĔnicö miödzy zachowywaniem a ignorowaniem separatorów:

"Trzy banalne wyrazy".split(/\s+/) # => ["Trzy", "banalne", "wyrazy"]
"Trzy banalne wyrazy".split(/(\s+)/)
# => ["Trzy", " ", "banalne", " ", "wyrazy"]

Patrz tak

że

x

Receptura 1.9, „Przetwarzanie poszczególnych säów äaþcucha”, ilustruje kilka wyraĔeþ
regularnych wyznaczajñcych alternatywnñ definicjö „säowa”.

x

Receptura 1.11, „Zarzñdzanie biaäymi znakami”.

x

Receptura 1.17, „Dopasowywanie äaþcuchów za pomocñ wyraĔeþ regularnych”.

2

Pojöcie „biaäego znaku” wyjaĈnione jest w recepturze 1.11 — przyp. täum.

background image

1.5. Reprezentowanie znaków niedrukowalnych

_

39

1.5. Reprezentowanie znaków niedrukowalnych

Problem

NaleĔy stworzyè äaþcuch zawierajñcy znaki sterujñce, znaki w kodzie UTF-8 lub dowolne znaki
niedostöpne z klawiatury.

Rozwi

ézanie

Ruby udostöpnia kilka mechanizmów unikowych (escape) w celu reprezentowania znaków
niedrukowalnych. W äaþcuchach ujötych w cudzysäowy mechanizmy te umoĔliwiajñ repre-
zentowanie dowolnych znaków.

Dowolny znak moĔna zakodowaè w äaþcuchu, podajñc jego kod ósemkowy (octal) w formie

\ooo

lub kod szesnastkowy (hexadecimal) w formie

\xhh

.

octal = "\000\001\010\020"
octal.each_byte { |x| puts x }
# 0
# 1
# 8
# 16

hexadecimal = "\x00\x01\x10\x20"
hexadecimal.each_byte { |x| puts x }
# 0
# 1
# 16
# 32

W ten sposób umieszczaè moĔna w äaþcuchach znaki, których nie moĔna wprowadziè bez-
poĈrednio z klawiatury czy nawet wyĈwietliè na ekranie terminala. Uruchom poniĔszy pro-
gram, po czym otwórz wygenerowany plik smiley.html w przeglñdarce WWW.

open('smiley.html', 'wb') do |f|
f << '<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">'
f << "\xe2\x98\xBA"
end

Niektóre z niedrukowalnych znaków — te wykorzystywane najczöĈciej — posiadajñ specjal-
ne, skrócone kody unikowe:

"\a" == "\x07" # => true #ASCII 0x07 = BEL (D

şwiĂk systemowy)

"\b" == "\x08" # => true #ASCII 0x08 = BS (Cofanie)
"\e" == "\x1b" # => true #ASCII 0x1B = ESC (Escape)
"\f" == "\x0c" # => true #ASCII 0x0C = FF (Nowa strona)
"\n" == "\x0a" # => true #ASCII 0x0A = LF (Nowy wiersz)
"\r" == "\x0d" # => true #ASCII 0x0D = CR (Pocz

îtek wiersza)

"\t" == "\x09" # => true #ASCII 0x09 = HT (Tabulacja pozioma)
"\v" == "\x0b" # => true #ASCII 0x0B = VT (Tabulacja pionowa)

Dyskusja

W jözyku Ruby äaþcuchy sñ ciñgami bajtów. Nie ma znaczenia, czy bajty te sñ drukowalny-
mi znakami ASCII, niedrukowalnymi znakami binarnymi, czy teĔ mieszankñ obydwu tych
kategorii.

background image

40

_

Rozdzia

ĥ 1. Ĥaħcuchy

Znaki niedrukowalne wyĈwietlane sñ w jözyku Ruby w czytelnej dla czäowieka reprezentacji

\ooo

, gdzie

ooo

jest kodem znaku w reprezentacji ósemkowej; znaki posiadajñce reprezenta-

cjö mnemonicznñ w postaci

\<znak>

wyĈwietlane sñ jednak w tej wäaĈnie postaci. Znaki dru-

kowalne wyĈwietlane sñ zawsze w swej naturalnej postaci, nawet jeĔeli w tworzonym äaþcu-
chu zakodowane zostaäy w inny sposób.

"\x10\x11\xfe\xff" # => "\020\021\376\377"
"\x48\145\x6c\x6c\157\x0a" # => "Hello\n"

Znak odwrotnego ukoĈnika (

\

) reprezentowany jest przez parö takich ukoĈników (

\\

) — jest

to konieczne dla odróĔnienia literalnego uĔycia znaku

\

od mnemonicznej sekwencji uniko-

wej rozpoczynajñcej siö od takiego znaku. Przykäadowo, äaþcuch

"\\n"

skäada siö z dwóch

znaków: odwrotnego ukoĈnika i litery

n

.

"\\".size # => 1
"\\" == "\x5c" # => true
"\\n"[0] == ?\\ # => true
"\\n"[1] == ?n # => true
"\\n" =~ /\n/ # => nil

Ruby udostöpnia takĔe kilka wygodnych skrótów dla reprezentowania kombinacji klawiszy
w rodzaju Ctrl+C. Sekwencja

\C-<znak>

oznacza rezultat naciĈniöcia klawisza

<znak>

z jed-

noczesnym przytrzymaniem klawisza Ctrl; analogicznie sekwencja

\M-<znak>

oznacza rezul-

tat naciĈniöcia klawisza

<znak>

z jednoczesnym przytrzymaniem klawisza Alt (lub Meta):

"\C-a\C-b\C-c" # => "\001\002\003" # Ctrl+A Ctrl+B Ctrl+C
"\M-a\M-b\M-c" # => "\341\342\343" # Alt+A Alt+B Alt+C

Dowolna z opisywanych sekwencji moĔe pojawiè siö wszödzie tam, gdzie Ruby spodziewa
siö znaku. W szczególnoĈci moĔliwe jest wyĈwietlenie kodu znaku w postaci dziesiötnej — na-
leĔy w tym celu poprzedziè ów znak znakiem zapytania (

?

).

?\C-a # => 1
?\M-z # => 250

W podobny sposób moĔna uĔywaè rozmaitych reprezentacji znaków do specyfikowania za-
kresu znaków w wyraĔeniach regularnych:

contains_control_chars = /[\C-a-\C-^]/
'Foobar' =~ contains_control_chars # => nil
"Foo\C-zbar" =~ contains_control_chars # => 3

contains_upper_chars = /[\x80-\xff]/
'Foobar' =~ contains_upper_chars # => nil
"Foo\212bar" =~ contains_upper_chars # => 3

PoniĔsza aplikacja Ĉledzi („szpieguje”) naciĈniöcia klawiszy, reagujñc na niektóre kombinacje
specjalne:

def snoop_on_keylog(input)
input.each_byte do |b|
case b
when ?\C-c; puts 'Ctrl+C: zatrzyma

ð proces?'

when ?\C-z; puts 'Ctrl+Z: zawiesi

ð proces?'

when ?\n; puts 'Nowy wiersz.'
when ?\M-x; puts 'Alt+X: uruchomi

ð Emacs?'

end
end
end

snoop_on_keylog("ls -ltR\003emacsHello\012\370rot13-other-window\012\032")
# Ctrl+C: zatrzyma

ð proces?

background image

1.6. Konwersja mi

ýdzy znakami a kodami

_

41

# Nowy wiersz.
# Alt+X: uruchomi

ð Emacs?

# Nowy wiersz.
# Ctrl+Z: zawiesi

ð proces?

Sekwencje reprezentujñce znaki specjalne interpretowane sñ tylko w äaþcuchach ujötych
w cudzysäów oraz äaþcuchach tworzonych za pomocñ konstrukcji

%{}

lub

%Q{}

. Nie sñ one

interpretowane w äaþcuchach zamkniötych znakami apostrofu oraz äaþcuchach tworzonych
za pomocñ konstrukcji

%q{}

. Fakt ten moĔna wykorzystaè w przypadku potrzeby literalnego

wyĈwietlenia sekwencji reprezentujñcych znaki specjalne oraz w przypadku tworzenia äaþcu-
chów zawierajñcych duĔñ liczbö odwrotnych ukoĈników.

puts "foo\tbar"
# foo bar
puts %{foo\tbar}
# foo bar
puts %Q{foo\tbar}
# foo bar

puts 'foo\tbar'
# foo\tbar
puts %q{foo\tbar}
# foo\tbar

Nieinterpretowanie sekwencji reprezentujñcych znaki specjalne w äaþcuchach zamkniötych
znakami apostrofu moĔe wydaè siö dziwne — i niekiedy nieco käopotliwe — programistom
przyzwyczajonym do jözyka Python. JeĔeli äaþcuch ujöty w cudzysäów sam zawiera znaki
cudzysäowu (

"

), jedynym sposobem reprezentowania tychĔe jest uĔycie sekwencji unikowej

\"

,

\042

lub

\x22

. W przypadku äaþcuchów obfitujñcych w znaki cudzysäowu moĔe siö to

wydaè käopotliwe i najwygodniejszym rozwiñzaniem jest zamkniöcie äaþcucha apostrofami
— znaków cudzysäowu moĔna wówczas uĔywaè literalnie, tracimy jednak moĔliwoĈè inter-
pretowania znaków specjalnych. Na szczöĈcie istnieje zäoty Ĉrodek pozwalajñcy na pogodze-
nie tych sprzecznych racji: jeĈli chcesz zachowaè moĔliwoĈè interpretowania znaków specjal-
nych w äaþcuchach najeĔonych znakami cudzysäowu, uĔyj konstrukcji

%{}

.

1.6. Konwersja mi

ýdzy znakami a kodami

Problem

Chcemy otrzymaè kod ASCII danego znaku lub przetransformowaè kod ASCII znaku w sam
znak.

Rozwi

ézanie

Kod ASCII znaku moĔemy poznaè za pomocñ operatora

?

:

?a # => 97
?! # => 33
?\n # => 10

W podobny sposób moĔemy poznaè kod ASCII znaku wchodzñcego w skäad äaþcucha — na-
leĔy wówczas wyäuskaè ów znak z äaþcucha za pomocñ indeksu:

'a'[0] # => 97
'kakofonia'[1] # => 97

background image

42

_

Rozdzia

ĥ 1. Ĥaħcuchy

Konwersjö odwrotnñ — kodu ASCII na znak o tym kodzie — realizuje metoda

chr

, zwracajñ-

ca jednoznakowy äaþcuch:

97.chr # => "a"
33.chr # => "!"
10.chr # => "\n"
0.chr # => "\000"
256.chr # RangeError: 256 out of char range

Dyskusja

Mimo iĔ äaþcuch jako taki nie jest tablicñ, moĔe byè utoĔsamiany z tablicñ obiektów

Fixnum

— po jednym obiekcie dla kaĔdego bajta. Za pomocñ odpowiedniego indeksu moĔna wyäu-
skaè obiekt

Fixnum

reprezentujñcy konkretny bajt äaþcucha, czyli kod ASCII tego bajta. Za po-

mocñ metody

String#each_byte

moĔna iterowaè po wszystkich obiektach

Fixnum

tworzñcych

dany äaþcuch.

Patrz tak

że

x

Receptura 1.8, „Przetwarzanie kolejnych znaków äaþcucha”.

1.7. Konwersja mi

ýdzy ĥaħcuchami a symbolami

Problem

Majñc symbol jözyka Ruby, naleĔy uzyskaè reprezentujñcy go äaþcuch, lub vice versa — ziden-
tyfikowaè symbol odpowiadajñcy danemu äaþcuchowi.

Rozwi

ézanie

Konwersjö symbolu na odpowiadajñcy mu äaþcuch realizuje metoda

Symbol#to_s

lub metoda

Symbol#id2name

, dla której

to_s

jest aliasem.

:a_symbol.to_s # => "a_symbol"
:InnySymbol.id2name # => "InnySymbol"
:"Jeszcze jeden symbol!".to_s # => "Jeszcze jeden symbol!"

Odwoäanie do symbolu nastöpuje zwykle przez jego nazwö. Aby uzyskaè symbol reprezento-
wany przez äaþcuch w kodzie programu, naleĔy posäuĔyè siö metodñ

String.intern

:

:dodecahedron.object_id # => 4565262
symbol_name = "dodecahedron"
symbol_name.intern # => :dodecahedron
symbol_name.intern.object_id # => 4565262

Dyskusja

Symbol

jest najbardziej podstawowym obiektem jözyka Ruby. KaĔdy symbol posiada nazwö

i wewnötrzny identyfikator (internal ID). UĔytecznoĈè symboli wynika z faktu, Ĕe wielokrot-
ne wystñpienie tej samej nazwy w kodzie programu oznacza kaĔdorazowo odwoäanie do te-
go samego symbolu.

background image

1.8. Przetwarzanie kolejnych znaków

ĥaħcucha

_

43

Symbole sñ czösto bardziej uĔyteczne niĔ äaþcuchy. Dwa äaþcuchy o tej samej zawartoĈci
sñ dwoma róĔnymi obiektami — moĔna jeden z nich zmodyfikowaè bez wpäywu na drugi.
Dwie identyczne nazwy odnoszñ siö do tego samego symbolu, co oczywiĈcie przekäada siö na
oszczödnoĈè czasu i pamiöci.

"string".object_id # => 1503030
"string".object_id # => 1500330
:symbol.object_id # => 4569358
:symbol.object_id # => 4569358

Tak wiöc n wystñpieþ tej samej nazwy odnosi siö do tego samego symbolu, przechowywanego
w pamiöci w jednym egzemplarzu. n identycznych äaþcuchów to n róĔnych obiektów o iden-
tycznej zawartoĈci. TakĔe porównywanie symboli jest szybsze niĔ porównywanie äaþcuchów,
bowiem sprowadza siö jedynie do porównywania identyfikatorów.

"string1" == "string2" # => false
:symbol1 == :symbol2 # => false

Na koniec zacytujmy hakera od jözyka Ruby, Jima Wericha:

x

UĔyj äaþcucha, jeĈli istotna jest zawartoĈè obiektu (sekwencja tworzñcych go znaków).

x

UĔyj symbolu, jeĈli istotna jest toĔsamoĈè obiektu.

Patrz tak

że

x

Receptura 5.1, „Wykorzystywanie symboli jako kluczy”.

x

Receptura 8.12, „Symulowanie argumentów zawierajñcych säowa kluczowe”.

x

Rozdziaä 10., a szczególnie receptura 10.4, „Uzyskiwanie referencji do metody”, i receptu-
ra 10.10, „Oszczödne kodowanie dziöki metaprogramowaniu”.

x

http://glu.ttono.us/articles/2005/08/19/understanding-ruby-symbols — interesujñcy artykuä o sym-
bolach jözyka Ruby.

1.8. Przetwarzanie kolejnych znaków

ĥaħcucha

Problem

NaleĔy wykonaè pewnñ czynnoĈè w stosunku do kaĔdego znaku äaþcucha z osobna.

Rozwi

ézanie

W dokumencie zäoĔonym wyäñcznie ze znaków ASCII kaĔdy bajt äaþcucha odpowiada jedne-
mu znakowi. Za pomocñ metody

String#each_byte

moĔna wyodröbniè poszczególne bajty

jako liczby, które nastöpnie mogñ byè skonwertowane na znaki.

'foobar'.each_byte { |x| puts "#{x} = #{x.chr}" }
# 102 = f
# 111 = o
# 111 = o
# 98 = b
# 97 = a
# 114 = r

background image

44

_

Rozdzia

ĥ 1. Ĥaħcuchy

Za pomocñ metody

String#scan

moĔna wyodröbniè poszczególne znaki äaþcucha jako jedno-

znakowe äaþcuchy:

'foobar'.scan( /./ ) { |c| puts c }
# f
# o
# o
# b
# a
# r

Dyskusja

PoniewaĔ äaþcuch jest sekwencjñ bajtów, moĔna by oczekiwaè, Ĕe metoda

String#each

umoĔ-

liwia iterowanie po tej sekwencji, podobnie jak metoda

Array#each

. Jest jednak inaczej — me-

toda

String#each

dokonuje podziaäu äaþcucha na podäaþcuchy wzglödem pewnego separa-

tora (którym domyĈlnie jest znak nowego wiersza):

"foo\nbar".each { |x| puts x }
# foo
# bar

Odpowiednikiem metody

Array#each

w odniesieniu do äaþcuchów jest metoda

each_byte

.

KaĔdy element äaþcucha moĔe byè traktowany jako obiekt

Fixnum

, a metoda

each_byte

umoĔ-

liwia iterowanie po sekwencji tych obiektów.

Metoda

String#each_byte

jest szybsza niĔ

String#scan

i jako taka zalecana jest w przypad-

ku przetwarzania plików ASCII — kaĔdy wyodröbniony obiekt

Fixnum

moĔe byè äatwo prze-

ksztaäcony w znak (jak pokazano w Rozwiñzaniu).

Metoda

String#scan

dokonuje sukcesywnego dopasowywania podanego wyraĔenia regu-

larnego do kolejnych porcji äaþcucha i wyodröbnia kaĔdñ z tych opcji. JeĔeli wyraĔeniem tym
jest

/./

, wyodröbniane sñ poszczególne znaki äaþcucha.

JeĈli zmienna

$KCODE

jest odpowiednio ustawiona, metoda

scan

moĔe byè stosowana takĔe

do äaþcuchów zawierajñcych znaki w kodzie UTF-8. Jest to najprostsza metoda przeniesienia
koncepcji „znaku” na grunt äaþcuchów jözyka Ruby, które z definicji sñ ciñgami bajtów, nie
znaków.

PoniĔszy äaþcuch zawiera zakodowanñ w UTF-8 francuskñ frazö „ça va”:

french = "\xc3\xa7a va"

Nawet jeĔeli znaku

ç

nie sposób poprawnie wyĈwietliè na terminalu, poniĔszy przykäad ilu-

struje zmianö zachowania metody

String#scan

w sytuacji, gdy okreĈli siö wyraĔenie regu-

larne stosownie do standardów Unicode lub ustawi zmiennñ

$KCODE

tak, by Ruby traktowaä

wszystkie äaþcuchy jako kodowane wedäug UTF-8:

french.scan(/./) { |c| puts c }
#

ë

# §
# a
#
# v
# a

french.scan(/./u) { |c| puts c }
# ç
# a
#

background image

1.9. Przetwarzanie poszczególnych s

ĥów ĥaħcucha

_

45

# v
# a

$KCODE = 'u'
french.scan(/./) { |c| puts c }
# ç
# a
#
# v
# a

Gdy Ruby traktuje äaþcuchy jako sekwencje znaków UTF-8, a nie ASCII, dwa bajty reprezen-
tujñce znak

ç

traktowane sñ äñcznie, jako pojedynczy znak. Nawet jeĈli niektórych znaków

UTF-8 nie moĔna wyĈwietliè na ekranie terminala, moĔna stworzyè programy, które zajmñ
siö ich obsäugñ.

Patrz tak

że

x

Receptura 11.12, „Konwersja dokumentu miödzy róĔnymi standardami kodowania”.

1.9. Przetwarzanie poszczególnych s

ĥów ĥaħcucha

Problem

NaleĔy wydzieliè z äaþcucha jego kolejne säowa i dla kaĔdego z tych säów wykonaè pewnñ
czynnoĈè.

Rozwi

ézanie

Najpierw naleĔy zastanowiè siö nad tym, co rozumiemy pod pojöciem „säowa” w äaþcuchu.
Co oddziela od siebie sñsiednie säowa? Tylko biaäe znaki, czy moĔe takĔe znaki interpunkcyj-
ne? Czy „taki-to-a-taki” to pojedyncze säowo, czy moĔe cztery säowa? Te i inne kwestie roz-
strzyga siö jednoznacznie, definiujñc wyraĔenie regularne reprezentujñce pojedyncze säowo
(kilka przykäadów takich wyraĔeþ podajemy poniĔej w Dyskusji).

Wspomniane wyraĔenie regularne naleĔy przekazaè jako parametr metody

String#scan

, któ-

ra tym samym dokona podzielenia äaþcucha na poszczególne säowa. Prezentowana poniĔej
metoda

word_count

zlicza wystñpienia poszczególnych säów w analizowanym tekĈcie; zgod-

nie z uĔytym wyraĔeniem regularnym „säowo” ma skäadniö identycznñ z identyfikatorem jö-
zyka Ruby, jest wiöc ciñgiem liter, cyfr i znaków podkreĈlenia:

class String
def word_count
frequencies = Hash.new(0)
downcase.scan(/\w+/) { |word| frequencies[word] += 1 }
return frequencies
end
end

%{Dogs dogs dog dog dogs.}.word_count
# => {"dogs"=>3, "dog"=>2}
%{"I have no shame," I said.}.word_count
# => {"no"=>1, "shame"=>1, "have"=>1, "said"=>1, "i"=>2}

background image

46

_

Rozdzia

ĥ 1. Ĥaħcuchy

Dyskusja

WyraĔenie regularne

/\w+/

jest co prawda proste i eleganckie, jednakĔe ucieleĈniana przezeþ

definicja „säowa” z pewnoĈciñ pozostawia wiele do Ĕyczenia. Przykäadowo, rzadko kto skäon-
ny byäby uwaĔaè za pojedyncze säowo dwa säowa (w rozumieniu potocznym) poäñczone zna-
kiem podkreĈlenia, ponadto niektóre ze säów angielskich — jak „pan-fried” czy „foc’s’le” —
zawierajñ znaki interpunkcyjne. Warto wiöc byè moĔe rozwaĔyè kilka alternatywnych wyra-
Ĕeþ regularnych, opartych na bardziej wyszukanych koncepcjach säowa:

# Podobne do /\w+/, lecz nie dopuszcza podkresle

Ĭ wewnîtrz sĪowa.

/[0-9A-Za-z]/

# Dopuszcza w s

Īowie dowolne znaki oprócz biaĪych znaków.

/[^\S]+/

# Dopuszcza w s

Īowie litery, cyfry, apostrofy i Īîczniki

/[-'\w]+/

# Zadowalaj

îca heurystyka reprezentowania sĪów angielskich

/(\w+([-'.]\w+)*)/

Ostatnie z prezentowanych wyraĔeþ regularnych wymaga krótkiego wyjaĈnienia. Reprezen-
towana przezeþ koncepcja dopuszcza znaki interpunkcyjne wewnñtrz säowa, lecz nie na jego
kraþcach — i tak na przykäad „Work-in-progress” zostanie w Ĉwietle tej koncepcji uznane
za pojedyncze säowo, lecz juĔ äaþcuch „--never--” rozpoznany zostanie jako säowo „never”
otoczone znakami interpunkcyjnymi. Co wiöcej, poprawnie rozpoznane zostanñ akronimy
w rodzaju „U.N.C.L.E.” czy „Ph.D.” — no, moĔe nie do koþca poprawnie, poniewaĔ ostatnia
z kropek, równouprawniona z poprzednimi, nie zostanie zaliczona w poczet säowa i pierw-
szy z wymienionych akronimów zostanie rozpoznany jako säowo „U.N.C.L.E”, po którym
nastöpuje kropka.

Napiszmy teraz na nowo naszñ metodö

word_count

, wykorzystujñc ostatnie z prezentowa-

nych wyraĔeþ regularnych. RóĔni siö ono od wersji poprzedniej pewnym istotnym szczegó-
äem: otóĔ wykorzystywane wyraĔenie regularne skäada siö tym razem z dwóch grup. Metoda

String#scan

wyodröbni wiöc kaĔdorazowo dwa podäaþcuchy i przekaĔe je jako dwa argu-

menty do swego bloku kodowego. PoniewaĔ tylko pierwszy z tych argumentów reprezento-
waè bödzie rzeczywiste säowo, drugi z nich musimy zwyczajnie zignorowaè.

class String
def word_count
frequencies = Hash.new(0)
downcase.scan(/(\w+([-'.]\w+)*)/) { |word, ignore| frequencies[word] += 1 }
return frequencies
end
end

%{"That F.B.I. fella--he's quite the man-about-town."}.word_count
# => {"quite"=>1, "f.b.i"=>1, "the"=>1, "fella"=>1, "that"=>1,
# "man-about-town"=>1, "he's"=>1}

Zwróèmy uwagö, iĔ fraza

\w

reprezentowaè moĔe róĔne rzeczy w zaleĔnoĈci od wartoĈci

zmiennej

$KCODE

. DomyĈlnie reprezentuje ona jedynie säowa skäadajñce siö wyäñcznie ze zna-

ków ASCII:

french = "il \xc3\xa9tait une fois"
french.word_count
# => {"fois"=>1, "une"=>1, "tait"=>1, "il"=>1}

background image

1.10. Zmiana wielko

ļci liter w ĥaħcuchu

_

47

JeĈli jednak wäñczymy obsäugö kodu UTF-8, reprezentowaè bödzie ona takĔe säowa zawiera-
jñce znaki w tymĔe kodzie:

$KCODE='u'
french.word_count
# => {"fois"=>1, "une"=>1, "était"=>1, "il"=>1}

Grupa

/b

w wyraĔeniu regularnym reprezentuje granicö säowa, czyli ostatnie säowo poprze-

dzajñce biaäy znak lub znak interpunkcyjny. Fakt ten bywa uĔyteczny w odniesieniu do me-
tody

String#split

(patrz receptura 1.4), lecz juĔ nie tak uĔyteczny w stosunku do metody

String#scan

.

Patrz tak

że

x

Receptura 1.4, „Odwracanie kolejnoĈci säów lub znaków w äaþcuchu”.

x

W bibliotece

Facets core

zdefiniowana jest metoda

String#each_word

, wykorzystujñca

wyraĔenie regularne

/([-'\w]+)/

.

1.10. Zmiana wielko

ļci liter w ĥaħcuchu

Problem

Wielkie/maäe litery sñ niewäaĈciwie uĔyte w äaþcuchu.

Rozwi

ézanie

Klasa

String

definiuje kilka metod zmieniajñcych wielkoĈè liter w äaþcuchu:

s = 'WITAM, nie ma Mnie W Domu, JesTeM W kaWIArNi.'
s.upcase # => "WITAM, NIE MA MNIE W DOMU, JESTEM W KAWIARNI."
s.downcase # => "witam, nie ma mnie w domu, jestem w kawiarni."
s.swapcase # => "witam, NIE MA mNIE w dOMU, jEStEm w KAwiaRnI."
s.capitalize # => "Witam, nie ma mnie w domu, jestem w kawiarni."

Dyskusja

Metody

upcase

i

downcase

wymuszajñ zmianö wszystkich liter w äaþcuchu na (odpowiednio)

wielkie i maäe. Metoda

swapcase

dokonuje zamiany maäych liter na wielkie i vice versa. Me-

toda

capitalize

dokonuje zamiany pierwszego znaku äaþcucha na wielkñ literö pod warun-

kiem,

Ĕe znak ten jest literñ; wszystkie nastöpne litery w äaþcuchu zamieniane sñ na maäe.

KaĔda z czterech wymienionych metod posiada swój odpowiednik dokonujñcy stosownej
zamiany liter w miejscu

upcase!

,

downcase!

,

swapcase!

i

capitalize!

. Przy zaäoĔeniu, Ĕe

oryginalny äaþcuch nie jest däuĔej potrzebny, uĔycie tych metod moĔe zmniejszyè zajötoĈè
pamiöci, szczególnie w przypadku däugich äaþcuchów:

un_banged = 'Hello world.'
un_banged.upcase # => "HELLO WORLD."
un_banged # => "Hello world."

banged = 'Hello world.'
banged.upcase! # => "HELLO WORLD."
banged # => "HELLO WORLD."

background image

48

_

Rozdzia

ĥ 1. Ĥaħcuchy

W niektórych przypadkach istnieje potrzeba zamiany pierwszego znaku äaþcucha na wielkñ
literö (jeĈli w ogóle jest literñ) bez zmiany wielkoĈci pozostaäych liter — w äaþcuchu mogñ
bowiem wystöpowaè nazwy wäasne. CzynnoĈè tö realizujñ dwie poniĔsze metody — druga
oczywiĈcie dokonuje stosownej zamiany „w miejscu”:

class String
def capitalize_first_letter
self[0].chr.capitalize + self[1, size]
end

def capitalize_first_letter!
unless self[0] == (c = self[0,1].upcase[0])
self[0] = c
self
end
# Zwraca nil, je

Łli nie dokonano šadnych zmian, podobnie jak np. upcase!.

end
end

s = 'teraz jestem w Warszawie. Jutro w Sopocie.'
s.capitalize_first_letter # => "Teraz jestem w Warszawie. Jutro w Sopocie."
s # => "teraz jestem w Warszawie. Jutro w Sopocie."
s.capitalize_first_letter!
s # => "Teraz jestem w Warszawie. Jutro w Sopocie."

Do zmiany wielkoĈci wybranej litery w äaþcuchu, bez zmiany wielkoĈci pozostaäych liter, moĔ-
na wykorzystaè metodö

tr

lub

tr!

, dokonujñcñ translacji jednego znaku na inny:

'LOWERCASE ALL VOWELS'.tr('AEIOU', 'aeiou')
# => "LoWeRCaSe aLL VoWeLS"

'Swap case of ALL VOWELS'.tr('AEIOUaeiou', 'aeiouAEIOU')
# => "SwAp cAsE Of aLL VoWeLS"

Patrz tak

że

x

Receptura 1.18, „Zastöpowanie wielu wzorców w pojedynczym przebiegu”.

x

W bibliotece

Facets core

zdefiniowana jest metoda

String#camelcase

oraz metody pre-

dykatowe

String#lowercase?

i

String#uppercase?

.

1.11. Zarz

édzanie biaĥymi znakami

Problem

ãaþcuch zawiera zbyt duĔo lub zbyt maäo biaäych znaków, bñdĒ uĔyto w nim niewäaĈciwych
biaäych znaków.

Rozwi

ézanie

Za pomocñ metody

strip

moĔna usunñè biaäe znaki z poczñtku i koþca äaþcucha.

" \tWhitespace at beginning and end. \t\n\n".strip
# => "Whitespace at beginning and end."

Metody

ljust

,

rjust

i

center

dokonujñ (odpowiednio) wyrównania äaþcucha do lewej stro-

ny, wyrównania do prawej oraz wyĈrodkowania:

background image

1.12. Czy mo

żna potraktowaë dany obiekt jak ĥaħcuch?

_

49

s = "To jest napis." # => "To jest napis."
s.center(30) => # => " To jest napis. "
s.ljust(30) => # => "To jest napis. "
s.rjust(30) => # => " To jest napis."

Za pomocñ metody

gsub

, w poäñczeniu z wyraĔeniami regularnymi, moĔna dokonywaè zmian

bardziej zaawansowanych, na przykäad zastöpowaè jeden typ biaäych znaków innym:

# Normalizacja kodu przez zast

Ăpowanie kašdego tabulatora ciîgiem dwóch spacji

rubyCode.gsub("\t", " ")

# Zamiana ograniczników wiersza z windowsowych na uniksowe
"Line one\n\rLine two\n\r".gsub("\n\r", "\n")
# => "Line one\nLine two\n"

# Zamiana ka

šdego ciîgu biaĪych znaków na pojedynczî spacjĂ

"\n\rThis string\t\t\tuses\n all\tsorts\nof whitespace.".gsub(/\s+/, " ")
# => " This string uses all sorts of whitespace."

Dyskusja

Biaäym znakiem (whitespace) jest kaĔdy z piöciu nastöpujñcych znaków: spacja, tabulator (

\t

),

znak nowego wiersza (

\n

), znak powrotu do poczñtku wiersza (

\r

) i znak nowej strony (

\f

).

WyraĔenie regularne

/\s/

reprezentuje dowolny znak z tego zbioru. Metoda

strip

dokonuje

usuniöcia dowolnej kombinacji tych znaków z poczñtku i koþca äaþcucha.

Niekiedy konieczne jest przetwarzanie innych niedrukowalnych znaków w rodzaju backspace
(

\b

lub

\010

) czy tabulatora pionowego (

\v

lub

\012

). Znaki te nie naleĔñ do grupy znaków

reprezentowanych przez

/s

w wyraĔeniu regularnym i trzeba je reprezentowaè explicite:

" \bIt's whitespace, Jim,\vbut not as we know it.\n".gsub(/[\s\b\v]+/, " ")
# => " It's whitespace, Jim, but not as we know it. "

Do usuniöcia biaäych znaków tylko z poczñtku lub tylko z koþca äaþcucha moĔna wykorzy-
staè metody (odpowiednio)

lstrip

i

rstrip

:

s = " Whitespace madness! "
s.lstrip # => "Whitespace madness! "
s.rstrip # => " Whitespace madness!"

Metody dopeäniajñce spacjami do Ĕñdanej däugoĈci (

ljust

,

rjust

i

center

) posiadajñ jeden

argument wywoäania — tö wäaĈnie däugoĈè. JeĔeli wyĈrodkowanie äaþcucha nie moĔe byè
wykonane idealnie, bo liczba doäñczanych spacji jest nieparzysta, z prawej strony doäñczana
jest jedna spacja wiöcej niĔ z lewej.

"napis".center(9) # => " napis "
"napis".center(10) # => " napis "

Podobnie jak wiökszoĈè metod modyfikujñcych äaþcuchy, metody

strip

,

gsub

,

lstrip

i

rstrip

posiadajñ swe odpowiedniki operujñce „w miejscu” —

strip!

,

gsub!

,

lstrip!

i

rstrip!

.

1.12. Czy mo

żna potraktowaë dany obiekt jak ĥaħcuch?

Problem

Czy dany obiekt przejawia elementy funkcjonalnoĈci charakterystyczne dla äaþcuchów?

background image

50

_

Rozdzia

ĥ 1. Ĥaħcuchy

Rozwi

ézanie

SprawdĒ, czy obiekt definiuje metodö

to_str

.

'To jest napis'.respond_to? :to_str # => true
Exception.new.respond_to? :to_str # => true
4.respond_to? :to_str # => false

Sformuäowany powyĔej problem moĔemy jednak rozwaĔaè w postaci bardziej ogólnej: czy
mianowicie dany obiekt definiuje pewnñ konkretnñ metodö klasy

String

, z której to metody

chcielibyĈmy skorzystaè. Oto przykäad konkatenacji obiektu z jego nastöpnikiem i konwersji
wyniku do postaci äaþcucha — to wszystko wykonalne jest jednak tylko wtedy, gdy obiekt
definiuje metodö

succ

wyznaczajñcñ nastöpnik:

def join_to_successor(s)
raise ArgumentError, 'Obiekt nie definiuje metody succ!' unless s.respond_to? :succ
return "#{s}#{s.succ}"
end

join_to_successor('a') # => "ab"
join_to_successor(4) # => "45"
join_to_successor(4.01) # ArgumentError: Obiekt nie definiuje metody succ!

GdybyĈmy zamiast predykatu

s.respond_to? :succ

uĔyli predykatu

s.is_a? String

, oka-

zaäoby siö, Ĕe nie jest moĔliwe wyznaczenie nastöpnika dla liczby caäkowitej:

def join_to_successor(s)
raise ArgumentError, 'Obiekt nie jest

ĪaĬcuchem!' unless s.is_a? String

return "#{s}#{s.succ}"
end

join_to_successor('a') # => "ab"
join_to_successor(4) # => ArgumentError: 'Obiekt nie jest

ĪaĬcuchem!'

join_to_successor(4.01) # => ArgumentError: 'Obiekt nie jest

ĪaĬcuchem!'

Dyskusja

To, co widzimy powyĔej, jest najprostszym przykäadem pewnego aspektu filozofii jözyka
Ruby, zwanego „kaczym typowaniem” (duck typing): jeĈli mianowicie chcemy przekonaè siö,
Ĕe dane zwierzö jest kaczkñ, moĔemy skäoniè je do wydania gäosu — powinniĈmy wówczas
usäyszeè kwakanie. Na podobnej zasadzie moĔemy badaè rozmaite aspekty funkcjonalnoĈci
obiektu, sprawdzajñc, czy obiekt ów definiuje metody o okreĈlonych nazwach, realizujñce tö
wäaĈnie funkcjonalnoĈè.

Jak przekonaliĈmy siö przed chwilñ, predykat

obj.is_a? String

nie jest najlepszym sposo-

bem badania, czy mamy do czynienia z äaþcuchem. Owszem, jeĈli predykat ten jest speäniony,
obiekt äaþcuchem jest niewñtpliwie, jego klasa wywodzi siö bowiem z klasy

String

; zaleĔnoĈè

odwrotna nie zawsze jest jednak prawdziwa — pewne zachowania typowe dla äaþcuchów
mogñ byè przejawiane przez obiekty niewywodzñce siö z klasy

String

.

Jako przykäad posäuĔyè moĔe klasa

Exceptions

, której obiekty sñ koncepcyjnie äaþcuchami

wzbogaconymi o pewne dodatkowe informacje. Klasa

Exceptions

nie jest jednak subklasñ

klasy

String

i uĔycie w stosunku do niej predykatu

is_a? String

moĔe spowodowaè prze-

oczenie jej „äaþcuchowoĈci”. Wiele moduäów jözyka Ruby definiuje inne rozmaite klasy o tej-
Ĕe wäasnoĈci.

background image

1.13. Wyodr

ýbnianie czýļci ĥaħcucha

_

51

Warto wiöc zapamiötaè (i stosowaè) opisanñ filozofiö: jeĈli chcemy badaè pewien aspekt funk-
cjonalny obiektu, powinniĈmy czyniè to, sprawdzajñc (za pomocñ predykatu

respond_to?

),

czy obiekt ten definiuje okreĈlonñ metodö, zamiast badaè jego genealogiö za pomocñ predy-
katu

is_a?

. Pozwoli to w przyszäoĈci na definiowanie nowych klas oferujñcych te same moĔ-

liwoĈci, bez kröpujñcego uzaleĔniania ich od istniejñcej hierarchii klas. Jedynym uzaleĔnie-
niem bödzie wówczas uzaleĔnienie od konkretnych nazw metod.

Patrz tak

że

x

Rozdziaä 8., szczególnie wstöp oraz receptura 8.3, „Weryfikacja funkcjonalnoĈci obiektu”.

1.13. Wyodr

ýbnianie czýļci ĥaħcucha

Problem

Majñc dany äaþcuch, naleĔy wyodröbniè okreĈlone jego fragmenty.

Rozwi

ézanie

W celu wyodröbnienia podäaþcucha moĔemy posäuĔyè siö metodñ

slice

lub wykorzystaè

operator indeksowania tablicy (czyli de facto wywoäaè metodö

[]

). W obydwu przypadkach

moĔemy okreĈliè bñdĒ to zakres (obiekt

Range

) wyodröbnianych znaków, bñdĒ parö liczb caä-

kowitych (obiektów

Fixnum

) okreĈlajñcych (kolejno) indeks pierwszego wyodröbnianego zna-

ku oraz liczbö wyodröbnianych znaków:

s = "To jest napis"
s.slice(0,2) # => "To"
s[3,4] # => "jest"
s[8,5] # => "napis"
s[8,0] # => ""

Aby wyodröbniè pierwszñ porcjö äaþcucha pasujñcñ do danego wyraĔenia regularnego, naleĔy
wyraĔenia tego uĔyè jako argumentu wywoäania metody

slice

lub operatora indeksowego:

s[/.pis/] # => "apis"
s[/na.*/] # => "napis"

Dyskusja

Dla uzyskania pojedynczego bajta äaþcucha (jako obiektu

Fixnum

) wystarczy podaè jeden ar-

gument — indeks tego bajta (pierwszy bajt ma indeks 0). Aby otrzymaè znakowñ postaè owe-
go bajta, naleĔy podaè dwa argumenty: jego indeks oraz

1

:

s.slice(3) # => 106
s[3] # => 106
106.chr # => "j"
s.slice(3,1) # => "j"
s[3,1] # => "j"

Ujemna wartoĈè pierwszego argumentu oznacza indeks liczony wzglödem koþca äaþcucha:

s.slice(-1,1) # => "s"
s.slice(-5,5) # => "napis"
s[-5,5] # => "napis"

background image

52

_

Rozdzia

ĥ 1. Ĥaħcuchy

JeĔeli specyfikowana däugoĈè podäaþcucha przekracza däugoĈè caäego äaþcucha liczonñ od
miejsca okreĈlonego przez pierwszy argument, zwracana jest caäa reszta äaþcucha poczñwszy
od tego miejsca. UmoĔliwia to wygodne specyfikowanie „koþcówek” äaþcuchów:

s[8,s.length] # => "napis"
s[-5,s.length] # => "napis"
s[-5, 65535] # => "napis"

Patrz tak

że

x

Receptura 1.9, „Przetwarzanie poszczególnych säów äaþcucha”.

x

Receptura 1.17, „Dopasowywanie äaþcuchów za pomocñ wyraĔeþ regularnych”.

1.14. Obs

ĥuga miýdzynarodowego kodowania

Problem

W äaþcuchu znajdujñ siö znaki niewchodzñce w skäad kodu ASCII — na przykäad znaki Uni-
code kodowane wedäug UTF-8.

Rozwi

ézanie

Aby zapewniè poprawnñ obsäugö znaków Unicode, naleĔy na poczñtku kodu umieĈciè nastö-
pujñcñ sekwencjö:

$KCODE='u'
require 'jcode'

Identyczny efekt moĔna osiñgnñè, uruchamiajñc interpreter jözyka Ruby w nastöpujñcy sposób:

$ ruby -Ku –rjcode

W Ĉrodowisku Uniksa moĔna okreĈliè powyĔsze parametry w poleceniu uruchamiajñcym
skrypt (shebang line):

#!/usr/bin/ruby -Ku –rjcode

W bibliotece

jcode

wiökszoĈè metod klasy

String

zostaäa przedefiniowana tak, by metody

te zapewniaäy obsäugö znaków wielobajtowych. Nie przedefiniowano metod

String#length

,

String#count

i

String#size

, definiujñc w zamian trzy nowe metody,

String#jlength

,

String#jcount

i

String#jsize

.

Dyskusja

Rozpatrzmy przykäadowy äaþcuch zawierajñcy szeĈè znaków Unicode:

efbca1

(

A

),

efbca2

(

B

),

efbca3

(

C

),

efbca4

(

D

),

efbca5

(

E

) i

efbca6

(

F

):

string = "\xef\xbc\xa1" + "\xef\xbc\xa2" + "\xef\xbc\xa3" +
"\xef\xbc\xa4" + "\xef\xbc\xa5" + "\xef\xbc\xa6"

ãaþcuch ten skäada siö z 18 bajtów, kodujñcych 6 znaków:

string.size # => 18
string.jsize # => 6

background image

1.15. Zawijanie wierszy tekstu

_

53

Metoda

String#count

zlicza wystñpienia okreĈlonych bajtów w äaþcuchu, podczas gdy me-

toda

String#jcount

dokonuje zliczania okreĈlonych znaków:

string.count "\xef\xbc\xa2" # => 13
string.jcount "\xef\xbc\xa2" # => 1

W powyĔszym przykäadzie metoda

count

traktuje argument

"\xef\xbc\xa2"

jak trzy od-

dzielne bajty

\xef

,

\xbc

i

\xa2

, zwracajñc sumö liczby ich wystñpieþ w äaþcuchu (6+6+1). Me-

toda

jcount

traktuje natomiast swój argument jako pojedynczy znak, zwracajñc liczbö jego wy-

stñpieþ w äaþcuchu (w tym przypadku znak wystöpuje tylko raz).

"\xef\xbc\xa2".length # => 3
"\xef\xbc\xa2".jlength # => 1

Metoda

String#length

zwraca, jak wiadomo, liczbö bajtów äaþcucha niezaleĔnie od tego, ja-

kie znaki sñ za pomocñ tych bajtów kodowane. Metoda

String#jlength

zwraca natomiast

liczbö kodowanych znaków.

Mimo tych wyraĒnych róĔnic obsäuga znaków Unicode odbywa siö w jözyku Ruby w wiök-
szoĈci „pod podszewkñ” — przetwarzanie äaþcuchów zawierajñcych znaki kodowane wedäug
UTF-8 odbywa siö w sposób elegancki i naturalny, bez jakiejĈ szczególnej troski ze strony pro-
gramisty. Stanie siö to caäkowicie zrozumiaäe, gdy uĈwiadomimy sobie, Ĕe twórca Ruby —
Yukihiro Matsumoto — jest Japoþczykiem.

Patrz tak

że

x

Tekst zäoĔony ze znaków kodowanych w systemie innym niĔ UTF-8 moĔe byè äatwo
przekodowany do UTF-8 za pomocñ biblioteki

iconv

, o czym piszemy w recepturze 11.2,

„Ekstrakcja informacji z drzewa dokumentu”.

x

Istnieje kilka wyszukiwarek on-line obsäugujñcych znaki Unicode; dwiema godnymi pole-
cenia wydajñ siö naszym zdaniem http://isthisthingon.org/unicode/ oraz http://www.fileformat.
info/info/unicode/char/search.htm
.

1.15. Zawijanie wierszy tekstu

Problem

ãaþcuch zawierajñcy duĔñ liczbö biaäych znaków naleĔy sformatowaè, dzielñc go na wiersze,
tak aby moĔliwe byäo jego wyĈwietlenie w oknie lub wysäanie e-mailem.

Rozwi

ézanie

Najprostszym sposobem wstawienia do äaþcucha znaków nowego wiersza jest uĔycie wyra-

Ĕenia regularnego podobnego do poniĔszego:

def wrap(s, width=78)
s.gsub(/(.{1,#{width}})(\s+|\Z)/, "\\1\n")
end

wrap("Ten tekst jest zbyt krótki, by trzeba go by

Īo zawijað.")

# => "Ten tekst jest zbyt krótki, by trzeba go by

Īo zawijað. \n"

puts wrap("Ten tekst zostanie zawini

Ăty.", 15)

background image

54

_

Rozdzia

ĥ 1. Ĥaħcuchy

# Ten tekst
# zostanie
# zawini

Ăty.

puts wrap("Ten tekst zostanie zawini

Ăty.", 20)

# Ten tekst zostanie
# zawini

Ăty.

puts wrap("By

ð albo nie byð – oto jest pytanie!",5)

# By

ð

# albo
# nie
# by

ð –

# oto
# jest
# pytanie!

Dyskusja

W prezentowanym przykäadzie zachowane zostaäo oryginalne formatowanie äaþcucha, jedno-
czeĈnie w kilku jego miejscach wstawione zostaäy znaki nowego wiersza. W efekcie uzyskali-
Ĉmy äaþcuch zdatny do wyĈwietlenia w stosunkowo niewielkim obszarze ekranu.

poetry = %q{It is an ancient Mariner,
And he stoppeth one of three.
"By thy long beard and glittering eye,
Now wherefore stopp'st thou me?}

puts wrap(poetry, 20)
# It is an ancient
# Mariner,
# And he stoppeth one
# of three.
# "By thy long beard
# and glittering eye,
# Now wherefore
# stopp'st thou me?

Niekiedy jednak biaäe znaki nie sñ istotne, co wiöcej — zachowanie ich w äaþcuchu powoduje
pogorszenie koþcowego rezultatu formatowania:

prose = %q{Czu

Īem siĂ tak samotny tego dnia, jak rzadko kiedy,

spogl

îdajîc apatycznie na deszcz padajîcy za oknem. Jak dĪugo jeszcze bĂdzie

pada

ð? W gazecie byĪa prognoza pogody, ale któš w ogóle zadaje sobie trud

jej czytania?}

puts wrap(prose, 50)
# Czu

Īem siĂ tak samotny tego dnia, jak rzadko

# kiedy,
# spogl

îdajîc apatycznie na deszcz padajîcy za

# oknem. Jak d

Īugo jeszcze bĂdzie

# pada

ð? W gazecie byĪa prognoza pogody, ale któš w

# ogóle zadaje sobie trud
# jej czytania?

By zniwelowaè efekt „postrzöpienia” tekstu, naleĔaäoby najpierw usunñè z niego istniejñce
znaki nowego wiersza. NaleĔy w tym celu uĔyè innego wyraĔenia regularnego:

def reformat_wrapped(s, width=78)
s.gsub(/\s+/, " ").gsub(/(.{1,#{width}})( |\Z)/, "\\1\n")
end

background image

1.16. Generowanie nast

ýpnika ĥaħcucha

_

55

Przetwarzanie sterowane wyraĔeniami regularnymi jest jednak stosunkowo powolne; znacz-
nie efektywniejszym rozwiñzaniem byäoby podzielenie äaþcucha na poszczególne säowa i zäo-
Ĕenie z nich nowego äaþcucha, podzielonego na wiersze nieprzekraczajñce okreĈlonej däugoĈci:

def reformat_wrapped(s, width=78)
lines = []
line = ""
s.split(/\s+/).each do |word|
if line.size + word.size >= width
lines << line
line = word
elsif line.empty?
line = word
else
line << " " << word
end
end
lines << line if line
return lines.join "\n"
end

puts reformat_wrapped(prose, 50)

# Czu

Īem siĂ tak samotny tego dnia, jak rzadko

# kiedy, spogl

îdajîc apatycznie na deszcz padajîcy

# za oknem. Jak d

Īugo jeszcze bĂdzie padað? W

# gazecie by

Īa prognoza pogody, ale któš w ogóle

# zadaje sobie trud jej czytania?

Patrz tak

że

x

W bibliotece

Facets Core

zdefiniowane sñ metody

String#word_wrap

i

String#word_

wrap!

.

1.16. Generowanie nast

ýpnika ĥaħcucha

Problem

NaleĔy wykonaè iteracjö po ciñgu äaþcuchów zwiökszajñcych siö alfabetycznie — w sposób
podobny do iterowania po ciñgu kolejnych liczb.

Rozwi

ézanie

JeĈli znany jest poczñtkowy i koþcowy äaþcuch z zakresu objötego iteracjñ, moĔna do tego za-
kresu (reprezentowanego jako obiekt

Range

) zastosowaè metodö

Range#each

:

('aa'..'ag').each { |x| puts x }
# aa
# ab
# ac
# ad
# ae
# af
# ag

background image

56

_

Rozdzia

ĥ 1. Ĥaħcuchy

Metodñ generujñcñ nastöpnik danego äaþcucha jest

String#succ

. JeĈli nie jest znany äaþcuch,

na którym naleĔy skoþczyè iterowanie, moĔna na bazie tej metody zdefiniowaè iteracjö nie-
skoþczonñ, którñ przerwie siö w momencie speänienia okreĈlonego warunku:

def endless_string_succession(start)
while true
yield start
start = start.succ
end
end

W poniĔszym przykäadzie iteracja jest koþczona w momencie, gdy dwa ostatnie znaki äaþcu-
cha sñ identyczne:

endless_string_succession('fol') do |x|
puts x
break if x[-1] == x[-2]
end
# fol
# fom
# fon
# foo

Dyskusja

WyobraĒmy sobie, Ĕe äaþcuch jest czymĈ na ksztaät (uogólnionego) licznika przejechanych ki-
lometrów — kaĔdy znak äaþcucha jest osobnñ pozycjñ tego licznika. Na kaĔdej z pozycji mo-
gñ pojawiaè siö znaki tylko jednego rodzaju: cyfry, maäe litery albo wielkie litery

3

.

Nastöpnikiem (successor) äaþcucha jest äaþcuch powstajñcy w wyniku zwiökszenia o 1 (inkre-
mentacji) wskazania wspomnianego licznika. Rozpoczynamy od zwiökszenia prawej skrajnej
pozycji; jeĈli spowoduje to jej „przekröcenie” na wartoĈè poczñtkowñ, zwiökszamy o 1 sñ-
siedniñ pozycjö z lewej strony — która teĔ moĔe siö przekröciè, wiöc opisanñ zasadö stosuje-
my rekurencyjnie:

'89999'.succ # => "90000"
'nzzzz'.succ # => "oaaaa"

JeĈli „przekröci” siö skrajna lewa pozycja, doäñczamy z lewej strony äaþcucha nowñ pozycjö
tego samego rodzaju co ona i ustawiamy tö dodanñ pozycjö na wartoĈè poczñtkowñ:

'Zzz'.succ # => "AAaa"

W powyĔszym przykäadzie skrajna lewa pozycja wyĈwietla wielkie litery; jej inkrementacja
powoduje „przekröcenie” z wartoĈci

Z

do wartoĈci

A

, dodajemy wiöc z lewej strony äaþcucha

nowñ pozycjö, takĔe wyĈwietlajñcñ wielkie litery, ustawiajñc jñ na wartoĈè poczñtkowñ

A

.

Oto przykäady inkrementacji äaþcuchów zawierajñcych wyäñcznie maäe litery:

'z'.succ # => "aa"
'aa'.succ # => "ab"
'zz'.succ # => "aaa"

W przypadku wielkich liter sprawa ma siö podobnie — naleĔy pamiötaè, Ĕe wielkie i maäe
litery nigdy nie wystöpujñ razem na tej samej pozycji:

'AA'.succ # => "AB"
'AZ'.succ # => "BA"

3

Ograniczamy siö tylko do liter alfabetu angielskiego

a

..

z

i

A

..

Z

przyp. täum.

background image

1.16. Generowanie nast

ýpnika ĥaħcucha

_

57

'ZZ'.succ # => "AAA"
'aZ'.succ # => "bA"
'Zz'.succ # => "AAa"

Inkrementowanie cyfr odbywa siö w sposób naturalny — inkrementacja cyfry

9

oznacza jej

„przekröcenie” na wartoĈè

0

:

'foo19'.succ # => "foo20"
'foo99'.succ # => "fop00"
'99'.succ # => "100"
'9Z99'.succ # => "10A00"

Znaki niealfanumeryczne — czyli inne niĔ cyfry, maäe litery i wielkie litery — sñ przy inkre-
mentowaniu äaþcucha ignorowane — wyjñtkiem jest jednak sytuacja, gdy äaþcuch skäada siö wy-

äñcznie ze znaków tej kategorii. UmoĔliwia to inkrementowanie äaþcuchów sformatowanych:

'10-99'.succ # => "11-00"

JeĈli äaþcuch skäada siö wyäñcznie ze znaków niealfanumerycznych, jego pozycje inkremento-
wane sñ zgodnie z uporzñdkowaniem znaków w kodzie ASCII; oczywiĈcie w wyniku inkre-
mentacji mogñ pojawiè siö w äaþcuchu znaki alfanumeryczne, wówczas kolejna jego inkremen-
tacja odbywa siö wedäug reguä wczeĈniej opisanych.

'a-a'.succ # => "a-b"
'z-z'.succ # => "aa-a"
'Hello!'.succ # => "Hellp!"
%q{'zz'}.succ # => "'aaa'"
%q{z'zz'}.succ # => "aa'aa'"
'$$$$'.succ # => "$$$%"

s = '!@-'
13.times { puts s = s.succ }
# !@.
# !@/
# !@0
# !@1
# !@2
# ...
# !@8
# !@9
# !@10

Nie istnieje metoda realizujñca funkcjö odwrotnñ do metody

String#succ

. Zarówno twórca

jözyka Ruby, jak i caäa wspólnota jego uĔytkowników zgodni sñ co do tego, Ĕe wobec ogra-
niczonego zapotrzebowania na takñ metodö nie warto wkäadaè wysiäku w jej tworzenie,
a zwäaszcza poprawnñ obsäugö róĔnych warunków granicznych. Iterowanie po zakresie äaþ-
cuchów w kierunku malejñcym najlepiej jest wykonywaè, transformujñc ów zakres na tablicö
i organizujñc iteracjö po tejĔe w kierunku malejñcych indeksów:

("a".."e").to_a.reverse_each { |x| puts x }
# e
# d
# c
# b
# a

Patrz tak

że

x

Receptura 2.15, „Generowanie sekwencji liczb”.

x

Receptura 3.4, „Iterowanie po datach”.

background image

58

_

Rozdzia

ĥ 1. Ĥaħcuchy

1.17. Dopasowywanie

ĥaħcuchów

za pomoc

é wyrażeħ regularnych

Problem

Chcemy sprawdziè, czy dany äaþcuch zgodny jest z pewnym wzorcem.

Rozwi

ézanie

Wzorce sñ zwykle definiowane za pomocñ wyraĔeþ regularnych. ZgodnoĈè („pasowanie”)

äaþcucha z wyraĔeniem regularnym testowane jest przez operator

=~

.

string = 'To jest

ĪaĬcuch 27-znakowy.'

if string =~ /([0-9]+)-character/ and $1.to_i == string.length
"Tak, to jest

ĪaĬcuch #$1-znakowy."

end
# "Tak, to jest

ĪaĬcuch 27-znakowy."

MoĔna takĔe uĔyè metody

Regexp#match

:

match = Regexp.compile('([0-9]+)-znakowy').match(string)
if match && match[1].to_i == string.length
"Tak, to jest

ĪaĬcuch #{match[1]}-znakowy."

end
# "Tak, to jest

ĪaĬcuch 27-znakowy."

Za pomocñ instrukcji

case

moĔna sprawdziè zgodnoĈè äaþcucha z caäym ciñgiem wyraĔeþ

regularnych:

string = "123"

case string
when /^[a-zA-Z]+$/
"Litery"
when /^[0-9]+$/
"Cyfry"
else
"Zawarto

Łð mieszana"

end
# => "Cyfry"

Dyskusja

WyraĔenia regularne stanowiñ maäo czytelny, lecz uĔyteczny minijözyk umoĔliwiajñcy dopaso-
wywanie äaþcuchów do wzorców oraz ekstrakcjö podäaþcuchów. WyraĔenia regularne wykorzy-
stywane sñ od dawna przez wiele narzödzi uniksowych (jak

sed

), lecz to Perl byä pierwszym

uniwersalnym jözykiem zapewniajñcym ich obsäugö. Obecnie wyraĔenia regularne w stylu
zbliĔonym do wersji z Perla obecne sñ w wiökszoĈci nowoczesnych jözyków programowania.

W jözyku Ruby wyraĔenia regularne inicjowaè moĔna na wiele sposobów. KaĔda z poniĔszych
konstrukcji daje w rezultacie taki sam obiekt klasy

Regexp

:

/cokolwiek/
Regexp.new("cokolwiek")
Regexp.compile("cokolwiek")
%r{ cokolwiek}

background image

1.17. Dopasowywanie

ĥaħcuchów za pomocé wyrażeħ regularnych

_

59

W wyraĔeniach regularnych moĔna uĔywaè nastöpujñcych modyfikatorów:

Regexp::IGNORECASE

i

Przy dopasowywaniu nieistotna jest wielko

ļë liter — maĥe litery utożsamiane sé z ich

wielkimi odpowiednikami.

Regexp:MULTILINE

m

Domy

ļlnie dopasowywanie realizowane jest w odniesieniu do ĥaħcucha mieszczécego

si

ý w jednym wierszu. Gdy użyty zostanie ten modyfikator, znaki nowego wiersza

traktowane s

é na równi z innymi znakami ĥaħcucha.

Regexp::EXTENDED

x

U

życie tego modyfikatora daje możliwoļë bardziej czytelnego zapisu wyrażenia

regularnego, przez wype

ĥnienie go biaĥymi znakami i komentarzami.

Oto przykäad wykorzystania wymienionych powyĔej modyfikatorów w definicji wyraĔenia
regularnego:

/something/mxi
Regexp.new('something',
Regexp::EXTENDED + Regexp::IGNORECASE + Regexp::MULTILINE)
%r{something}mxi

A oto efekt dziaäania tychĔe modyfikatorów:

case_insensitive = /mangy/i
case_insensitive =~ "I'm mangy!" # => 4
case_insensitive =~ "Mangy Jones, at your service." # => 0

multiline = /a.b/m
multiline =~ "banana\nbanana" # => 5
/a.b/ =~ "banana\nbanana" # => nil
# Ale zwróc uwag

Ă na to:

/a\nb/ =~ "banana\nbanana" # => 5

extended = %r{ \ was # Dopasowano " was"
\s # Dopasowano jeden bia

Īy znak

a # Dopasowano "a" }xi
extended =~ "What was Alfred doing here?" # => 4
extended =~ "My, that was a yummy mango." # => 8
extended =~ "It was\n\n\na fool's errand" # => nil

Patrz tak

że

x

KsiñĔka Jeffreya Friedla Mastering Regular Expressions

4

dostarcza eleganckiego i zwiözäe-

go wprowadzenia w tematykö wyraĔeþ regularnych, ilustrowanego wieloma praktyczny-
mi przykäadami.

x

Witryna RegExLib.com (http://regexlib.com/default.aspx) jest obszernñ bazñ wyraĔeþ regu-
larnych, wyposaĔonñ w wyszukiwarkö.

x

Przewodnik po wyraĔeniach regularnych i ich wykorzystywaniu w jözyku Ruby dostöp-
ny jest pod adresem http://www.regular-expressions.info/ruby.html.

x

Informacje na temat klasy

Regexp

moĔesz uzyskaè za pomocñ polecenia

ri Regexp

.

x

Receptura 1.19, „Weryfikacja poprawnoĈci adresów e-mailowych”.

4

Wydanie polskie: WyraĔenia regularne, wyd. Helion 2001 (http://helion.pl/ksiazki/wyrare.htm) — przyp. täum.

background image

60

_

Rozdzia

ĥ 1. Ĥaħcuchy

1.18. Zast

ýpowanie wielu wzorców

w pojedynczym przebiegu

Problem

Chcemy wykonaè kilka operacji typu „znajdĒ i zamieþ”, sterowanych oddzielnymi wyraĔe-
niami regularnymi — równolegle, w pojedynczym przejĈciu przez äaþcuch.

Rozwi

ézanie

Musimy uĔyè metody

Regexp.union

do zagregowania poszczególnych wyraĔeþ regularnych

w pojedyncze wyraĔenie, pasujñce do kaĔdego z wyraĔeþ czñstkowych. Zagregowane wyra-

Ĕenie musimy nastöpnie przekazaè jako parametr metody

String#gsub

wraz z blokiem kodo-

wym bazujñcym na obiekcie

MatchData

. Wiedzñc, do którego z wyraĔeþ czñstkowych przy-

porzñdkowaè moĔna znalezionñ frazö, moĔemy wybraè odpowiedniñ frazö zastöpujñcñ:

class String
def mgsub(key_value_pairs=[].freeze)
regexp_fragments = key_value_pairs.collect { |k,v| k }
gsub(Regexp.union(*regexp_fragments)) do |match|
key_value_pairs.detect{|k,v| k =~ match}[1]
end
end
end

Oto prosty przykäad uĔycia metody

mgsub

:

"GO HOME!".mgsub([[/.*GO/i, 'Home'], [/home/i, 'is where the heart is']])
# => "Home is where the heart is!"

W powyĔszym przykäadzie Ĕñdamy zamiany dowolnego ciñgu koþczñcego siö na

GO

(bez

wzglödu na wielkoĈè liter) na ciñg

Home

, zaĈ ciñgu

Home

(bez wzglödu na wielkoĈè liter) na

ciñg

is where the heart is

.

W poniĔszym przykäadzie zamieniamy wszystkie litery na znak #, a kaĔdy znak

#

na literö

P

:

"To jest liczba #123".mgsub([[/[a-z]/i, '#'], [/#/, 'P']])
# => "#### ## ###### P123"

Dyskusja

Wydawaäoby siö, Ĕe naiwne podejĈcie polegajñce na sukcesywnym wywoäaniu metody

gsub

dla kaĔdej operacji „znajdĒ i zamieþ” da identyczny efekt i tylko efektywnoĈciñ ustöpowaè
bödzie rozwiñzaniu wyĔej opisanemu. Jest jednak inaczej, o czym moĔemy siö przekonaè, spo-
glñdajñc na poniĔsze przykäady:

"GO HOME!".gsub(/.*GO/i, 'Home').gsub(/home/i, 'is where the heart is')
# => "is where the heart is is where the heart is!"

"To jest liczba #123".gsub(/[a-z]/i, '#').gsub(/#/, 'P')
# => "PP PPPP PPPPPP P123"

Przyczyna rozbieĔnoĈci z rozwiñzaniem „równolegäym” nie jest Ĕadnñ tajemnicñ: otóĔ w oby-
dwu przypadkach materiaäem wejĈciowym dla drugiego wywoäania metody

gsub

jest wynik

background image

1.19. Weryfikacja poprawno

ļci adresów e-mailowych

_

61

jej pierwszego wywo

äania. W wariancie równolegäym natomiast obydwa wywoäania metody

gsub

operujñ na äaþcuchu oryginalnym. W pierwszym przypadku moĔna zniwelowaè owñ in-

terferencjö, zamieniajñc kolejnoĈè operacji, w drugim jednak nawet i to nie pomoĔe.

Do metody

mgsub

moĔna przekazaè takĔe hasz, w którym poszukiwane frazy sñ kluczami,

a frazy zastöpujñce — wartoĈciami. Nie jest to jednak rozwiñzanie bezpieczne, bowiem ele-
menty hasza sñ z natury nieuporzñdkowane i w zwiñzku z tym kolejnoĈè zastöpowania fraz
wymyka siö spod kontroli. Znacznie lepszym wyjĈciem byäoby uĔycie tablicy elementów ty-
pu „klucz-wartoĈè”. PoniĔszy przykäad z pewnoĈciñ uäatwi zrozumienie tego problemu:

"between".mgsub(/ee/ => 'AA', /e/ => 'E') # Z

Īy kod

# => "bEtwEEn"

"between".mgsub([[/ee/, 'AA'], [/e/, 'E']]) # Dobry kod
# => "bEtwAAn"

W drugim przypadku najpierw wykonywane jest pierwsze zastöpowanie. W pierwszym przy-
padku jest ono wykonywane jako drugie i szukana fraza nie zostaje znaleziona — to jedna
z osobliwoĈci implementacji haszów w jözyku Ruby.

JeĈli efektywnoĈè programu jest czynnikiem krytycznym, naleĔy zastanowiè siö nad innñ im-
plementacjñ metody

mgsub

. Im wiöcej bowiem fraz do znalezienia i zastñpienia, tym däuĔej

trwaè bödzie caäa operacja, poniewaĔ metoda

detect

wykonuje sprawdzenie dla kaĔdego wy-

raĔenia regularnego i dla kaĔdej znalezionej frazy.

Patrz tak

że

x

Receptura 1.17, „Dopasowywanie äaþcuchów za pomocñ wyraĔeþ regularnych”.

x

Czytelnikom, którym zagadkowa wydaje siö skäadnia

Regexp.union(*regexp_fragments)

,

polecamy przestudiowanie receptury 8.11, „Metody wywoäywane ze zmiennñ liczbñ ar-
gumentów”.

1.19. Weryfikacja poprawno

ļci adresów e-mailowych

Problem

Chcemy sprawdziè, czy podany adres e-mailowy jest poprawny.

Rozwi

ézanie

Oto kilka przykäadowych adresów e-mail — poprawnych

test_addresses = [ # Poni

šsze adresy czyniî zadoŁð specyfikacji RFC822.

'joe@example.com', 'joe.bloggs@mail.example.com',
'joe+ruby-mail@example.com', 'joe(and-mary)@example.museum',
'joe@localhost',

i niepoprawnych

# Poni

šsze adresy sî niezgodne ze specyfikacjî RFC822

'joe', 'joe@', '@example.com',
'joe@example@example.com',
'joe and mary@example.com' ]

background image

62

_

Rozdzia

ĥ 1. Ĥaħcuchy

Oto kilka przykäadowych wyraĔeþ regularnych filtrujñcych bäödne adresy e-mailowe. Pierw-
sze z nich ogranicza siö do bardzo elementarnej kontroli.

valid = '[^ @]+' # Wyeliminowanie znaków bezwzgl

Ădnie niedopuszczalnych w adresie e-mail

username_and_machine = /^#{valid}@#{valid}$/

test_addresses.collect { |i| i =~ username_and_machine }
# => [0, 0, 0, 0, 0, nil, nil, nil, nil, nil]

Drugie z wyraĔeþ eliminuje adresy typowe dla sieci lokalnej, w rodzaju

joe@localhost

wiökszoĈè aplikacji nie zezwala na ich uĔywanie.

username_and_machine_with_tld = /^#{valid}@#{valid}\.#{valid}$/

test_addresses.collect { |i| i =~ username_and_machine_with_tld }
# => [0, 0, 0, 0, nil, nil, nil, nil, nil, nil]

Niestety, jak za chwilö zobaczymy, prawdopodobnie poszukujemy rozwiñzania nie tego pro-
blemu.

Dyskusja

WiökszoĈè systemów weryfikacji adresów e-mailowych opiera swe funkcjonowanie na naiw-
nych wyraĔeniach regularnych, podobnych do prezentowanych powyĔej. Niestety, wyraĔe-
nia takie bywajñ czösto zbyt rygorystyczne, wskutek czego zdarza siö, Ĕe poprawny adres zo-
staje odrzucony. Jest to powszechna przyczyna frustracji uĔytkowników posäugujñcych siö
nietypowymi adresami w rodzaju joe(and-mary)@example.museum oraz uĔytkowników wyko-
rzystujñcych w swych adresach specyficzne cechy systemu e-mail (joe+ruby-mail@example.com).
Prezentowane powyĔej wyraĔenia regularne cierpiñ na dokäadnie odwrotnñ przypadäoĈè —
nie kwestionujñc nigdy adresów poprawnych, akceptujñ niektóre niepoprawne.

Dlaczego wiöc nie stworzyè (publicznie znanego) wyraĔenia regularnego, które z zadaniem
weryfikacji adresów e-mailowych poradzi sobie zawsze? OtóĔ dlatego, Ĕe byè moĔe wyraĔe-
nie takie wcale nie istnieje — definicji skäadni adresu e-mailowego zarzuciè moĔna wszystko,
tylko nie prostotö. Haker jözyka Perl, Paul Warren, w stworzonym przez siebie module

Mail

::RFC822:Address

zdefiniowaä wyraĔenie regularne skäadajñce siö z 6343 znaków, lecz na-

wet ono wymaga przetwarzania wstöpnego dla absolutnie (w zamierzeniu) bezbäödnej wery-
fikacji adresu. WyraĔenia tego moĔna uĔyè bez zmian w jözyku Ruby — zainteresowani Czy-
telnicy mogñ znaleĒè je w katalogu

Mail-RFC822-Address-0.3

na CD-ROM-ie doäñczonym

do niniejszej ksiñĔki.

Weryfikuj prawdziwo

ļë, nie poprawnoļë

Jednak najbardziej nawet wyszukane wyraĔenie regularne nie potrafi zapewniè nic wiöcej niĔ
tylko weryfikacjö skäadniowej poprawnoĈci adresu. PoprawnoĈè skäadniowa nie oznacza wcale,
Ĕe dany adres jest istniejñcym adresem.

Przy wpisywaniu adresu äatwo moĔna siö pomyliè, wskutek czego poprawny adres zamienia
siö w (takĔe poprawny) adres kogo innego (joe@example.com). Adres !@ jest skäadniowo popraw-
ny, lecz nikt na Ĉwiecie go nie uĔywa. Nawet zbiór domen najwyĔszego poziomu (top-level
domains
) teĔ nie jest ustalony i jako taki nie moĔe byè przedmiotem weryfikacji w oparciu o sta-
tycznñ listö. Reasumujñc — weryfikacja poprawnoĈci skäadniowej adresu e-mail jest tylko ma-
äñ czöĈciñ rozwiñzania rzeczywistego problemu.

background image

1.19. Weryfikacja poprawno

ļci adresów e-mailowych

_

63

Jedynym sposobem stwierdzenia poprawnoĈci adresu jest udane wysäanie listu na ów adres.
O tym, czy adres ten jest wäaĈciwy, moĔemy przekonaè siö dopiero po otrzymaniu odpowie-
dzi od adresata. Jak widaè, nietrudny na pozór problem wymaga wcale niemaäo zachodu przy
tworzeniu aplikacji.

Nie tak dawno jeszcze adres e-mailowy uĔytkownika zwiñzany byä nierozerwalnie w jego
toĔsamoĈciñ w sieci, bo przydzielany byä przez dostawcö internetowego (ISP). Przestaäo tak
byè w dobie poczty webowej, gdzie kaĔdy uĔytkownik moĔe sobie przydzieliè tyle adresów,
ile tylko zechce. W efekcie weryfikacja poprawnoĈci adresów nie jest w stanie zapobiec ani
dublowaniu kont, ani teĔ antyspoäecznym zachowaniom w sieci (i wñtpliwe jest, czy kiedy-
kolwiek mogäa).

Nie oznacza to bynajmniej, Ĕe weryfikacja skäadni adresu e-mailowego jest caäkowicie bezu-
Ĕyteczna, albo Ĕe nie jest problemem niezamierzone znieksztaäcenie wpisywanego adresu
(„literówka”). Aby usprawniè pracö uĔytkownika aplikacji wpisujñcego adres e-mailowy, bez
obawy o kwestionowanie poprawnych adresów, moĔesz zrobiè trzy nastöpujñce rzeczy oprócz
weryfikacji adresu w oparciu o prezentowane wcze

Ĉniej wyraĔenia regularne:

1.

UĔyj drugiego, naiwnego i bardziej restrykcyjnego wyraĔenia regularnego, lecz w przypad-
ku stwierdzenia niepoprawnoĈci adresu ogranicz siö do wypisania komunikatu ostrze-
gawczego, nie blokujñc uĔytkownikowi moĔliwoĈci uĔycia tego adresu. Nie jest to tak
uĔyteczne, jak mogäoby siö wydawaè, bo adres bödñcy wynikiem pomyäki literowej jest
czösto takĔe adresem poprawnym skäadniowo (po prostu jedna litera zamieniona zostaje
na innñ).

def probably_valid?(email)
valid = '[A-Za-z\d.+-]+' # Znaki powszechnie spotykane w adresach
(email =~ /#{valid}@#{valid}\.#{valid}/) == 0
end

# Wyniki weryfikacji zgodne z oczekiwaniami
probably_valid? 'joe@example.com' # => true
probably_valid? 'joe+ruby-mail@example.com' # => true
probably_valid? 'joe.bloggs@mail.example.com' # => true
probably_valid? 'joe@examplecom' # => false
probably_valid? 'joe+ruby-mail@example.com' # => true
probably_valid? 'joe@localhost' # => false

# Adres poprawny, lecz kwestionowany przez metod

Ă probably_valid?

probably_valid? 'joe(and-mary)@example.museum' # => false

# Adres sk

Īadniowo poprawny, lecz ewidentnie bĪĂdny

probably_valid? 'joe@example.cpm' # => true

2.

Wydziel adres serwera z adresu e-mailowego (np. example.com) i sprawdĒ (za pomocñ
DNS), czy serwer ten zapewnia obsäugö poczty (tzn. czy da siö z niego odczytaè rekord
MX DNS). PoniĔszy fragment kodu zdolny jest wychwyciè wiökszoĈè pomyäek w zapisie
adresu serwera, co jednak nie chroni przed podaniem nazwy nieistniejñcego uĔytkowni-
ka. Ponadto ze wzglödu na zäoĔonoĈè samego dokumentu RFC822 nie moĔna zagwaran-
towaè, Ĕe analiza adresu serwera zawsze bödzie przeprowadzona bezbäödnie:

require 'resolv'
def valid_email_host?(email)
hostname = email[(email =~ /@/)+1..email.length]
valid = true

background image

Czytaj dalej...

64

_

Rozdzia

ĥ 1. Ĥaħcuchy

begin
Resolv::DNS.new.getresource(hostname, Resolv::DNS::Resource::IN::MX)
rescue Resolv::ResolvError
valid = false
end
return valid
end

# example.com jest adresem rzeczywistej domeny, lecz jej serwer
# nie obs

Īuguje poczty.

valid_email_host?('joe@example.com') # => false

# lcqkxjvoem.mil nie jest adresem istniej

îcej domeny.

valid_email_host?('joe@lcqkxjvoem.mil') # => false

# domena oreilly.com istnieje i jej serwer zapewnia obs

ĪugĂ poczty, jednakše

# uzytkownik 'joe' mo

še nie byð zdefiniowany na tym serwerze.

valid_email_host?('joe@oreilly.com') # => true

3.

WyĈlij list na adres wpisany przez uĔytkownika aplikacji, z proĈbñ do adresata o potwier-
dzenie poprawnoĈci adresu. Aby uäatwiè adresatowi zadanie, moĔna w treĈci listu umie-
Ĉciè stosowny URL (gotowy do klikniöcia) z odpowiednim komentarzem. Jest to jedyny
sposób upewnienia siö, Ĕe uĔyto wäaĈciwego adresu. Powrócimy do tej kwestii w recep-
turach 14.5 i 15.19.

Mimo iĔ rozwiñzanie to stanowczo podnosi poprzeczkö wymagaþ wobec programisty
tworzñcego aplikacjö, moĔe okazaè siö nieskuteczne z bardzo prostej przyczyny — roz-
maitych sposobów walki z niechcianñ pocztñ. UĔytkownik moĔe zdefiniowaè filtr, który
zaklasyfikuje wspomnianñ wiadomoĈè jako niechcianñ (junk), bñdĒ teĔ generalnie odrzu-
caè wszelkñ pocztö pochodzñcñ z nieznanego Ēródäa. JeĔeli jednak weryfikacja adresów
e-mail nie jest dla aplikacji zagadnieniem krytycznym, opisywane sposoby tej weryfikacji
powinny okazaè siö wystarczajñce.

Patrz tak

że

x

Receptura 14.5, „Wysyäanie poczty elektronicznej”.

x

Receptura 15.19, „Przesyäanie wiadomoĈci pocztowych za pomocñ aplikacji Rails”.

x

Wspomniane wczeĈniej kolosalne wyraĔenie regularne autorstwa Paula Warrena dostöp-
ne jest do pobrania pod adresem http://search.cpan.org/~pdwarren/Mail-RFC822-Address-0.3/
Address.pm
.

1.20. Klasyfikacja tekstu

za pomoc

é analizatora bayesowskiego

Problem

Majñc dany fragment tekstu, chcemy dokonaè jego klasyfikacji — na przykäad zdecydowaè,
czy otrzymany list moĔna potraktowaè jako spam, bñdĒ czy zawarty w liĈcie dowcip jest na-
prawdö Ĉmieszny.


Wyszukiwarka

Podobne podstrony:
informatyka php i jquery receptury vijay joshi ebook
Ruby Receptury rubyre
Ruby Receptury rubyre
informatyka skrypty powloki systemu linux receptury sarath lakshman ebook
informatyka android receptury ian f darwin ebook
Ruby Receptury rubyre
informatyka testowanie bezpieczenstwa aplikacji internetowych receptury paco hope ebook
informatyka ruby wprowadzenie michael fitzgerald ebook
informatyka access analiza danych receptury ken bluttman ebook
informatyka cakephp 1 3 programowanie aplikacji receptury mariano iglesias ebook
inne nawiedzil nas diabel lucia ebook
Szlachetne marzenia i inne utwory Teresa Jadwiga Papi ebook
informatyka sieci linux receptury carla schroder ebook
informatyka tworzenie aplikacji dla ios we flashu receptury christopher caleb ebook
Ruby Receptury
informatyka html5 canvas receptury eric rowell ebook
Ruby Receptury
informatyka ruby programowanie david flanagan ebook
informatyka wyrazenia regularne receptury jan goyvaerts ebook

więcej podobnych podstron