4. Ciągi znaków

4.1. Skrypt [str_01]: zapis ciągów znaków
Skrypt [str_01] wygląda następująco:
# ciągi znaków
# trzy możliwe notacje
chaine1 = "un"
chaine2 = 'deux'
chaine3 = """hélène va au
marché acheter des légumes"""
# wyświetlanie
print(f"chaine1=[{chaine1}], chaine2=[{chaine2}], chaine3=[{chaine3}]")
Komentarze
- wiersz 3: ciąg znaków ograniczony cudzysłowami „;
- wiersz 4: ciąg znaków ograniczony apostrofami ';
- wiersz 5: ciąg znaków ograniczony potrójnymi cudzysłowami """. W tym przypadku ciąg znaków może rozciągać się na kilka wierszy;
Wyniki są następujące:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/strings/str_01.py
chaine1=[un], chaine2=[deux], chaine3=[hélène va au
marché acheter des légumes]
Process finished with exit code 0
4.2. Skrypt [str_02]: metody klasy <str>
Skrypt [str_02] przedstawia niektóre metody klasy <str>, która jest klasą ciągów znaków:
# funkcje na ciągach znaków
# ciąg znaków pisany małymi literami
print(f"'ABCD'.lower()={'ABCD'.lower()}")
# ciąg znaków pisany wielkimi literami
print(f"'abcd'.upper()={'abcd'.upper()}")
# znak nr 2
print(f"'cheval[2]={'cheval'[2]}")
# podciąg zawierający znaki 5 i 6
print(f"'caractères accentués'[5:7]={'caractères accentués'[5:7]}")
# podciąg od znaku 4 włącznie
print(f"'caractères accentués'[4:]={'caractères accentués'[4:]}")
# podciąg do znaku 6 włącznie
print(f"'caractères accentués'[:5]={'caractères accentués'[:5]}")
# długość ciągu
print(f"len('123')={len('123')}")
# usunięcie spacji przed i po ciągu
print(f"' abcd '.strip()=[{' abcd '.strip()}]")
# usunięcie spacji następujących po ciągu
print(f"' abcd '.rstrip()=[{' abcd '.rstrip()}]")
# usunięcie spacji poprzedzających ciąg
print(f"' abcd '.lstrip()=[{' abcd '.lstrip()}]")
# termin „spacja” obejmuje w rzeczywistości różne znaki
str = ' \r\nabcd \t\f'
print(f"str.strip()=[{str.strip()}]")
# zastąpienie jednego podciągu innym
print(f"'abcd'.replace('a','x')={'abcd'.replace('a', 'x')}")
print(f"'abcd'.replace('ab','xy')={'abcd'.replace('ab', 'xy')}")
# wyszukiwanie podciągu: zwraca pozycję lub -1, jeśli podciąg nie został znaleziony
print(f"'abcd'.find('bc')={'abcd'.find('bc')}")
print(f"'abcd'.find('bc')={'abcd'.find('Bc')}")
# początek ciągu
print(f"'abcd'.startswith('ab')={'abcd'.startswith('ab')}")
print(f"'abcd'.startswith('x')={'abcd'.startswith('x')}")
# koniec ciągu
print(f"'abcd'.endswith('cd')={'abcd'.endswith('cd')}")
print(f"'abcd'.endswith('x')={'abcd'.endswith('x')}")
# przejście z listy ciągów znaków do pojedynczego ciągu znaków
print(f"'[X]'.join(['abcd', '123', 'èéà'])={'[X]'.join(['abcd', '123', 'èéà'])}")
print(f"''.join(['abcd', '123', 'èéà'])={''.join(['abcd', '123', 'èéà'])}")
# przejście z ciągu znaków do listy ciągów znaków
print(f"'abcd 123 cdXY'.split('cd')={'abcd 123 cdXY'.split('cd')}")
# pobieranie słów z ciągu
print(f"'abcd 123 cdXY'.split(None)={'abcd 123 cdXY'.split(None)}")
Komentarze połączone z uzyskanymi wynikami wystarczają do zrozumienia skryptu. Wyniki są następujące:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/strings/str_02.py
'ABCD'.lower()=abcd
'abcd'.upper()=ABCD
'koń[2]=e
'znaki z akcentami'[5:7]=tè
'znaki z akcentami'[4:]=znaki z akcentami
'znaki z akcentami'[:5]=znaki
len('123')=3
' abcd '.strip()=[abcd]
' abcd '.rstrip()=[ abcd]
' abcd '.lstrip()=[abcd ]
str.strip()=[abcd]
'abcd'.replace('a','x')=xbcd
'abcd'.replace('ab','xy')=xycd
'abcd'.find('bc')=1
'abcd'.find('bc')=-1
'abcd'.startswith('ab')=True
'abcd'.startswith('x')=False
'abcd'.endswith('cd')=True
'abcd'.endswith('x')=False
'[X]'.join(['abcd', '123', 'èéà'])=abcdQZXW2HTMLBW1hdZQX123QZXW2HTMLBW1hdZQXèéà
''.join(['abcd', '123', 'èéà'])=abcd123èéà
'abcd 123 cdXY'.split('cd')=['ab', ' 123 ', 'XY']
'abcd 123 cdXY'.split(None)=['abcd', '123', 'cdXY']
Process finished with exit code 0
4.3. Skrypt [str_03]: kodowanie ciągów znaków (1)
Skrypt [str_03] przedstawia podstawowe pojęcia dotyczące kodowania ciągów znaków:
# kodowanie znaków
# ciąg znaków typu str
str = "hélène va au marché acheter des légumes"
print(f"str=[{str}, type={type(str)}]")
# kodowanie utf-8
print("--- utf-8")
bytes1 = str.encode('utf-8')
print(f"bytes1={bytes1}, type={type(bytes1)}")
bytes2 = bytes(str, 'utf-8')
print(f"bytes2={bytes2}, type={type(bytes2)}")
# kodowanie iso-8859-1
print("--- iso-8859-1")
bytes1 = str.encode('iso-8859-1')
print(f"bytes1={bytes1}, type={type(bytes1)}")
bytes2 = bytes(str, 'iso-8859-1')
print(f"bytes2={bytes2}, type={type(bytes2)}")
# kodowanie latin1=iso-8859-1
print("--- latin1")
bytes1 = str.encode('latin1')
print(f"bytes1={bytes1}, type={type(bytes1)}")
bytes2 = bytes(str, 'latin1')
print(f"bytes2={bytes2}, type={type(bytes2)}")
Kodowanie ciągu znaków typu <str> tworzy ciąg binarny, w którym każdy znak ciągu jest reprezentowany przez jeden lub więcej bajtów. Istnieją różne rodzaje kodowania. Powyższy skrypt przedstawia dwa najpopularniejsze w krajach zachodnich: „utf-8” oraz „iso-8859-1”, znany również jako „latin1”.
Zasada kodowania/dekodowania została zilustrowana poniżej (patrz |https://realpython.com/python-encodings-guide/ |):

Komentarze
- wiersze 4–5: początkowy ciąg znaków, który ma zostać zakodowany. Instancje typu <str> są ciągami znaków Unicode: |https://docs.python.org/3/howto/unicode.html|, |https://realpython.com/python-encodings-guide/ |;
- wiersze 6–11: dwa sposoby kodowania ciągu znaków w formacie UTF68:
- wiersz 8: str.encode('utf-8);
- wiersz 10: bytes(str, 'utf-8');
- wiersze 12–17: powtarzamy tę samą operację z kodowaniem „iso-8859-1”;
- wiersze 18–23: „latin1” to inna nazwa kodowania „iso-8859-1”;
Wyniki są następujące:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/strings/str_03.py
str=[hélène va au marché acheter des légumes, type=<class 'str'>
--- utf-8
bytes1=b'h\xc3\xa9l\xc3\xa8ne va au march\xc3\xa9 acheter des l\xc3\xa9gumes', type=<class 'bytes'>
bytes2=b'h\xc3\xa9l\xc3\xa8ne va au march\xc3\xa9 acheter des l\xc3\xa9gumes', type=<class 'bytes'>
--- iso-8859-1
bytes1=b'h\xe9l\xe8ne va au march\xe9 acheter des l\xe9gumes', type=<class 'bytes'>
bytes2=b'h\xe9l\xe8ne va au march\xe9 acheter des l\xe9gumes', type=<class 'bytes'>
--- latin1
bytes1=b'h\xe9l\xe8ne va au march\xe9 acheter des l\xe9gumes', type=<class 'bytes'>
bytes2=b'h\xe9l\xe8ne va au march\xe9 acheter des l\xe9gumes', type=<class 'bytes'>
Process finished with exit code 0
Komentarze
- wiersz 4: widać, że znaki z akcentami zostały zakodowane na dwa bajty:
- é: [\xc3\xa9], co odpowiada sekwencji binarnej 11000011 10101001;
- è: [\xc3\xa8], co odpowiada sekwencji binarnej 11000011 10101000;
- wiersze 7: w kodowaniu ISO-8859-1 te dwa znaki z akcentami są kodowane inaczej:
- é: [\xe9], co odpowiada sekwencji binarnej 11101001;
- è: [\xe8], co odpowiada sekwencji binarnej 11101000;
4.4. Skrypt [str_04]: kodowanie ciągów znaków (2)
Skrypt [str_04] przedstawia dwa inne rodzaje kodowania: „base64” i „quoted-printable”. Oba te rodzaje kodowania nie kodują ciągów znaków Unicode, lecz obiekty binarne. Na przykład, gdy do wiadomości e-mail dołącza się dokument Word, zostanie on poddany jednemu z tych dwóch kodowań w zależności od używanego klienta poczty. Dotyczy to większości załączonych plików.
Skrypt wygląda następująco:
# kodowanie / dekodowanie
import codecs
# ciąg znaków
print("---- chaîne unicode")
str1 = "hélène va au marché acheter des légumes"
print(f"str1=[{str1}], type(str1)={type(str1)}")
# kodowanie utf-8
print("---- chaîne unicode -> binaire utf-8")
bytes1 = bytes(str1, "utf-8")
print(f"bytes1=[{bytes1}], type(bytes1)={type(bytes1)}")
# dekodowanie utf-8
print("---- binaire utf-8 -> chaîne unicode")
str2 = bytes1.decode("utf-8")
print(f"str2=[{str2}], type(str2)={type(str2)}")
print(f"str2==str1={str2 == str1}")
# kodowanie iso-8859-1
print("---- chaîne unicode -> binaire iso-8859-1")
bytes2 = bytes(str1, "iso-8859-1")
print(f"bytes2=[{bytes2}], type(bytes2)={type(bytes2)}")
# dekodowanie iso-8859-1
print("---- binaire iso-8859-1 -> chaîne unicode")
str3 = bytes2.decode("iso-8859-1")
print(f"str3=[{str3}], type(str3)={type(str3)}")
print(f"str3==str1={str3 == str1}")
# błąd dekodowania – bytes1 jest w formacie utf-8 – dekodujemy go do formatu iso-8859-1
print("--- binaire utf-8 (décodage iso-8859-1) --> chaîne unicode")
str4 = bytes1.decode("iso-8859-1")
print(f"str4=[{str4}], type(str4)={type(str4)}")
# kodowanie UTF-8 ciągu znaków Unicode
print("---- chaîne unicode -> binaire utf-8")
bytes3 = codecs.encode(str1, "utf-8")
print(f"bytes3=[{bytes3}], type(bytes3)={type(bytes3)}")
# kodowanie ciągu binarnego UTF-8 w base64
print("---- binaire utf-8 -> binaire base64")
bytes4 = codecs.encode(bytes1, "base64")
print(f"bytes4=[{bytes4}], type(bytes4)={type(bytes4)}")
# powrót do oryginalnego ciągu znaków Unicode
print("---- binaire base64 -> binaire utf-8 -> chaîne unicode")
str6 = codecs.decode(bytes4, "base64").decode("utf-8")
print(f"str6=[{str6}], type(str6)={type(str6)}")
# kodowanie ciągu binarnego w formacie quoted-printable
print("---- binaire utf-8 -> binaire quoted-printable")
str7 = codecs.encode(bytes1, "quoted-printable")
print(f"str7=[{str7}], type(str7)={type(str7)}")
# przywrócenie oryginalnego ciągu znaków Unicode
print("---- binaire quoted-printable -> binaire utf-8 -> chaîne unicode")
str8 = codecs.decode(str7, "quoted-printable").decode("utf-8")
print(f"str8=[{str8}], type(str8)={type(str8)}")
Komentarze
- wiersz 2: moduł [codecs] umożliwia konwersję do formatów „base64” i „quoted-printable”. Obsługuje on również wiele innych formatów;
- wiersze 4–7: ciąg znaków Unicode, który zostanie poddany różnym kodowaniom;
- wiersze 9–12: kodowanie UTF-8. Otrzymujemy plik binarny;
- wiersze 14–18: dekodowanie UTF-8 w celu przywrócenia oryginalnego ciągu znaków Unicode;
- wiersze 20–29: powtarzamy ten sam proces z kodowaniem „iso-8859-1”;
- wiersze 31–34: pokazano błąd dekodowania:
- wiersz 33:
bytes1to ciąg binarny zakodowany w „utf-8”. Dekodujemy go do „iso-8859-1”; - wiersze 36–39: inny sposób kodowania ciągu znaków w formacie utf-8 za pomocą modułu [codecs];
- wiersze 41–44: ciąg binarny „utf-8” jest kodowany w formacie „base64”;
- wiersze 46–49: pokazują, jak przejść od ciągu binarnego „base64” do oryginalnego ciągu Unicode;
- wiersze 51–59: powtarzamy ten proces, stosując kodowanie „quoted-printable” zamiast „base64”;
Wyniki są następujące:
C:\Data\st-2020\dev\python\cours-2020\python3-flask-2020\venv\Scripts\python.exe C:/Data/st-2020/dev/python/cours-2020/python3-flask-2020/strings/str_04.py
---- chaîne unicode
str1=[hélène va au marché acheter des légumes], type(str1)=<class 'str'>
---- chaîne unicode -> binaire utf-8
bytes1=[b'h\xc3\xa9l\xc3\xa8ne va au march\xc3\xa9 acheter des l\xc3\xa9gumes'], type(bytes1)=<class 'bytes'>
---- binaire utf-8 -> chaîne unicode
str2=[hélène va au marché acheter des légumes], type(str2)=<class 'str'>
str2==str1=True
---- chaîne unicode -> binaire iso-8859-1
bytes2=[b'h\xe9l\xe8ne va au march\xe9 acheter des l\xe9gumes'], type(bytes2)=<class 'bytes'>
---- binaire iso-8859-1 -> chaîne unicode
str3=[hélène va au marché acheter des légumes], type(str3)=<class 'str'>
str3==str1=True
--- binaire utf-8 (décodage iso-8859-1) --> chaîne unicode
str4=[hélène va au marché acheter des légumes], type(str4)=<class 'str'>
---- chaîne unicode -> binaire utf-8
bytes3=[b'h\xc3\xa9l\xc3\xa8ne va au march\xc3\xa9 acheter des l\xc3\xa9gumes'], type(bytes3)=<class 'bytes'>
---- binaire utf-8 -> binaire base64
bytes4=[b'aMOpbMOobmUgdmEgYXUgbWFyY2jDqSBhY2hldGVyIGRlcyBsw6lndW1lcw==\n'], type(bytes4)=<class 'bytes'>
---- binaire base64 -> binaire utf-8 -> chaîne unicode
str6=[hélène va au marché acheter des légumes], type(str6)=<class 'str'>
---- binaire utf-8 -> binaire quoted-printable
str7=[b'h=C3=A9l=C3=A8ne=20va=20au=20march=C3=A9=20acheter=20des=20l=C3=A9gumes'], type(str7)=<class 'bytes'>
---- binaire quoted-printable -> binaire utf-8 -> chaîne unicode
str8=[hélène va au marché acheter des légumes], type(str8)=<class 'str'>
Process finished with exit code 0
- wiersze 14–15: plik binarny w formacie utf-8 jest dekodowany na ciąg znaków Unicode przy użyciu niewłaściwego dekodera „iso-8859-1”. W związku z tym niektóre wygenerowane znaki Unicode są nieprawidłowe, w tym przypadku są to znaki z akcentami;
- wiersze 18–19: kodowanie „base64” polega na wykorzystaniu 64 znaków ASCII (kodowanych na 7 bitów) do zakodowania dowolnego pliku binarnego. Jak widać, zwiększa to rozmiar pliku binarnego w ciągu znaków;
- wiersze 22–23: kodowanie „quoted-printable” również polega na wykorzystaniu znaków ASCII (kodowanych na 7 bitów) do zakodowania dowolnego pliku binarnego;
Należy pamiętać, że gdy otrzymujemy plik binarny, na przykład z sieci internetowej, który reprezentuje tekst, aby go odtworzyć, trzeba znać kodowania, którym został poddany.