Operacje wejścia-wyjścia w Rubim

Posted by Jacek Galanciak on

W tej części tutoriala nauczymy się korzystać z wejścia i wyjścia na poziomie daleko wykraczającym poza programy “Hello World!”…

Kernel

Moduł Kernel dostarcza nam podstawowe metody związane z obsługą wejścia i wyjścia. Oto i one:

putc - wyświetla znak o kodzie ASCII podanym za argument; zwrócmy uwagę, że dodanie do kodu wielokrotności liczby 256 wyświetli taki sam znak

(97..99).each do |c|
  putc c
end

(354..356).each do |c|
  putc c
end

Co na wyjściu da nam:

abcbcd

puts - wyświetla tekst podany za argument, dodając na koniec znak końca linii; jeśli argumentem jest tablica, wyświetla każdy element, oddzielając je separatorem linii; jeżeli którykolwiek z elementów zawiera już separatory, metoda usuwa jeden z nich

puts "Hello World!"
puts ["To\n", "tylko\n\n", "test"];

Wyjście:

Hello World!
To
tylko

test

print - wypisuje obiekt (lub obiekty) na standardowe wyjście; odziela każdy z nich łańcuchem określonym w zmiennej magicznej $, (która domyślnie ma wartość nil); dodaje na koniec Stringa wynikowego zawartość $\ (domyślnie nil)

print "a", 1, [2, '4'], "b", "\n"

$, = ", "
$\ = "\n"
print "a", 1, [2, '4'], "b"

Wyjście:

a124b
a, 1, 2, 4, b

Zwróćmy uwagę, że po ustawieniu zmiennej $\ nie musimy dorzucać znaku \n na koniec sekwencji obiektów.

printf - działa tak samo, jak standardowa funkcja języka C o takiej samej nazwie

open - otwiera plik (jeśli parametr nie rozpoczyna się znakiem |) lub potok podprocesu.

Poniższy program na dwa sposoby realizuje odczyt pierwszej linii pliku:

# 1
f = open "C:\\plik.txt"
puts f.gets

# 2
open "C:\\plik.txt" do |f|
  puts f.gets
end

Rozpoczynając parametr metody open znakiem potoku, otwieramy podproces z możliwością śledzenia jego wyjścia oraz zapisu danych na jego wejście. Napiszemy teraz program, który wywołuje systemowe polecenie ls (lub jakiekolwiek inne) i wyświetli wynik jego działania na ekranie.

open("|ls") do |f|
  while ln = f.gets do puts ln end
end

Wyniku działania, ze względów bezpieczeństwa, nie podam :-).

Operujemy na plikach

Można to robić w sposób klasyczny lub blokowy:

# 1
f = File.open "C:\\plik.txt"
while ln = f.gets do puts ln end
f.close

# 2
File.open "C:\\plik.txt" do |f|
  while ln = f.gets do puts ln end
end

Przewaga tego drugiego jest dokładnie widoczna, gdy zwracany jest wyjątek. Pierwszy sposób może wtedy ominąć metodę zamykającą plik. Stosując podejście blokowe, możemy kodować (i spać) spokojnie.

Zapis do pliku realizujemy za pomocą metod puts, print itp. obiektu klasy File, które działają tak samo, jak ich imiennicy z modułu Kernel.

Używamy iteratorów

Iteratory to potężne narzędzie (pisałem o nich w poprzedniej części tutoriala), zaimplementowano takowe również do operacji wejścia i wyjścia.

Stwórzmy przykładowy plik o następującej zawartości:

Pierwsza linia
Druga linia
A to jest trzecia

Napiszemy teraz program, który, z wykorzystaniem iteratorów, odczyta kolejno wszystkie znaki (oddzielając każdy minusem) i każdą linię pliku, informując o odczytanym wierszu.

File.open "C:\\plik.txt" do |f|
  f.each_byte { |c| putc c; print "-" }
end

puts # Przejście do nowego wiersza

File.open "C:\\plik.txt" do |f|
  f.each_line { |ln| puts "Napotkałem na wiersz: #{ln}" }
end

Output:

P-i-e-r-w-s-z-a- -l-i-n-i-a--
-D-r-u-g-a- -l-i-n-i-a--
-A- -t-o- -j-e-s-t- -t-r-z-e-c-i-a-
Napotkałem na wiersz: Pierwsza linia
Napotkałem na wiersz: Druga linia
Napotkałem na wiersz: A to jest trzecia

Nic nie stoi na przeszkodzie, byśmy sami wybrali sobie separator linii, gdy domyślny (\n) nam nie odpowiada.

File.open "C:\\plik.txt" do |f|
  f.each_line(" ") { |ln| puts ln }
end
Pierwsza 
linia
Druga 
linia
A 
to 
jest 
trzecia

Jak widać w tym przypadku, za separator linii obraliśmy sobie spację.

Aby URL plikiem się stał

To żaden problem. Wystarczy dodać jedną linię kodu, aby można było traktować adresy internetowe jak zwykłe pliki. Poniższy program wyświetli kod HTML wyszukiwarki Google.

require 'open-uri'

open("http://google.com") do |f|
  puts f.readlines
end

Prawda, że piękne?