Łańcuchy znaków

https://docs.python.org/3/library/stdtypes.html#text-sequence-type-str

WPROWADZENIE

Łańcuch (string) to uporządkowany ciąg znaków używany do przechowywania informacji tekstowej. W Pythonie 2 znaki odpowiadają bajtom, więc bytes() to alias do str() (Py2.7). W Pythonie 3 string (typ 'str') zawiera znaki Unikodu (code points), które zamienia się na ciąg bajtów korzystając z ustalonego kodowania. Jeden znak Unikodu może odpowiadać kilku bajtom.

Łańcuchy zaliczane są do niezmiennych sekwencji, czyli nie można ich zmieniać w obszarze przechowywania ich w pamięci. Dostęp do znaków możliwy jest za pomocą offsetu (przesunięcia). Dozwolone są ujemne offsety (odliczanie od końca).

W Pythonie nie ma osobnego typu znakowego jak w języku C. Znakami są po prostu stringi o długości jeden.

Moduły pomocnicze: string, re, itp.

Stałe łańcuchowe mogą być zapisywane z użyciem apostrofów lub znaków cudzysłowu. Dzięki temu możemy wstawiać znak cudzysłowu/apostrofu wewnątrz łańcucha bez specjalnych zabiegów. Wielowierszowe bloki wygodnie jest zapisywać wewnątrz potrójnych znaków, ponieważ zachowywane są końce linii.

OPERACJE NA ŁAŃCUCHACH

+----------------------+-----------------------------+
| Operacja             | Znaczenie                   |
+----------------------+-----------------------------+
| "", '', str()        | puste napisy                |
| S = "abc"            | tworzenie napisu            |
| S = str(obiekt)      | tworzenie napisu            |
| len(S)               | długość napisu              |
| S1 + S2              | łączenie                    |
| n * S, S * n         | powtarzanie                 |
| S[i]                 | indeksowanie                |
| S[i:j]               | wycinanie                   |
| S[i:j:k]             | wycinanie                   |
| S2 = S1[:]           | kopiowanie                  |
| S2 = str(S1)         | kopiowanie                  |
| for char in S: pass  | iteracja                    |
| S1 in S2             | zawieranie (bool)           |
| S1 not in S2         |                             |
| S1.count(S2)         | liczba wystąpień            | (non-overlapping matches!)
| S1.index(S2)         | pierwsze wystąpienie        |
| max(S), min(S)       | największy|najmniejszy znak |
| "%s dom" % "stary"   | formatowanie łańcucha       |
| "%s %s" % ("a", "b") |                             |
| S.join(iterable)     | sklejanie z napisem         |
| S.format(...)        | Py2.6+, Py3.x               |
| del S                | usuwanie napisu             |
+----------------------+-----------------------------+

Standard Unikodu przypisuje abstrakcyjnym znakom (characters)
liczby całkowite dodatnie (code points).
String Unikodu jest to ciąg code points.
Reguły transformacji stringu Unikodu na ciąg bajtów jest to kodowanie.
Najpopularniejsze obecnie kodowanie to UTF-8.

Code point 97 (litera 'a') odpowiada u'\x61' = u'\u0061' = u'\U00000061', 
(0)110.0001, jeden bajt w UTF-8.

Code point 257 odpowiada u'\u0101', 
(110)0.0100.(10)00.0001, dwa bajty w UTF-8.
(u'\u0101').encode('utf-8') zwraca w Pythonie 2 string '\xc4\x81'.
('\u0101').encode('utf-8') zwraca w Pythonie 3 bytes b'\xc4\x81'.

Code point 8364 (znak euro) odpowiada u'\u20ac',
(1110).0010.(10)00.0010.(10)10.1100, trzy bajty w UTF-8, '\xe2\x82\xac'.

p = 8364     # code point dla znaku Euro (int)
hex(p)       # '0x20ac'
char = chr(p)   # znak Euro jako string (str)
ascii(char)       # '\u20ac' (str), znak zapisany przez cyfry szesnastkowe
utf = char.encode('utf-8')   # b'\xe2\x82\xac' (bytes), znak zakodowany

# Zwykłe stringi. W Pythonie 3 zawierają znaki Unikodu.
"pierwszy", 'drugi', "trzeci'x", 'czwarty"y'
"raz"  'dwa'                  # napisy obok siebie będą sklejone

# Raw strings - napisy ze znakami specjalnymi.
r"raw string\n\t\a"

# Unicode strings (Py2). Nazwa typu to unicode().
unicode('abc')
u"unicode"
# Tworzenie jednego znaku Unikodu (Py2).
unichr(97)   # return u'a' = u'\x61' = u'\u0061' = u'\U00000061'
unichr(256)   # return u'\u0100'

