Poprzednio poznaliśmy sporo zastosowań listy i możliwości pracy z listą. Teraz pochylmy się nad innym typem danych, jakim jest napis. Istnieje wiele operacji, jakie można wykonywać na tekście. Poznajmy podstawowe z nich.

Test na obecność w napisie – in, count

Aby sprawdzić, czy coś jest elementem napisu możemy użyć po prostu słówka kluczowego in, będącego elementem Pythona. Oto taki test:

msg = "hello world"
print("world" in msg)
#True

Możemy też go użyć w warunku, każdą rzecz, którą da się zredukować do typu prawda/fałsz można użyć jako warunek:

msg = "hello world"
if "world" in msg:
    print("world is in msg")
#world is in msg

Możemy też użyć count:

msg = "hello world"
if msg.count("world") > 0:
    print("world is in msg")
#world is in msg

Funkcja count zlicza nam ilość wystąpień dla „world”, która jest równa 1 tutaj, zaś warunek daliśmy, aby ta ilość była większa niż 0. Ale wartości 0 i nie-zero też podlegają konwersji, zero do fałszu, nie-zero do prawdy, więc można to tak zapisać:

msg = "hello world"
if msg.count("world"):
    print("world is in msg")
#world is in msg

Zawsze, gdy count zwróci coś innego niż 0 nasz warunek będzie prawdziwy, a zawsze, gdy dostaniemy 0 to znak, że naszego elementu w tekście nie ma.

Testy na obecność – startswith, endswith, indeksy

Możemy sprawdzić, czy coś zaczyna się lub kończy w określony sposób. Służą do tego odpowiednie funkcje i mogą one sprawdzać zarówno jeden znak jak i dłuższy tekst:

msg = "Hello World!"
print(msg.startswith("H"))
print(msg.endswith("!"))
print(msg.startswith("Hello"))
print(msg.endswith("World!"))
# True
# True
# True
# True

Jeżeli natomiast chcemy w prosty sposób sprawdzić czy tekst zaczyna się na określoną literę bądź nią kończy, możemy użyć indeksów. Pierwszy znak to zawsze indeks 0 zaś ostatni to -1:

msg = "Hello World!"
print(msg[0] == "H")
print(msg[-1] == "!")
# True
# True

Inne testy – wielkość liter, rodzaj znaku

Możemy sprawdzić, czy coś jest wielką bądź małą literą. Służą do tego funkcje islower() oraz isupper(). Wykonajmy je na pierwszym znaku danego napisu:

msg = "Hello World!"
print(msg[0].islower())
print(msg[0].isupper())
# False
# True

Funkcja index pozwoli nam zobaczyć indeks pierwszego wystąpienia danego znaku lub napisu:

msg = "Hello World!"
print(msg.index("Hello"))
print(msg.index(" "))
# 0
# 5

Fajnie. Widzimy, że pierwsze „Hello” to indeks 0, zaś pierwsza spacja to indeks 5. Teraz, jak już znamy indeks spacji (policzyć go sobie na palcach też mogliśmy) sprawdźmy, czy element o indeksie 5 jest spacją:

msg = "Hello World!"
print(msg[0].isspace())
print(msg[5].isspace())
# False
# True

Pierwszy element spacją nie jest, ale ten o indeksie 5 jak najbardziej. Funkcja isspace służy do wykrywania spacji. Teraz zobaczmy kilka innych funkcji testujących:

msg = "Hello World!"
print(msg[-1].isalpha())
print(msg[-1].isalnum())
# False
# False
print("1".isalpha())
print("1".isalnum())
print("1".isdigit())
# False
# True
# True

Element ostatni nie jest ani literą ani znakiem alfanumerycznym (litery + cyfry). Dla porównania – „1” nie przechodzi testu na literę, przechodzi test na znak alfanumeryczny (litery + cyfry) oraz na bycie cyfrą.

Zamiana w tekście – replace

W tekście możemy coś zamienić korzystając z replace. Oto przykład zamiany spacji na znak '|’

msg = "Hello World!"
msg = msg.replace(" ", "|")
print(msg)
#Hello|World!

Warto zwrócić uwagę, że tekst jest niemutowalnym typem, a zatem msg.replace zwraca nam kopię tego tekstu z odpowiednią „podmianką”. Tę kopię przypisujemy do naszej zmiennej. Taki drobny szczegół.

Odwrócenie tekstu – reversed, [::-1]

Tekst można odwracać na dwa sposoby. Pierwszy to [::-1]:

msg = "Hello World!"
msgrev = msg[::-1]
print(msgrev)
#!dlroW olleH

Drugi to zastosowanie reversed, aczkolwiek pomęczyć się trzeba, aby obiekt reversed zamienić na napis:

msg = "Hello World!"
msgrev = "".join(reversed(msg))
print(msgrev)
#!dlroW olleH

Możemy też po reversed iterować i dodawać element po elemencie:

msg = "Hello World!"
msgrev = ""
for char in reversed(msg):
    msgrev += char
print(msgrev)
#!dlroW olleH

Zamiana wielkości liter – upper, lower, swapcase

Funkcja upper podnosi do wielkiej litery, lower – do małej, swapcase – wielką zamienia na małą i odwrotnie:

print("anna".upper())
print("ANNA".lower())
print("aNNA".swapcase())
# ANNA
# anna
# Anna

Usuwanie niepotrzebnych spacji – strip, lstrip, rstrip

Strip usuwa niepotrzebne spacje, czyli takie, które znajdują się przed tekstem, po jego lewej stronie i po tekście, po jego prawej stronie:

