05 โ String Handling
Estimasi: 4 jam Tujuan: String adalah tipe data paling penting di NLP/LLM. Setelah ini kamu bisa manipulasi text dengan lancar.
Kenapa Materi Ini Penting?
LLM seperti GPT, Claude, Gemini pada akhirnya adalah mesin manipulasi string. Input prompt = string, output response = string, tokenisasi = string operations, prompt template = f-string. Saat kamu mulai bekerja dengan LangChain, RAG, dan fine-tuning di fase berikutnya, 70% kode kamu akan berurusan dengan string: cleaning, splitting, formatting, matching.
Regex (regular expression) khususnya adalah pisau Swiss Army untuk text processing. Ekstrak nomor HP dari testimoni customer, pisahkan kode dari natural text, validasi format input โ semua jadi mudah dengan regex. Skill ini juga penting untuk preprocessing dataset NLP dan parsing log model.
Analogi besar: string = kalung manik-manik. Tiap karakter adalah satu manik. Kamu bisa potong-potong (slice), gabung-gabung (join), ganti manik tertentu (replace), tapi tidak bisa langsung tukar manik di tengah kalung yang sudah jadi (immutable).
Peta Konsep
flowchart TD
A[๐ String Handling] --> B[โ๏ธ Membuat]
A --> C[โ๏ธ Slicing]
A --> D[๐ง Methods]
A --> E[๐จ Formatting]
A --> F[๐ Regex]
B --> B1[' ' / quoted]
B --> B2[r-string]
B --> B3[f-string]
D --> D1[case: upper/lower]
D --> D2[search: find/replace]
D --> D3[split / join]
D --> D4[strip]
E --> E1[f-string]
E --> E2[format spec]
F --> F1[search/findall]
F --> F2[sub/split]
F --> F3[capture groups]
Bagian 1 โ Membuat String
single = 'hello'
double = "hello"
triple = """multi
line
string"""
# Escape character
text = "She said \"hi\""
path = "C:\\Users\\yazid"
newline = "Line 1\nLine 2"
tab = "Col1\tCol2"
# Raw string (abaikan escape)
path = r"C:\Users\yazid" # tanpa double backslash
regex = r"\d+\.\d+" # untuk regex
Tips: untuk path file di Windows, pakai raw string (
r"...") ataupathlib.
Bagian 2 โ Indexing & Slicing (Sama dengan List)
s = "Python"
s[0] # 'P'
s[-1] # 'n'
s[1:4] # 'yth'
s[::-1] # 'nohtyP' (reverse)
s[::2] # 'Pto'
String immutable โ tidak bisa diubah:
s = "hello"
s[0] = "H" # โ TypeError!
Untuk "modifikasi", bikin string baru:
s = "H" + s[1:] # "Hello"
Bagian 3 โ String Methods Penting
Case
"Hello".lower() # "hello"
"hello".upper() # "HELLO"
"hello world".title() # "Hello World"
"Hello".swapcase() # "hELLO"
"hello".capitalize() # "Hello"
Whitespace
" hello ".strip() # "hello"
" hello ".lstrip() # "hello "
" hello ".rstrip() # " hello"
"--hello--".strip("-") # "hello" (strip karakter custom)
Cek Konten
"hello".startswith("he") # True
"hello".endswith("lo") # True
"hello".isalpha() # True (semua huruf)
"123".isdigit() # True (semua angka)
"abc123".isalnum() # True (huruf+angka)
" ".isspace() # True
"Hello".istitle() # True
"".isascii() # True
Cari & Replace
"hello world".find("world") # 6 (index ditemukan)
"hello world".find("xyz") # -1 (tidak ditemukan)
"hello world".index("world") # 6 (sama, tapi error kalau tidak ada)
"hello world".count("l") # 3
"hello world".replace("o", "0") # "hell0 w0rld"
"hello world".replace("o", "0", 1) # "hell0 world" (cuma 1x)
Split & Join
# Split
"a,b,c".split(",") # ['a', 'b', 'c']
"a b c".split() # ['a', 'b', 'c'] (default: whitespace)
"a\nb\nc".splitlines() # ['a', 'b', 'c']
"a,b,c".split(",", 1) # ['a', 'b,c'] (max 1 split)
# Join
",".join(["a", "b", "c"]) # "a,b,c"
" ".join(["hello", "world"]) # "hello world"
"".join(["P", "y"]) # "Py"
Pattern wajib hafal:
" ".join(list_of_strings)untuk gabung list jadi string.
Padding
"5".zfill(3) # "005" (zero-fill)
"abc".center(10, "-") # "---abc----"
"abc".ljust(10, ".") # "abc......."
"abc".rjust(10, ".") # ".......abc"
Bagian 4 โ String Formatting
F-String (Modern, Wajib)
nama = "Budi"
umur = 25
f"Halo {nama}, umur {umur}"
f"{nama!r}" # repr (dengan quote)
f"{2+2 = }" # "2+2 = 4" (Python 3.8+)
Format Spec
pi = 3.14159265
f"{pi:.2f}" # "3.14" (2 desimal)
f"{pi:.4f}" # "3.1416"
f"{pi:10.2f}" # " 3.14" (width 10, right-align)
f"{pi:<10.2f}" # "3.14 " (left-align)
f"{pi:^10.2f}" # " 3.14 " (center)
f"{pi:0>10.2f}" # "0000003.14" (pad dengan 0)
n = 1234567
f"{n:,}" # "1,234,567" (thousands separator)
f"{n:_}" # "1_234_567"
p = 0.8534
f"{p:.2%}" # "85.34%"
# Hex/oct/bin
f"{255:x}" # "ff"
f"{255:X}" # "FF"
f"{255:b}" # "11111111"
f"{255:o}" # "377"
Format Spec Generic
{value:[fill][align][sign][#][0][width][,][.precision][type]}
Contoh: {x:>10.2f} = right-align, width 10, 2 decimal float.
Cara Lama (Hindari)
# % formatting (deprecated style)
"Hello, %s. Umur %d" % (nama, umur)
# .format()
"Hello, {}. Umur {}".format(nama, umur)
"Hello, {0}. Umur {1}".format(nama, umur)
"Hello, {name}. Umur {age}".format(name=nama, age=umur)
Pakai f-string. Kecuali kamu kerja di codebase lama yang konsisten pakai
.format().
Bagian 5 โ Iterasi String
for char in "Python":
print(char)
# P y t h o n
# Dengan index
for i, char in enumerate("Python"):
print(f"{i}: {char}")
Cek Karakter Tertentu
text = "Hello World"
# Hitung vokal
vokal = sum(1 for c in text.lower() if c in "aeiou")
print(vokal) # 3
Bagian 6 โ Encoding (Penting untuk NLP)
text = "Hรซllo cafรฉ"
# Encode jadi bytes
bytes_data = text.encode("utf-8")
print(bytes_data) # b'H\xc3\xablo caf\xc3\xa9'
# Decode kembali
s = bytes_data.decode("utf-8")
print(s) # "Hรซllo cafรฉ"
# Cek panjang
len(text) # 9 karakter
len(bytes_data) # 11 bytes (karakter Unicode > 1 byte)
NLP/LLM context: UTF-8 standar default. Tapi saat baca file, kadang Windows pakai cp1252 atau UTF-16. Selalu spesifikasikan encoding.
Bagian 7 โ Regex Dasar
Diagram: Pipeline Regex
flowchart LR
T[๐ Text input] --> P[๐ Pattern]
P --> M{Match?}
M -->|Ya| G[๐ค Group / Match object]
M -->|Tidak| N[None / empty]
G --> O1[search: match pertama]
G --> O2[findall: semua match]
G --> O3[sub: replace]
Analogi: regex = template pencari. Bayangin kamu kasih cap berlubang ke text, hanya yang cocok pola lubang yang lolos.
\d+= "satu atau lebih digit",\w+@\w+\.\w+= "format email kasar".
Regex (regular expression) = pattern matching string. Powerful untuk text processing.
Setup
import re
Pattern Dasar
| Pattern | Match |
|---|---|
\d |
digit (0-9) |
\D |
non-digit |
\w |
word char (a-z, A-Z, 0-9, _) |
\W |
non-word |
\s |
whitespace |
\S |
non-whitespace |
. |
any character (kecuali newline) |
^ |
awal string |
$ |
akhir string |
* |
0+ kali |
+ |
1+ kali |
? |
0 atau 1 |
{n} |
tepat n kali |
{n,m} |
n sampai m kali |
[abc] |
salah satu dari a, b, c |
[^abc] |
bukan a, b, c |
(...) |
group |
| |
OR |
Function Utama
import re
# search โ cari pertama yang match
m = re.search(r"\d+", "Saya umur 25 tahun")
if m:
print(m.group()) # "25"
print(m.start()) # 10
print(m.end()) # 12
# findall โ cari semua match
re.findall(r"\d+", "20 apel, 30 jeruk, 5 mangga")
# ['20', '30', '5']
# sub โ replace
re.sub(r"\d+", "X", "20 apel 30 jeruk")
# "X apel X jeruk"
# split
re.split(r"[,;]\s*", "apel, jeruk; mangga,pisang")
# ['apel', 'jeruk', 'mangga', 'pisang']
# match โ match dari awal string
re.match(r"Hello", "Hello World") # match
re.match(r"World", "Hello World") # None
Capture Group
text = "Email: budi@gmail.com, Ani: ani@yahoo.com"
# Cari email dengan capture group
matches = re.findall(r"(\w+)@(\w+\.\w+)", text)
print(matches)
# [('budi', 'gmail.com'), ('ani', 'yahoo.com')]
# Match object
m = re.search(r"(\w+)@(\w+\.\w+)", text)
print(m.group(0)) # "budi@gmail.com" (full match)
print(m.group(1)) # "budi" (group 1)
print(m.group(2)) # "gmail.com"
Pattern Berguna untuk Text
import re
text = "Halo! Saya umur 25 tahun, email: budi@example.com. Tlp: 0812-3456-7890"
# Cari email
re.findall(r"\w+@\w+\.\w+", text)
# Cari nomor HP Indonesia
re.findall(r"08\d{2}-\d{4}-\d{4}", text)
# Hapus tanda baca
re.sub(r"[^\w\s]", "", text)
# Hapus angka
re.sub(r"\d+", "", text)
# Whitespace berlebih
re.sub(r"\s+", " ", " hello world ").strip()
Compile (Untuk Pattern yang Dipakai Berulang)
pattern = re.compile(r"\d+")
# Lebih cepat untuk banyak penggunaan
pattern.findall("20 apel")
pattern.sub("X", "30 jeruk")
Bagian 8 โ Pattern Praktis untuk NLP
Cleaning Text
def clean_text(text: str) -> str:
"""Bersihkan text untuk NLP."""
text = text.lower() # lowercase
text = re.sub(r"http\S+", "", text) # hapus URL
text = re.sub(r"@\w+", "", text) # hapus mention
text = re.sub(r"#\w+", "", text) # hapus hashtag
text = re.sub(r"[^\w\s]", "", text) # hapus tanda baca
text = re.sub(r"\d+", "", text) # hapus angka
text = re.sub(r"\s+", " ", text).strip() # whitespace berlebih
return text
print(clean_text("Cek post @budi tentang AI! Link: https://example.com #ai"))
# "cek post tentang ai link"
Tokenisasi Sederhana
text = "Halo dunia! Apa kabar?"
# Split by whitespace (basic)
tokens = text.split()
# Split by word boundary (lebih akurat)
tokens = re.findall(r"\b\w+\b", text)
print(tokens) # ['Halo', 'dunia', 'Apa', 'kabar']
Catatan: tokenisasi nyata untuk LLM jauh lebih kompleks (BPE, WordPiece). Yang ini cuma versi sederhana.
Parse Structured Text
log_line = "2026-05-13 10:30:45 [ERROR] Database connection failed"
pattern = r"(\d{4}-\d{2}-\d{2}) (\d{2}:\d{2}:\d{2}) \[(\w+)\] (.+)"
m = re.match(pattern, log_line)
if m:
date, time, level, message = m.groups()
print(f"Date: {date}, Time: {time}, Level: {level}, Msg: {message}")
Bagian 9 โ Common Pitfalls
Pitfall 1: Lupa Raw String
# โ \n di interpret sebagai newline
re.search("\n", text)
# โ
pakai r-string
re.search(r"\n", text)
Pitfall 2: Greedy vs Lazy
text = "<b>bold</b> dan <i>italic</i>"
# Greedy (default) โ match sebanyak mungkin
re.findall(r"<.+>", text)
# ['<b>bold</b> dan <i>italic</i>'] โ terlalu banyak
# Lazy โ match sesedikit mungkin (tambah ?)
re.findall(r"<.+?>", text)
# ['<b>', '</b>', '<i>', '</i>'] โ yang dimau
Pitfall 3: Encoding Saat Baca File
# โ Default encoding bisa beda di Windows vs Mac
with open("data.txt") as f:
text = f.read()
# โ
Selalu spesifikasikan
with open("data.txt", encoding="utf-8") as f:
text = f.read()
Common Mistakes & FAQ
โ Mistake 1: String immutable
s = "hello"
s[0] = "H" # โ TypeError
# Fix: bikin string baru
s = "H" + s[1:]
โ Mistake 2: == vs is untuk string
a = "hello"
b = "hello"
a == b # True (perbandingan nilai)
a is b # True/False (tergantung interning Python)
# Aturan: pakai == untuk perbandingan string
โ Mistake 3: Lupa raw string di regex
# \n di string biasa = newline character
re.search("\n", text) # cari newline beneran
# Pakai raw string
re.search(r"\n", text) # cari literal "\n"
โ Mistake 4: Greedy regex tanpa sengaja
text = "<b>bold</b>"
re.findall(r"<.+>", text) # ['<b>bold</b>'] โ terlalu banyak!
re.findall(r"<.+?>", text) # ['<b>', '</b>'] โ yang dimau
โ Mistake 5: f-string dengan tanda kutip yang sama
nama = "Budi"
print(f"Halo "{nama}"") # โ SyntaxError
# Fix: pakai quote berbeda
print(f'Halo "{nama}"') # โ
โ Mistake 6: encoding error saat baca file
# โ Default encoding bisa beda di Windows vs Linux
with open("data.txt") as f:
text = f.read()
# โ
Selalu spesifikasi
with open("data.txt", encoding="utf-8") as f:
text = f.read()
FAQ
Q: Lebih baik f-string atau .format()?
A: f-string. Lebih ringkas, lebih cepat, lebih readable. .format() hanya untuk codebase lama atau template terpisah.
Q: Kapan pakai regex vs str method? A:
- str method = kalau pattern fixed (
startswith,replacesubstring) - regex = kalau pattern kompleks (variasi, pilihan, repetisi)
Aturan: kalau pakai str method bisa, jangan regex (lebih cepat, lebih mudah).
Q: Regex itu bahasa apa? A: Regex bukan Python โ itu bahasa pattern universal. Sintaks-nya hampir sama di Python, JavaScript, Java, grep. Tapi tiap bahasa ada flavor sedikit beda (ECMAScript, PCRE, POSIX).
Q: Kapan pakai re.compile?
A: Kalau pattern dipakai berulang kali (loop), compile dulu untuk performance. Kalau cuma sekali, langsung re.search() saja.
Q: Apakah regex bisa parse HTML? A: Tidak ideal. HTML nested dan irregular โ pakai BeautifulSoup atau lxml. Regex hanya untuk text patterns sederhana.
Q: len(string) itu hitung byte atau karakter?
A: Karakter (Unicode codepoint), bukan byte. len("cafรฉ") = 4. Kalau mau byte, encode dulu: len("cafรฉ".encode("utf-8")) = 5.
Cek Pemahaman
- Bisa pakai f-string dengan format spec?
- Tahu method string penting (split, join, strip, replace)?
- Bisa pakai regex untuk cari email/angka?
- Tahu beda greedy dan lazy match?
- Tahu kenapa pakai raw string untuk regex?
Challenge 2.5
Challenge 1 โ Text Statistics
Input: paragraph Output:
- Jumlah karakter (with/without spaces)
- Jumlah kata
- Jumlah kalimat (split by
.!?) - Rata-rata panjang kata
- Kata terpanjang dan terpendek
Challenge 2 โ Palindrome Checker
Cek apakah string palindrome (baca sama dari depan/belakang).
- Case-insensitive
- Ignore spasi dan tanda baca
is_palindrome("A man a plan a canal Panama") # True
is_palindrome("hello") # False
Challenge 3 โ Caesar Cipher
Encode/decode pakai Caesar cipher (geser huruf N posisi).
encode("hello", 3) # "khoor"
decode("khoor", 3) # "hello"
Challenge 4 โ Email Validator
Pakai regex untuk validasi email:
- Format:
name@domain.tld - Name: alfanumerik + dot/dash/underscore
- Domain: alfanumerik + dash
- TLD: 2-6 huruf
is_valid_email("budi@example.com") # True
is_valid_email("budi.foo@example.co.id") # True
is_valid_email("budi@example") # False
is_valid_email("@example.com") # False
Challenge 5 โ Slug Generator
Convert title jadi URL slug:
- Lowercase
- Replace space dengan dash
- Hapus tanda baca
- Trim
slugify("Hello, World! Belajar AI") # "hello-world-belajar-ai"
slugify(" Halo Dunia ") # "halo-dunia"
Challenge 6 โ Sederhanakan Phone Number
Format nomor HP Indonesia jadi format standar:
format_phone("0812 3456 7890") # "+62 812-3456-7890"
format_phone("62812-3456-7890") # "+62 812-3456-7890"
format_phone("+628123456789") # "+62 812-3456-789"
Challenge 7 โ Word Wrap
Tulis function word_wrap(text, width) yang pecah text jadi baris-baris dengan max width karakter, tanpa memotong kata di tengah.
text = "Belajar AI memerlukan ketekunan dan latihan terus menerus"
word_wrap(text, 20)
# Output:
# Belajar AI
# memerlukan
# ketekunan dan
# latihan terus
# menerus
Challenge 8 โ Mini Markdown to HTML
Convert subset markdown ke HTML:
**bold**โ<b>bold</b>*italic*โ<i>italic</i>[text](url)โ<a href="url">text</a>- Headers
# H1,## H2,### H3
Pakai regex.
Selanjutnya: 06-oop.md โ Object-Oriented Programming. Wajib paham untuk PyTorch dan HuggingFace.