Pytanie Co robi znak "b" przed literałem literowym?


Wygląda na to, że następująca jest poprawna składnia

my_string = b'The string'

Chciałbym wiedzieć:

  1. Co to b znak przed ciągiem oznacza?
  2. Jakie są efekty korzystania z niego?
  3. Jakie są odpowiednie sytuacje, aby z niego skorzystać?

znalazłem powiązane pytanie tutaj na SO, ale to pytanie dotyczy PHP, a to stwierdza b jest używany do wskazania, że ​​string jest binarny, w przeciwieństwie do Unicode, który był potrzebny do kompatybilności kodu z wersją PHP <6, podczas migracji do PHP 6. Nie sądzę, aby dotyczyło to Pythona.

Znalazłem ta dokumentacja na stronie Python o korzystaniu z u znak w tej samej składni, aby określić ciąg znaków w formacie Unicode. Niestety, nie wspomina o b znak w dowolnym miejscu w tym dokumencie.

Ponadto, tylko z ciekawości, jest więcej symboli niż b i u które robią inne rzeczy?


461
2018-06-07 18:14


pochodzenie




Odpowiedzi:


Cytować dokumentacja Python 2.x.:

Prefiks "b" lub "B" jest ignorowany w   Python 2; wskazuje, że   literał powinien być literałem dosłownym   w Pythonie 3 (np. gdy kod jest   automatycznie konwertowane za pomocą 2to3). ZA   Po przedrostku "u" lub "b" może następować   prefiks "r".

The Dokumentacja w języku Python 3.3 stwierdza:

Literały bajtów zawsze poprzedzone są "b" lub "B"; generują instancję typu bajtów zamiast typu str. Mogą zawierać tylko znaki ASCII; Bajty o wartości liczbowej 128 lub większej należy wyrazić przy pomocy znaków ucieczki.


254
2018-06-07 18:16



Brzmi to tak, jakby Python <v3 zignorował tę dodatkową postać. Jaki byłby przypadek w wersji 3, w której trzeba by użyć łańcucha b, a nie zwykłego łańcucha? - Jesse Webb
@Gweebz - jeśli faktycznie piszesz ciąg znaków w określonym kodowaniu zamiast z kodami unikowymi (np. B '\ xff \ xfe \ xe12' zamiast '\ u32e1'). - detly
To ma sens. Zaznaczę to jako zaakceptowaną odpowiedź, ale są tu i inne dobre odpowiedzi! - Jesse Webb
Właściwie, jeśli zaimportowałeś unicode_literals od __future__, to "odwróci" zachowanie dla tego konkretnego ciągu znaków (w Pythonie 2.x) - Romuald Brunet
Nieco bardziej zwykła narracja językowa wokół cytowanej dokumentacji sprawiłaby, że byłaby to lepsza odpowiedź na IMHO - Hack-R


Python 3.x wyraźnie rozróżnia typy:

  • str = '...' literals = sekwencja znaków Unicode (UTF-16 lub UTF-32, w zależności od kompilacji Pythona)
  • bytes = b'...' literals = sekwencja oktetów (całkowitych od 0 do 255)

Jeśli znasz język Java lub C #, pomyśl o tym str tak jak String i bytes tak jak byte[]. Jeśli znasz SQL, pomyśl o tym str tak jak NVARCHAR i bytes tak jak BINARY lub BLOB. Jeśli znasz rejestr systemu Windows, pomyśl str tak jak REG_SZ i bytes tak jak REG_BINARY. Jeśli znasz C (++), to zapomnij o wszystkim, czego się dowiedziałeś char i struny, ponieważ CHARAKTER NIE JEST BYTE. Ten pomysł jest już od dawna przestarzały.

Używasz str kiedy chcesz reprezentować tekst.

print('שלום עולם')

Używasz bytes kiedy chcesz reprezentować dane binarne niskiego poziomu, takie jak struct.

NaN = struct.unpack('>d', b'\xff\xf8\x00\x00\x00\x00\x00\x00')[0]

Możesz kodować za str do a bytes obiekt.

>>> '\uFEFF'.encode('UTF-8')
b'\xef\xbb\xbf'

Możesz też dekodować bytes do a str.

>>> b'\xE2\x82\xAC'.decode('UTF-8')
'€'

Ale nie możesz dowolnie mieszać tych dwóch typów.

>>> b'\xEF\xBB\xBF' + 'Text with a UTF-8 BOM'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: can't concat bytes to str

The b'...' notacja jest nieco myląca, ponieważ pozwala na podanie bajtów 0x01-0x7F ze znakami ASCII zamiast liczb szesnastkowych.

>>> b'A' == b'\x41'
True

Ale muszę podkreślić, postać nie jest bajtem.

>>> 'A' == b'A'
False

W języku Python 2.x

W wersjach pre-3.0 Pythona brakowało tego rodzaju rozróżnienia między tekstem a danymi binarnymi. Zamiast tego było:

  • unicode = u'...' literals = sekwencja znaków Unicode = 3.x str
  • str = '...'literals = sekwencje pomieszanych bajtów / znaków
    • Zwykle tekst, zakodowany w nieokreślonym kodowaniu.
    • Ale również używane do reprezentowania danych binarnych, takich jak struct.pack wydajność.

