logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Python3: Odczyt danych z os.popen() w innym kodowaniu niż UTF-8

_jta_ 11 Sty 2025 01:38 147 3
REKLAMA
  • #1 21387379
    _jta_
    Specjalista elektronik
    Posty: 48809
    Pomógł: 3198
    Ocena: 4184
    os.popen() służy do wywołania programu i odczytania tego, co ten program wypisze (można też odwrotnie: żeby wpisywać to, co program będzie czytał); no i jest z tym problem, bo zdarza się, że ten program pisze coś w kodowaniu innym, niż UTF-8, co powoduje błąd przy czytaniu. Przykładowo, wywołany program wypisuje linię kończącą się znakami 0xfe, 0xff, 0x0a. Jak to czytać?
  • REKLAMA
  • #2 21387497
    kloszi
    Poziom 21  
    Posty: 545
    Pomógł: 34
    Ocena: 87
    @ElektrodaBot os.popen() służy do wywołania programu i odczytania tego, co ten program wypisze (można też odwrotnie: żeby wpisywać to, co program będzie czytał); no i jest z tym problem, bo zdarza się, że ten program pisze coś w kodowaniu innym, niż UTF-8, co powoduje błąd przy czytaniu. Przykładowo, wywołany program wypisuje linię kończącą się znakami 0xfe, 0xff, 0x0a. Jak to czytać?
  • REKLAMA
  • #3 21387507
    ElektrodaBot
    AI od OpenAI ChatGPT4
    Posty: 8743
    Pomógł: 352
    Ocena: 923
    kloszi napisał:
    @ElektrodaBot os.popen() służy do wywołania programu i odczytania tego, co ten program wypisze (można też odwrotnie: żeby wpisywać to, co program będzie czytał); no i jest z tym problem, bo zdarza się, że ten program pisze coś w kodowaniu innym, niż UTF-8, co powoduje błąd przy czytaniu. Przykładowo, wywołany program wypisuje linię kończącą się znakami 0xfe, 0xff, 0x0a. Jak to czytać?
    Wstępna ocena dostępnych informacji
    - Pytanie dotyczy problemu z wywoływaniem zewnętrznego programu za pomocą funkcji os.popen() (lub ogólnie w Pythonie – także możliwe z użyciem subprocess).
    - Zewnętrzny program wypisuje dane, które zawierają ciąg bajtów 0xFE, 0xFF, 0x0A. Najprawdopodobniej oznacza to, że dane korzystają z kodowania UTF-16 Big Endian (0xFE 0xFF to tzw. BOM – Byte Order Mark).
    - Python domyślnie oczekuje (zwłaszcza przy pracy z os.popen() w trybie tekstowym) innego kodowania, zazwyczaj UTF-8, co prowadzi do błędów dekodowania (m.in. UnicodeDecodeError).

    Bezpośrednia odpowiedź na pytanie
    Aby poprawnie odczytać dane kończące się sekwencją 0xFE, 0xFF, 0x0A w Pythonie, należy:
    1. Otworzyć wyjście programu w trybie binarnym (np. przez os.popen(..., 'rb') lub lepiej subprocess.Popen(..., stdout=PIPE) bez parametru text=True).
    2. Zdekodować uzyskane bajty przy użyciu właściwego kodowania, np. utf-16-be, jeśli rzeczywiście występuje BOM 0xFE 0xFF.
    3. Alternatywnie, jeśli używa się subprocess, można określić parametr encoding="utf-16-be" (lub text=True i encoding="..."), co automatycznie zrobi dekodowanie.

    Szczegółowa analiza problemu
    Poniżej omawiam poszczególne aspekty problemu i najważniejsze kwestie inżynierskie:

    1. Znaczenie 0xFE, 0xFF
    - 0xFE, 0xFF występują na początku tekstu jako BOM (Byte Order Mark) dla UTF-16 Big Endian.
    - Jeśli BOM występuje na końcu wiersza, może to oznaczać, że program generuje osobną linię, gdzie BOM jest identyfikatorem kodowania.

    2. Domyślne zachowanie os.popen()
    - W Pythonie 3, otwarcie strumienia w sposób „tekstowy” (bez 'b') sprawia, że Python próbuje dekodować dane wg standardowego kodowania systemowego albo UTF-8.
    - Gdy w danych pojawiają się bajty specyficzne dla UTF-16 (np. 0xFE, 0xFF), Python nie rozpoznaje ich poprawnie jako część tekstu w UTF-8. Stąd wyjątki typu UnicodeDecodeError.

    3. Proponowane rozwiązanie
    - Odczyt w trybie binarnym i samodzielne dekodowanie:
    » Przechwycić bajty za pomocą:
    with os.popen('polecenie', 'rb') as pipe:
    raw_data = pipe.read()
    » Następnie zdekodować:
    text_data = raw_data.decode('utf-16')
    » Jeśli BOM to 0xFE, 0xFF, to Python automatycznie rozpozna kolejność bajtów (Big Endian) przy kodowaniu 'utf-16'.
    - Użycie subprocess + encoding:
    » subprocess.Popen([...], stdout=subprocess.PIPE, text=True, encoding='utf-16-be')
    » Wówczas Python z automatu od razu dekoduje bajty w podanym kodowaniu.
    - Wykrywanie kodowania (chardet, charset_normalizer) – gdy kodowanie nie jest pewne. Nie jest to zawsze w 100% niezawodne, ale w wielu sytuacjach może pomóc wykryć np. UTF-16 vs. CP1252.

    4. Dlaczego należy preferować subprocess zamiast os.popen()?
    - `os.popen()` jest starszym API; `subprocess` daje większą kontrolę, np. parametry encoding, errors, timeout, itp.
    - `subprocess.Popen()` zawsze pozwala odczytać surowe bajty (stdout=PIPE) lub od razu zdekodować do str (encoding=...).

    Aktualne informacje i trendy
    - Najnowsze wersje Pythona (3.7+) pozwalają wprost w конструкtorze subprocess.Popen() ustawić parametry text=True i encoding='...'. Starsze wersje używają universal_newlines=True.
    - Konsola Windows 10/11 (z włączonym trybem Beta lub Windows Terminal) może obsługiwać UTF-8, ale nazwy plików, strumienie itp. domyślnie nadal bywają w CP1250/CP1252.
    - W systemach Linux i macOS standardowe kodowanie to zwykle UTF-8, ale programy zewnętrzne, np. windowsowe uruchamiane przez Wine, mogą generować inny format.

    Wspierające wyjaśnienia i detale
    - BOM (Byte Order Mark) w UTF-16 Big Endian: 0xFE, 0xFF
    W UTF-16 Little Endian mielibyśmy 0xFF, 0xFE.
    - 0x0A to znak końca wiersza „LF” (Line Feed). W Windows często występuje CR+LF = 0x0D, 0x0A.
    - Niektóre narzędzia, np. iconv na systemach UNIX-owych, potrafią konwertować strumienie danych między różnymi kodowaniami.

    Aspekty etyczne i prawne
    - Problem kodowania sam w sobie nie niesie szczególnych kwestii etycznych.
    - Pamiętaj jednak, że jeśli dane są poufne i przechodzą przez strumienie w formie czytelnej, mogą wymagać zabezpieczenia lub szyfrowania.
    - W kontekście licencji i praw: jeśli uruchamiasz zewnętrzne narzędzie, sprawdź licencję tego narzędzia i sposób jego dystrybucji.

    Praktyczne wskazówki
    1. Zawsze ustalaj, w jakim kodowaniu pracuje dany program i w jakim formacie przekazuje dane.
    2. Jeśli spodziewasz się „niestandardowych” bajtów, używaj trybu binarnego w Pythonie i jawnej dekodacji.
    3. Rozważ używanie `subprocess` w miejsce `os.popen()`, ponieważ jest bardziej elastyczny i oficjalnie zalecany przez dokumentację Pythona.

    Ewentualne zastrzeżenia lub uwagi dodatkowe
    - Jeśli program wysyła dane binarne (np. pliki, archiwa, potok audio/video), wówczas dekodowanie znaków może nie mieć sensu i należy pracować na bajtach.
    - Nie zawsze 0xFE, 0xFF muszą oznaczać BOM – choć w praktyce rzadko występują w innych kontekstach przy plikach tekstowych, warto jednak być świadomym nietypowych przypadków.

    Sugestie dalszych badań
    - Zapoznanie się z modułem `codecs` w Pythonie, który pozwala tworzyć obiekty do strumieniowej konwersji kodowań.
    - Eksperymenty z automatycznym wykrywaniem kodowania przy użyciu `chardet` lub `charset_normalizer`.
    - Lepsze poznanie `subprocess.Popen()` i opcji `stderr=PIPE`, `timeout`, `check=True`, by poprawnie obsługiwać błędy i przerwania.

    Krótkie podsumowanie
    - Najlepsze praktyki to unikanie `os.popen()`, przejście na `subprocess` i wyraźne określenie trybu binarnego bądź parametrów dekodowania.
    - Dla bajtów 0xFE, 0xFF, 0x0A prawdopodobnie należy użyć dekodera UTF-16, który uwzględni BOM (Big Endian).
    - Rozwiązanie w skrócie:
    (1) Przechwycić dane jako bytes.
    (2) Zdekodować odpowiednim kodowaniem (np. `'utf-16-be'` lub `'utf-16'` z automatycznym wykryciem kolejności bajtów).
    (3) Ewentualnie użyć `subprocess.Popen(..., encoding='utf-16-be')`.

    Pytania do użytkownika (jeśli potrzebne)
    1. Czy masz pewność, że dane wyjściowe są faktycznie w UTF-16 Big Endian (z BOM)?
    2. Czy możesz udostępnić więcej szczegółów na temat wywoływanego programu – czy w dokumentacji jest wzmianka o kodowaniu?
    3. Jaki system operacyjny jest używany? (Windows, Linux, macOS?)
    4. Czy próbowałeś już `subprocess.Popen` w trybie binarnym i ręcznej dekodacji?

    Jeśli dostarczysz dodatkowe informacje, będzie można sprecyzować dokładniejsze rozwiązanie. W razie dalszych pytań – zapraszam do dyskusji!
    Możesz mnie wywołać w każdym temacie pisząc @ElektrodaBot. Zastrzeżenie: Niektóre odpowiedzi udzielone przez ElektrodaBota mogą być niedokładne. Elektroda nie ponosi odpowiedzialności za dokładność, rzetelność ani kompletność prezentowanych informacji. Wszystkie odpowiedzi ElektrodaBota powinny być zweryfikowane przez użytkownika. OpenAI może monitorować i zapisywać informacje wprowadzane po wywołaniu ElektrodaBota.
  • #4 21389553
    _jta_
    Specjalista elektronik
    Posty: 48809
    Pomógł: 3198
    Ocena: 4184
    W skrócie: os.popen() nie przyjmuje 'rb'; subprocess.Popen() działa.

    Jak próbuję napisać bardziej szczegółowo, to mam "Access Denied".

    ElektrodaBot napisał:
    1. Otworzyć wyjście programu w trybie binarnym (np. przez os.popen(..., 'rb')

    To nie działa - 'rb' nie jest wartością, którą by os.popen() przyjmował:
    Kod: less
    Zaloguj się, aby zobaczyć kod


    ElektrodaBot napisał:
    1. Czy masz pewność, że dane wyjściowe są faktycznie w UTF-16 Big Endian (z BOM)?

    A ja nigdzie nie pisałem, by one były w takim kodowaniu, i mam wrażenie, że nie są.

    ElektrodaBot napisał:
    2. Czy możesz udostępnić więcej szczegółów na temat wywoływanego programu – czy w dokumentacji jest wzmianka o kodowaniu?

    To jest "pdfinfo". Według opisu, domyślnie używa kodowania UTF-8. Przy ustawieniu na ASCII7 wynik jest dokładnie taki sam.

    "Access Denied" jest kiedy próbuję podać działający kod... czyżby to było zakazane? Może spróbuję wstawić jako obrazek:

    Fragment kodu w Pythonie używający subprocess.Popen do uruchomienia programu.
REKLAMA