# Raw Unicode strings (Py2).
ur"raw unicode"

# Potrójne znaki do wielowierszowych bloków.
# Można stosować """ lub '''.
"""
Syntax: nazwa [OPCJE]
    -h    pomoc
    -v    wersja
"""

# Korzystanie z odwrotnego ukośnika (backslash).
# Generujemy znaki specjalne lub pozbawiamy jakiś znak
# jego specjalnego znaczenia.
"jeden\"dwa", 'trzy\'cztery'  # " lub ' w środku nie kończy stringu
"pole1\tpole2\tpole3"         # \t to tabulacja, odstep poziomy
"pierwszy wiersz\ndrugi wiersz"         
# \n to newline, znak przejścia do nowego wiersza
"tak pokazujemy ukośnik \\"       # \\ to jeden ukośnik (backslash)

# W skrypcie można wykorzystać potrójne znaki do tymczasowego
# blokowania (wykomentowania) kodu.
X = 1
"""
import os                     # zablokowany kod
print(os.getcwd())
"""
Y = 2

INDEKSOWANIE

Dla łańcucha S dozwolone są indeksy od 0 do len(S)-1. Indeks ujemny S[-n] rozumiany jest jako S[len(S)-n]. S[i] jest napisem o długości jeden.

WYCINANIE

S[j:k] wycina elementy od S[j] do S[k-1] włącznie, tworząc nowy napis. S[:k] oznacza S[0:k]. S[j:] oznacza S[j:len(S)]. S[:] daje cały napis. S[:j] + S[j:] daje cały napis.


# Napisy można porównywać (<, >, ==), nawet trójkami.
napis1 = "abc"
napis2 = "bcd"
napis1 < napis2
"a" > "Z"

# owoc = "banan"
# Nie wolno podstawić tak jak w C, napisy są niezmienne.
# owoc[0] = "s"       ERROR!
# Trzeba utworzyć nowy string np. poleceniem
# nowy_owoc = "s" + owoc[1:]

a = "abc"
b = "abc"
c = a
d = a[:]
# to samo miejsce w pamięci
id(a), id(b), id(c), id(d)
# też dla copy.copy() jest to samo miejsce w pamięci
# UWAGA Te efekty zachodzą dla krótkich napisów, bo Python
# krótkie napisy trzyma w pamięci do późniejszego wykorzystania.

# Napisy są niezmienne (immutable).
>>> a = "abc"
>>> a[1] = "s"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>>

KONKATENACJA I POWTÓRZENIE NAPISÓW


>>> S = "abc"
>>> S + "xyz"        # konkatenacja
'abcxyz'
>>> S                # S pozostaje bez zmian
'abc'
>>> 3 * S
'abcabcabc'
>>> S * 4
'abcabcabcabc'
>>> S[::-1]          # wycinanie od końca
'cba'
>>>

Znak plus (+) czy gwiazdka (*) oznacza co innego dla liczb, a co innego dla napisów. Jest to przykład polimorfizmu w Pythonie.

BUDOWANIE NAPISÓW


colors = ["red", "blue", "green", "yellow"]

# Pierwszy sposób dopuszczalny tylko dla kilku składników.
result = ""
for item in colors:
    result = result + item

# Prawidłowy sposób. Składnia.
# S.join(iterable), S to separator przy sklejaniu
result = "".join(colors)

METODY STRINGÓW

Metody dla stringów nie mogą zmienić oryginału, więc zwykle zwracają nowy łańcuch.