msg ="   Hello World   "
print(msg.strip())
#Hello World

Istnieje też lstrip, który usuwa spacje tylko po lewej i rstrip – tylko po prawej:

msg ="   Hello World   "
print(msg.strip())
print(msg.lstrip())
print(msg.rstrip())
#Hello World
#Hello World   
#   Hello World

Zamiana znaku na jego wartość liczbową – ord

Patrząc na klawiaturę, wydawać by się mogło, że komputer gdzieś tam w środku przechowuje litery. Nie jest to jednak prawda, każda litera posiada swoją wartość liczbową. Możemy podejrzeć tę wartość używając funkcji ord:

msg = "Hello world"
for char in msg:
    print(ord(char))
# 72
# 101
# 108
# 108
# 111
# 32
# 119
# 111
# 114
# 108
# 100

Jak widać każdy znak ma swoją wartość liczbową. Wielkie litery to dwucyfrowa wartość (H to 72), małe to już liczby powyżej 100, zaś spacja (znak jak każdy inny) to 32. Tak widzi nasze znaki komputer.

Możemy to jeszcze bardziej przesunąć i pokazać każdą literę w zapisie binarnym, wrzucając liczbę z ord do funkcji bin, która liczbę dziesiętną zamienia na binarną:

msg = "Hello world"
for char in msg:
    print(bin(ord(char)))
# 0b1100101
# 0b1101100
# 0b1101100
# 0b1101111
# 0b100000
# 0b1110111
# 0b1101111
# 0b1110010
# 0b1101100
# 0b1100100

I tak mniej więcej (minus przedrostek 0b sygnalizujący nam system dwójkowy/binarny) widzi ten nasz napis komputer. Ciekawe, prawda?

Zadanie 1 – zamień tekst na reprezentację liczbową

Tutaj chcemy napisać funkcję, która bierze tekst i wyświetla nam, jakie liczby ten tekst tworzą, jak to widzi komputer. Zabierzmy się do tego. Próba pierwsza:

msg = "Hello World"
lst = [ord(char) for char in msg]
print(lst)
#[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]

Mamy skrypt, który zamienia nam każdy znak z tekstu na jego liczbową wartość i pakuje do listy. Jeżeli w takiej formie chcemy zwrócić dane użytkownikowi to w zasadzie mamy wszystko. Jeżeli jednak chcemy mieć napis, to musimy każdy element zamienić na napis:

msg = "Hello World"
lst = [str(ord(char)) for char in msg]
print(lst)
#['72', '101', '108', '108', '111', '32', '87', '111', '114', '108', '100']

Niby różnicy nie ma wielkiej, ale teraz możemy za pomocą join zrobić napis zawierający te liczby:

msg = "Hello World"
lst = [str(ord(char)) for char in msg]
print(lst)
#['72', '101', '108', '108', '111', '32', '87', '111', '114', '108', '100']
print(" ".join(lst))
#72 101 108 108 111 32 87 111 114 108 100

Kleimy po spacji, liczby mają różną długość, więc musi być jakiś znak, który nam mówi, gdzie kończy się 72 a zaczyna 101… Tutaj użyliśmy spacji.

Teraz możemy naszą funkcję napisać. Wersja zwracająca listę:

def text_in_numbers(text):
    return [ord(char) for char in text]

print(text_in_numbers("Hello world"))
#[72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]

Wersja zwracająca napis:

def text_in_numbers(text):
    numbers = [str(ord(char)) for char in text]
    return " ".join(numbers)

print(text_in_numbers("Hello world"))
#72 101 108 108 111 32 119 111 114 108 100

Zadanie 2 – napisz własny lstrip

Musimy napisać funkcję, która robi lstrip, czyli usuwa niepotrzebne spacje z lewej strony. Powinniśmy umieć to zrobić.

def my_lstrip(text):
    idx = 0
    while text[idx] == " ":
        idx += 1
    return text[idx:]

print(my_lstrip("   hello!"))
#hello!
print(my_lstrip("hello!"))
#hello!

Ta funkcja bierze tekst. Zmienną idx ustawia na zero. Następnie tak długo jak text[idx] jest spacją, zwiększa idx o 1. W pewnym momencie dojdzie to znaku spacją niebędącego i warunek przestanie być prawdziwy.

Ale zostanie nam indeks, gdzie po lewej stronie kończą się spacje. Możemy zatem zwrócić tekst od tego indeksu do końca i to właśnie robimy.

Możemy też napisać własny rstrip. Podobnie zadziała:

def my_rstrip(text):
    idx = len(text)-1
    while text[idx] == " ":
        idx -= 1
    return text[:idx+1]

print(my_rstrip("hello!   "))
#hello!
print(my_rstrip("hello!"))
#hello!

Tutaj aby „dobrać się” do ostatniego indeksu musimy od długości tekstu odjąć 1. Następnie idziemy do tyłu (od prawej do lewej) czyli zmniejszamy idx o 1, aż text[idx] przestanie być spacją. I mamy indeks, w którym po prawej stronie zaczyna się tekst.

Teraz robimy slice, tylko pamiętać musimy, że po prawej stronie jest inaczej. Zapis np. [1:10] oznacza „od indeksu 1 włącznie do indeksu 10 wyłącznie”.

My chcemy wszystko od początku do ostatniego indeksu, gdzie jeszcze tej spacji nie ma, ale włącznie z nim, stąd to +1.

Może i odrobinę się trzeba pomęczyć, aby to zrozumieć, ale tak to działa.