2Python untuk AI

String Handling

4 jam12 min baca
Tujuan

String adalah tipe data paling penting di NLP/LLM. Setelah ini kamu bisa manipulasi text dengan lancar.

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"...") atau pathlib.


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, replace substring)
  • 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.