dir(str)                 # lista metod stringów
print(ord("a"))       # 97, kod ASCII znaku
print(ord(u"ą))       # 261, ord() działa też dla unikodu w Py2
print(chr(98))        # "b", znak dla danego kodu ASCII

word = "".join(["a","b","c","d","e","f"])   # sklejanie listy znaków lub stringów
print(word)           # "abcdef"
word.find("cd")     # 2, odnalezienie przesunięcia podłańcucha
word.replace("bc", "xyz")     # zastąpienie wystąpień podłańcucha

# Justowanie tekstu.
word = "hej"
word.ljust(6)                 # "hej   "
word.rjust(6)                 # "   hej"
word.center(6)                # " hej  "

# Usuwanie białych znaków (whitespace).
word = " abc  "
word.lstrip()                 # "abc  "
word.rstrip()                 # " abc"
word.strip()                  # "abc"

word = "...abc.."
word.strip(".")               # "abc"

word = "rAz dWa tRzY"
word.lower()                  # "raz dwa trzy"
word.upper()                  # "RAZ DWA TRZY"
word.capitalize()             # "Raz dwa trzy"
word.title()                  # "Raz Dwa Trzy"
word.swapcase()               # "RaZ DwA TrZy"
word.startswith("abc")        # sprawdzam prefix
word.endswith("abc")          # sprawdzam suffix
"abbbbc".count("bb")          # wynik 2, a nie 3
    # bo nie wlicza się nakładających wystąpień

# Lista wyrazów bez białych znaków.
line = "a\tb c\nd"
line.split()                  # ['a', 'b', 'c', 'd']
line.split("\t")              # ["a", "b c\nd"]
line.splitlines()             # ['a\tb c', 'd']

"111".zfill(8)                # "00000111"

# Badanie typów znaków w stringu.
S.isalnum()
S.isalpha()
S.isdigit()
S.islower()
S.isupper()
S.isspace()

Jeżeli chcemy sprawdzić, czy obiekt jest łańcuchem, korzystamy z funkcji isinstance(obiekt, basestring), gdzie basestring to klasa bazowa dla str i unicode (Python 2.3+).

Łańcuchy znaków obsługują zaawansowane operacje formatowania ze znakiem procenta (%) lub metodą format [więcej informacji przy opisie standardowego wyjścia].

MEMORYVIEW

W Pythonie 2.7 pojawia się memoryview(object), przy czym obiekt musi wspierać buffer protocole. Buffer protocole dostarcza sposobu na dostęp do wewnętrznych danych obiektu. W zwykłym kodzie pythonowym używa się buffer protocole za pomocą memoryview(). Obiekty wbudowane wspierające buffer protocole to str i bytearray (ale nie unicode). memoryview() ma dwie metody tobytes() i tolist(), oraz kilka atrybutów tylko do odczytu.

Za pomocą memoryview() można używać i modyfikować duże ilości danych bez ich kopiowania. Dzięki temu dany program używa mniej pamięci i działa szybciej.


>>> v = memoryview('abcefg')   # widok obiektu str
>>> v[1]   # zwraca str
'b'
>>> v[-1]
'g'
>>> v[1:4]   # wycinek zwraca subview
<memory at 0x77ab28>
>>> v[1:4].tobytes()   # zwraca bytestring, czyli str
'bce'
>>> v.readonly   # czy pamięć jest readonly
True
>>> v.tolist()   # zwraca listę int
[97, 98, 99, 101, 102, 103]

>>> data = bytearray('abcefg')
>>> v = memoryview(data)
>>> v.readonly   # czy pamięć jest readonly
False
>>> v[0] = 'z'   # mutable
>>> data
bytearray(b'zbcefg')
>>> v[2] = 'spam'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: cannot modify size of memoryview object

https://stackoverflow.com/questions/6736771/buffers-and-memoryview-objects-explained-for-the-non-c-programmer

Buffers and Memoryview Objects explained for the non-C programmer

https://eli.thegreenplace.net/2011/11/28/less-copies-in-python-with-the-buffer-protocol-and-memoryviews

Less copies in Python with the buffer protocol and memoryviews

https://julien.danjou.info/high-performance-in-python-with-zero-copy-and-the-buffer-protocol/

High-Performance in Python with Zero-Copy and the Buffer Protocol (by Julien Danjou)

PYTHON 3

W Py3 zamiast typów str i unicode występują typy bytes i str, które mają odmienne własności. W Py3 znika basestring (PEP 3137).

bytes(object) zwraca niezmienną (immutable) tablicę bajtów. [W Py2.7 jest to alias do str().]

bytearray(object) zwraca zmienną (mutable) tablicę bajtów. [Istnieje w Py2.7.]

str(object) zwraca niezmienny string mogący zawierać znaki unikodu. [W Py2 odpowiada mu unicode().]

memoryview(object) tworzy widok obiektu. Obiekt musi wspierać protokół buforowania (buffer protocol), czyli dawać dostęp do bufora obiektu bez robienia kopii. Było wiele zmian w kolejnych wersjach Py3.

PRZYKŁAD

Praca w systemie Linux Debian 10, ustawienie locale 'pl_PL.UTF-8'.


# Wprowadzanie tekstu z klawiatury (Py2).
>>> a = 'jabłko'   # str
>>> a
'jab\xc5\x82ko'
>>> b = u'jabłko'   # unicode
>>> b
u'jab\u0142ko'

# Wprowadzanie tekstu z klawiatury (Py3).
>>> a = 'jabłko'
>>> a
'jabłko'