Zanurkuj w Pythonie/Standardowy strumień wejścia, wyjścia i błędów: Różnice pomiędzy wersjami

Usunięta treść Dodana treść
zamiana {{Nawigacja|Python|->{{Nawigacja|Zanurkuj w Pythonie| [[Python/->[[../
Piotr (dyskusja | edycje)
poprawki
Linia 1:
{{Podświetl|py}}
== Standardowy strumień wejścia, wyjścia i błędów==
 
Użytkownicy Uniksa są już prawdopodobnie zapoznani z koncepcją standardowego wejścia, standardowego wyjścia i standardowego strumienia błędów. Ten podrozdział jest dla pozostałych osób.
 
Standardowe wyjście i strumień błędów (powszechnie używana skrócona forma to ''stdout'' i ''stderr'') są strumieniami danych wbudowanymi do każdego systemu Unix. Kiedy coś wypisujeszwypisujemy, idzie to do strumienia ''stdout''; kiedy wystąpi błąd w twoim programie, a program wypisze informacje pomocne przy debugowaniu (jak traceback w Pythonie), to wszystko pójdzie do strumienia <tt>''stderr</tt>''. Te dwa strumienie są zwykle połączone z oknem terminala, na którym pracujeszpracujemy, więc jeżeli program coś wypisuje, zobaczyszzobaczymy to na wyjściu, a kiedy program spowoduje błąd, zobaczymy informacje debugujące. (Jeśli pracujeszpracujemy w systemie z okienkowym IDE Pythona, ''stdout'' i ''stderr'' domyślnie będą połączone z twoim"interaktywnym „interaktywnym oknem”oknem".)
 
'''Przykład 10.8. Wprowadzenie do ''stdout'' i ''stderr'''''
 
{{Python/Przykład
'''Przykład |10.8. |Wprowadzenie do ''stdout'' i ''stderr'''''
|tekst=
>>> for i in range(3):
... print 'Dive inNurkujemy' #(1)
{{samp|Nurkujemy
Dive in
Nurkujemy
Dive in
Nurkujemy}}
Dive in
>>> import sys
>>> for i in range(3):
... sys.stdout.write('Dive inNurkujemy') #(2)
{{samp|NurkujemyNurkujemyNurkujemy}}
Dive inDive inDive in
>>> for i in range(3):
... sys.stderr.write('Dive inNurkujemy') #(3)
{{samp|NurkujemyNurkujemyNurkujemy}}
Dive inDive inDive in
 
# Jak zobaczyliśmy w Przykładzie[[../Pętla for#przy=6.9|przykładzie 6.9, “Prosty"Prosty licznik”licznik"]], możemy wykorzystać wbudowaną funkcje <ttcode>range</ttcode>, aby zbudować prostą pętlę licznikową, która powtarza pewną operację określoną liczbę razy.
# <ttcode>stdout</ttcode> jest ''obiektem pliko-podobnym''; wywołując jego funkcję <ttcode>write</ttcode> będziemy wypisywać na wyjście napis, który przekazaliśmy. W rzeczywistość, to właśnie funkcja <ttcode>print</ttcode> naprawdę robi; dodaje ona znak nowej linii do wypisywanego napisu, a następnie wywołuje <ttcode>sys.stdout.write</ttcode>.
# W tym prostym przypadku <ttcode>stdout</ttcode> i <ttcode>stderr</ttcode> wysyłają wyjście do tego samego miejsca: do IDE Pythona (jeśli jesteśjesteśmy w nim) lub do terminala (jeśli maszmamy uruchomionego Pythona z linii poleceń). Podobnie jak <ttcode>stdout</ttcode>, <ttcode>stderr</ttcode> nie dodaje znaku nowej linii za ciebienas; jeśli chceszchcemy, aby ten znak został dodany, musiszmusimy to zrobić samemusami.
}}
 
Zarówno <tt>stdout</tt> i <tt>stderr</tt> są ''obiektami pliko-podobnymi'', a które omawialiśmy w Podrozdziale 10.1, “Abstrakcyjne źródła wejścia”, lecz te są tylko do zapisu. Nie posiadają one metody <tt>read</tt>, tylko <tt>write</tt>. Jednak nadal są one ''obiektami pliko-podobnymi'' i możemy do nich przypisać inny obiekt pliku lub ''obiekt pliko-podobny'', aby przekierować ich wyjście.
 
Zarówno <ttcode>stdout</ttcode> i <ttcode>stderr</ttcode> są ''obiektami pliko-podobnymi'', a które omawialiśmy w Podrozdziale[[../Abstrakcyjne źródła wejścia|podrozdziale 10.1, “Abstrakcyjne"Abstrakcyjne źródła wejścia”wejścia"]], lecz te są tylko do zapisu. Nie posiadają one metody <ttcode>read</ttcode>, tylko <ttcode>write</ttcode>. Jednak nadal są one ''obiektami pliko-podobnymi'' i możemy do nich przypisać inny obiekt pliku lub ''obiekt pliko-podobny'', aby przekierować ich wyjście.
'''Przykład 10.9. Przekierowywanie wyjścia'''
 
{{Python/Przykład
'''Przykład |10.9. |Przekierowywanie wyjścia'''
|tekst=
[you@localhost kgp]$ python stdout.py
{{samp|Nurkujemy}}
Dive in
[you@localhost kgp]$ cat out.log
{{samp|Ta wiadomość będzie logowana i nie zostanie wypisana na wyjście}}
This message will be logged instead of displayed
 
(W Windowsie, możeszmożemy wykorzystać polecenie <tt>type</tt>, zamiast <tt>cat</tt>, aby wyświetlić zawartość pliku.)
 
<pre>
Jeśli jeszcze tego nie zrobiłeś, możesz pobrać [http://diveintopython.org/download/diveintopython-examples-5.4.zip ten i inne przykłady] użyte w tej książce.
#-*- coding: utf-8 -*-
 
#stdout.py
import sys
print 'Dive inNurkujemy' #(1)
saveout = sys.stdout #(2)
fsock = open('out.log', 'w') #(3)
sys.stdout = fsock #(4)
print 'ThisTa messagewiadomość willbędzie belogowana loggedi insteadnie ofzostanie displayedwypisana na wyjście' #(5)
sys.stdout = saveout #(6)
fsock.close() #(7)
</pre>
 
# To zostanie wypisane w interaktywnym oknie IDE (lub w terminalu, jeśli skrypt został uruchomiony z linii poleceń).
# Zawsze, zanim przekierujemy standardowe wyjście, przypisujemy gdzieś <ttcode>stdout</ttcode>, dzięki temu, będziemy potem mogli do niego normalnie wrócić.
# Otwieramy plik do zapisu. Jeśli plik nie istnieje, zostanie utworzony. Jeśli istnieje, zostanie nadpisany.
# Całe późniejsze wyjście zostanie przekierowane do pliku, który właśnie otworzyliśmy.
# Zostanie to wypisane tylko do pliku <tt>out.log</tt>; nie będzie widoczne w oknie IDE lub w terminalu.
# Przywracamy <ttcode>stdout</ttcode> do początkowej, oryginalnej postaci.
# Zamykamy plik <tt>out.log</tt>.
}}
 
Dodajmy, że w wypisywanym łańcuchu znaków użyliśmy polskich znaków, a ponieważ nie skorzystaliśmy z [[../Łańcuchy znaków i unikod|unikodu]], więc napis ten zostanie wypisany w takiej samej postaci, w jakiej został zapisany w pliku Pythona (czyli wiadomość zostanie zapisana w kodowaniu <tt>utf-8</tt>). Gdybyśmy skorzystali z unikodu, musielibyśmy wyraźnie zakodować ten napis do jakiegoś kodowania za pomocą [[../Praca z unikodem#przy-3.36|metody <code>encode</code>]], ponieważ Python nie wie, z jakiego kodowania chce korzystać utworzony przez nas plik (plik <tt>out.log</tt> przypisany do zmiennej <code>stdout</code>).
Przekierowywanie standardowego strumienia błędów (<tt>stderr</tt>) działa w ten sam sposób, wykorzystując <tt>sys.stderr</tt>, zamiast <tt>sys.stdout</tt>.
 
Przekierowywanie standardowego strumienia błędów (<tt>''stderr</tt>'') działa w ten sam sposób, wykorzystując <ttcode>sys.stderr</ttcode>, zamiast <ttcode>sys.stdout</ttcode>.
'''Przykład 10.10. Przekierowywanie informacji o błędach'''
 
{{Python/Przykład
'''Przykład |10.10. |Przekierowywanie informacji o błędach'''
|tekst=
[you@localhost kgp]$ python stderr.py
[you@localhost kgp]$ cat error.log
{{samp|<nowiki>Traceback (most recent line last):
File "stderr.py", line 56, in ?
raise Exception, ('thisten errorbłąd willbędzie be logged'logowany)
Exception: thisten errorbłąd willbędzie be loggedlogowany</nowiki>}}
 
<pre>
Jeśli jeszcze tego nie zrobiłeś, możesz pobrać [http://diveintopython.org/download/diveintopython-examples-5.4.zip ten i inne przykłady] użyte w tej książce.
#stderr.py
 
#-*- coding: utf-8 -*-
#stderr.py
import sys
fsock = open('error.log', 'w') #(1)
sys.stderr = fsock #(2)
raise Exception, ('this errorten willbłąd bebędzie loggedlogowany') #(3) (4)
</pre>
 
# Otwieramy plik <tt>error.log</tt>, gdzie chcemy przechowywać informacje debugujące.
# Przekierowujemy standardowy strumień błędów, dzięki przypisaniu obiektu nowo otwartego pliku do <ttcode>sys.stderr</ttcode>.
# Rzucamy wyjątek. Zauważmy, że na ekranie wyjściowym nic nie zostanie wypisane. Wszystkie informacje ''traceback'' zostały zapisane w <tt>error.log</tt>.
# Zauważmy także, że nie zamknęliśmy jawnie pliku <tt>error.log</tt>, a nawet nie przypisaliśmy do <ttcode>sys.stderr</ttcode> jego pierwotnej wartości. To jest wspaniałe, że kiedy program się rozwali (z powodu wyjątku), Python wyczyści i zamknie wszystkie pliki za nas. Nie ma żadnej różnicy, czy <ttcode>stderr</ttcode> zostanie przywrócony, czy też nie, ponieważ program się rozwala, a Python kończy działanie. Przywrócenie wartości do oryginalnej, jest bardziej ważne dla <ttcode>stdout</ttcode>, jeśli zamierzasz później wykonywać jakieś inne operacje w tym samym skrypcie.
}}
 
Ponieważ powszechnie wypisuje się informacje o błędach na standardowy strumień błędów, Python posiada skrótową składnie, która można wykorzystać do bezpośredniego przekierowywania wyjścia.
 
{{Python/Przykład
'''Przykład |10.11. |Wypisywanie do <ttcode>stderr</ttcode>'''
 
|tekst=
>>> print 'enteringwchodzimy functiondo funkcji'
entering function
{{samp|wchodzimy do funkcji}}
>>> import sys
>>> print >> sys.stderr, 'enteringwchodzimy functiondo funkcji' #(1)
wchodzimy do funkcji
entering function
 
# Ta skrótowa składnia wyrażenia <ttcode>print</ttcode> może być wykorzystywana do pisania do dowolnego, otwartego pliku, lub do ''obiektu pliko-podobnego''. W tym przypadku, możemy przekierować pojedynczą instrukcję <ttcode>print</ttcode> do <ttcode>stderr</ttcode> bez wpływu na następne instrukcje <ttcode>print</ttcode>.
}}
 
Z innej strony, standardowe wejścia jest obiektem pliku tylko do odczytu i reprezentuje dane przechodzące z niektórych wcześniejszych programów. Prawdopodobnie nie jest to zrozumiałe dla klasycznych użytkowników Mac OS-a lub nawet dla użytkowników Windows, którzy nie mieli za wiele do czynienia z linią poleceń MS-DOS-a. Działa to w ten sposób, że konstruujemy ciąg poleceń w jednej linii, w taki sposób, że to co jeden program wypisuje na wyjście, następny w tym ciągu traktuje jako wejście. Pierwszy program prosto wypisuje wszystko na standardowe wyjście (bez korzystania ze specjalnych przekierowań, wykorzystuje normalną instrukcję <ttcode>print</ttcode> itp.), a następny program czyta ze standardowego wejścia, a system operacyjny udostępnia połączenie pomiędzy wyjściem pierwszego programu, a wyjściem kolejnego.
 
'''{{Python/Przykład
|10.12. |Ciąg poleceń'''
|tekst=
<nowiki>
<span>[you@localhost kgp]$ python kgp.py -g binary.xml #(1)
{{samp|01100111}}
[you@localhost kgp]$ cat binary.xml #(2)
{{samp|<nowiki><?xml version="1.0"?>
<!DOCTYPE grammar PUBLIC "-//diveintopython.org//DTD Kant Generator Pro v1.0//EN" "kgp.dtd">
<grammar>
<ref id="bit">
<p>0</p>
<p>1</p>
</ref>
<ref id="byte">
<p><xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/>\
<xref id="bit"/><xref id="bit"/><xref id="bit"/><xref id="bit"/></p>
</ref>
</grammar></nowiki>}}
<nowiki>[you@localhost kgp]$ cat binary.xml | python kgp.py -g - #(3) (4)</nowiki>
{{samp|10110001}}</span>
</nowiki>
 
# Jak zobaczyliśmy w [[../Przetwarzanie XML-a#przy-9.4|podrozdziale 9.1, “Nurkujemy”"Nurkujemy"]], polecenie to wyświetli ciąg ośmiu przypadkowych bitów, <ttcode>0</ttcode> lubi <ttcode>1</ttcode>.
# Dzięki temu po prostu wypiszemy całą zawartość pliku <tt>binary.xml</tt>. (Użytkownicy Windowsa powinni wykorzystać polecenie <tt>type</tt> zamiast <tt>cat</tt>.)
# Polecenie to wypisuje zawartość pliku <tt>{{Python/Src|kgp/binary.xml</tt>|binary.xml}}, ale znak "<nowiki>|</nowiki>" (ang. ''pipe''), oznacza, że standardowe wyjście nie zostanie wypisana na ekran. Zamiast tego, zawartość standardowego wyjścia zostanie wykorzystane jako standardowe wejście następnego programu, który w tym przypadku jest skryptem Pythona.
# Zamiast określać modułu (np. <tt>binary.xml</tt>), dajemy "-", który każe naszemu skryptowi wczytać gramatykę ze standardowego wejścia, zamiast z określonego pliku na dysku. (Więcej o tym, w jaki sposób to się dzieje w następnym przykładzie.) Zatem efekt będzie taki sam, jak w pierwszym poleceniu, gdzie bezpośrednio określamy plik gramatyki, ale tutaj zwróćmy uwagę na rozszerzone możliwości. Zamiast wywoływać <tt>cat binary.xml</tt>, moglibyśmy uruchomić skrypt, który by dynamicznie generował gramatykę, a następnie mógłby ją doprowadzić do naszego skryptu. Dane mogłyby przyjść skądkolwiek: z bazy danych, innego skryptu generującego gramatykę lub jeszcze inaczej. Zaletą tego jest to, że nie musimy zmieniać w żaden sposób <tt>{{Python/Src|kgp/kgp.py</tt>|kgp.py}}, aby dołączyć jakąś funkcjonalność. Jedynie, co potrzebujemy, to możliwość wczytania gramatyki ze standardowego wejścia, a całą logikę dodatkowej funkcjonalności możemy rozdzielić wewnątrz innego programu.
}}
 
Więc w jaki sposób skrypt "wie", żeby czytać ze standardowego wejścia, gdy plik gramatyki to "-"? To nie jest żadna magia; to tylko właśnie prosty kod.
 
{{Python/Przykład
'''Przykład |10.13. |Czytanie ze standardowego wejścia w <tt>{{Python/Src|kgp/kgp.py</tt>'''|kgp.py}}
 
|tekst=
 
<pre>
def openAnything(source):
def if openAnything(source == "-"): #(1)
if source == "-": import sys #(1)
returnimport sys.stdin
return sys.stdin
# try to open with urllib (if source is http, ftp, or file URL)
import urllib
try:
[... ciach ...]
 
[... ciach ...]
# Jest to funkcja <tt>openAnything</tt> z <tt>toolbox.py</tt>, którą wcześniej badaliśmy w podrozdziale 10.1, "Abstrakcyjne źródła wejścia”. Wszystko, co musimy zrobić, to dodanie trzech linii kodu na początku, aby sprawdzić, czym źródłem nie jest "-"; jeśli tak, to zwracamy <tt>sys.stdin</tt>. Naprawdę, to tylko tyle! Pamiętasz, <tt>stdin</tt> jest ''obiektem pliko-podobnym'' z metodą read, więc pozostałą część kodu (w <tt>kgp.py</tt>, gdzie wywołujemy funkcję <tt>openAnything</tt>) w żaden sposób nie zmieniamy.
</pre>
# Jest to funkcja <ttcode>openAnything</ttcode> z <tt>{{Python/Src|kgp/toolbox.py</tt>|toolbox.py}}, którą wcześniej badaliśmy w [[../Abstrakcyjne źródła wejścia#przy-10.6|podrozdziale 10.1, "Abstrakcyjne źródła wejścia”]]. Wszystko, co musimy zrobić, to dodanie trzech linii kodu na początku, aby sprawdzić, czymczy źródłem nie jest <code>"-"</code>; jeśli tak, to zwracamy <ttcode>sys.stdin</ttcode>. Naprawdę, to tylko tyle! Pamiętasz, <ttcode>stdin</ttcode> jest ''obiektem pliko-podobnym'' z metodą <code>read</code>, więc pozostałą część kodu (w <tt>{{Python/Src|kgp/kgp.py</tt>|kgp.py}}, gdzie wywołujemy funkcję <ttcode>openAnything</ttcode>) w żaden sposób nie zmieniamy.
}}
 
<noinclude>
{{Nawigacja|Zanurkuj w Pythonie|
[[../Abstrakcyjne źródła wejścia|Abstrakcyjne źródła wejścia/]]|
[[../Buforowanie odszukanego węzła|Buforowanie odszukanego węzła/]]|
}}
{{Podświetl|py}}
</noinclude>