Aby ułatwić przejście od 2.x do 3.x, b'...' literalna składnia została przeniesiona do Pythona 2.6, aby umożliwić rozróżnienie łańcuchów binarnych (które powinny być bytes w 3.x) od ciągów tekstowych (które powinny być str w 3.x). The b prefiks nie robi nic w 2.x, ale mówi 2to3 skrypt nie konwertuje go na ciąg znaków Unicode w 3.x.

Więc tak, b'...' literały w Pythonie mają ten sam cel, który robią w PHP.

Poza tym, po prostu z ciekawości   więcej symboli niż te, które robisz   inne rzeczy?

The r prefix tworzy surowy ciąg (np. r'\t' to ukośnik odwrotny + t zamiast karty) i potrójne cytaty '''...''' lub """...""" pozwalają na pisanie wieloliniowych ciągów literowych.


412
2018-06-08 02:34



Dzięki! Zrozumiałem to po przeczytaniu tych zdań: "Aby ułatwić przejście od 2.x do 3.x, dosłowna składnia b" ... została przeniesiona do Pythona 2.6, aby umożliwić rozróżnienie łańcuchów binarnych (co powinno być bajtami w 3.x) z ciągów tekstowych (które powinny być równe 3.x) Prefiks b nie robi nic w 2.x, ale mówi skryptowi 2to3, aby nie konwertował go na ciąg znaków Unicode w 3.x. " - tommy.carstensen
The 'A' == b'A' --> Falseczek naprawdę wyjaśnia. Reszta jest doskonała, ale do tej pory nie zrozumiałem, że jest ciąg bajtów naprawdę nie tekst. - Wildcard
'שלום עולם' == 'hello world' - Eli
+1 dla .decode('UTF-8'). Szukałem sposobu na zmianę mojego ciągu b 'otrzymanego przez serwer POST z powrotem do unicode. - nikhilvj
Jest to o wiele bardziej jasne niż przyjęta odpowiedź, która właśnie cytuje dokumentację. Dokumentacja dla mnie nie miała sensu, więc zapewnienie dodatkowego kontekstu w dokumentacji jest niesamowite. Dzięki! - rayryeng


B oznacza ciąg bajtów.

Bajty są rzeczywistymi danymi. Struny są abstrakcją.

Jeśli masz ciąg tekstowy składający się z wielu znaków i wziąłbyś pojedynczy znak, byłby to ciąg znaków, który może mieć więcej niż 1 bajt w zależności od kodowania.

Jeśli wziąłby 1 bajt z łańcuchem bajtowym, otrzymalibyśmy pojedynczą ośmiobitową wartość z zakresu od 0 do 255 i może to nie być kompletny znak, jeśli te znaki z powodu kodowania miały> 1 bajt.

TBH użyłbym łańcuchów, chyba że miałem jakiś konkretny powód niskiego poziomu użycia bajtów.


13
2018-06-07 18:34





Zmienia go w bytes dosłowny (lub str w wersji 2.x) i jest ważna dla wersji 2.6+.

The r prefiks powoduje, że ukośniki odwrotne są "niezinterpretowane" (nie są ignorowane, a różnica robi materia).


8
2018-06-07 18:16



Brzmi to niesłusznie, zgodnie z dokumentacją cytowaną w odpowiedzi aix; b będzie ignorowane w wersji Pythona innej niż 3. - Jesse Webb
To bedzie str w 2.x w obu kierunkach, więc można powiedzieć, że jest to ignorowane. Rozróżnienie ma znaczenie przy imporcie unicode_literals z __future__ moduł. - Ignacio Vazquez-Abrams
Przepraszam, źle zrozumiałem twoje oryginalne oświadczenie. Twoja odpowiedź jest dokładna w tym, co mówi. - Jesse Webb


Oto przykład, w którym brak "b" spowodowałby wyjątek TypeError w Pythonie 3.x

>>> f=open("new", "wb")
>>> f.write("Hello Python!")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' does not support the buffer interface

Dodanie przedrostka "b" rozwiązałoby problem.


6
2018-06-23 07:02





Oprócz tego, co powiedzieli inni, zwróć uwagę na pojedynczy znak w Unicode może składać się z wielu bajtów.

Sposób, w jaki działa unicode, to stary format ASCII (7-bitowy kod, który wygląda jak 0xxx xxxx) i dodany sekwencje wielobajtowe gdzie wszystkie bajty zaczynają się od 1 (1xxx xxxx), aby reprezentować znaki poza ASCII, tak aby był to kod Unicode wstecznie kompatybilny z ASCII.

>>> len('Öl')  # German word for 'oil' with 2 characters
2
>>> 'Öl'.encode('UTF-8')  # convert str to bytes 
b'\xc3\x96l'
>>> len('Öl'.encode('UTF-8'))  # 3 bytes encode 2 characters !
3

1
2018-03-07 12:16