Ôn thi HSG Tin học THPT – Bài 5: Kiểu xâu (String)

1. Mục tiêu

  • Nắm vững cú pháp và cách sử dụng của các phương thức xử lý xâu phổ biến trong Python, bao gồm split(), join(), find(), và các phương thức liên quan.
  • Hiểu rõ và áp dụng thành thạo kỹ thuật cắt lát (slicing) để truy xuất và xử lý các xâu con.
  • Vận dụng các công cụ đã học để giải quyết các bài toán xử lý văn bản cơ bản trong lập trình thi đấu.

2. Giới thiệu và phạm vi ứng dụng

Kiểu dữ liệu xâu (string) là một chuỗi các ký tự, được sử dụng để biểu diễn văn bản. Trong Python, xâu là một trong những kiểu dữ liệu cơ bản và có vai trò cực kỳ quan trọng. Bất kỳ dữ liệu nào được đặt trong cặp dấu nháy đơn (‘) hoặc nháy kép (“) đều được coi là một xâu.

Trong lập trình thi đấu, việc xử lý xâu xuất hiện rất thường xuyên, từ việc đọc và phân tích dữ liệu đầu vào (parsing input), xử lý các bài toán về văn bản, cho đến định dạng dữ liệu đầu ra (formatting output). Nắm vững các công cụ xử lý xâu là một kỹ năng nền tảng và thiết yếu.

3. Cú pháp và các ví dụ minh họa

3.1. Cắt lát (Slicing)

Cắt lát là kỹ thuật cho phép trích xuất một xâu con từ một xâu ban đầu bằng cách chỉ định chỉ số bắt đầu và kết thúc.

  • Cấu trúc cú pháp chuẩn:
    ten_xau[start:stop:step]
    • start: Chỉ số bắt đầu (bao gồm, mặc định là 0).
    • stop: Chỉ số kết thúc (không bao gồm, mặc định là độ dài xâu).
    • step: Bước nhảy (mặc định là 1).
  • Ví dụ:
# Khởi tạo một xâu
s = "Programming"

# Trích xuất xâu con từ chỉ số 0 đến 4 (không bao gồm 5)
s1 = s[0:5] # Kết quả: "Progr"

# Trích xuất từ chỉ số 3 đến hết xâu
s2 = s[3:] # Kết quả: "gramming"

# Trích xuất từ đầu đến chỉ số 6 (không bao gồm 7)
s3 = s[:7] # Kết quả: "Program"

# Trích xuất toàn bộ xâu với bước nhảy 2
s4 = s[::2] # Kết quả: "Pormig"

# Đảo ngược xâu bằng slicing
s_reversed = s[::-1] # Kết quả: "gnimmargorP"

print(f"s[0:5] -> {s1}")
print(f"s[3:] -> {s2}")
print(f"s[:7] -> {s3}")
print(f"s[::2] -> {s4}")
print(f"s[::-1] -> {s_reversed}")

3.2. Phương thức split()

Phương thức split() được sử dụng để tách một xâu thành một danh sách (list) các xâu con dựa trên một ký tự phân tách (delimiter).

  • Cấu trúc cú pháp chuẩn:
    ten_xau.split(separator, maxsplit)
    • separator (tùy chọn): Ký tự hoặc xâu dùng làm điểm phân tách. Nếu không được chỉ định, phương thức sẽ tách xâu dựa trên các khoảng trắng (whitespace) và loại bỏ các khoảng trắng thừa.
    • maxsplit (tùy chọn): Số lần tách tối đa.
  • Ví dụ:
# Ví dụ 1: Tách bằng khoảng trắng (mặc định)
sentence = "Lập trình thi đấu rất thú vị"
words = sentence.split()
# Kết quả: ['Lập', 'trình', 'thi', 'đấu', 'rất', 'thú', 'vị']
print(f"Tách theo khoảng trắng: {words}")

# Ví dụ 2: Tách bằng một ký tự cụ thể
data = "username@example.com"
parts = data.split('@')
# Kết quả: ['username', 'example.com']
print(f"Tách theo ký tự '@': {parts}")

# Ví dụ 3: Tách với maxsplit
line = "item1,item2,item3,item4"
items = line.split(',', 2)
# Kết quả: ['item1', 'item2', 'item3,item4']
print(f"Tách với maxsplit=2: {items}")

3.3. Phương thức join()

Ngược lại với split(), phương thức join() được dùng để nối các phần tử của một danh sách (hoặc một iterable) thành một xâu duy nhất, với một xâu phân cách cho trước.

  • Cấu trúc cú pháp chuẩn:
    xau_phan_cach.join(danh_sach)
  • Ví dụ:
# Nối các từ thành một câu
words_list = ['Học', 'Python', 'thật', 'dễ']
sentence_joined = " ".join(words_list)
# Kết quả: "Học Python thật dễ"
print(f"Nối bằng khoảng trắng: '{sentence_joined}'")

# Nối các phần tử bằng dấu gạch ngang
parts_list = ['2023', '10', '26']
date_string = "-".join(parts_list)
# Kết quả: "2023-10-26"
print(f"Nối bằng dấu '-': '{date_string}'")

3.4. Phương thức find()

Phương thức find() được sử dụng để tìm kiếm vị trí xuất hiện đầu tiên của một xâu con. Nó trả về chỉ số (index) của vị trí bắt đầu, hoặc trả về -1 nếu không tìm thấy.

  • Cấu trúc cú pháp chuẩn:
    ten_xau.find(sub, start, end)
    • sub: Xâu con cần tìm.
    • start (tùy chọn): Chỉ số bắt đầu tìm kiếm.
    • end (tùy chọn): Chỉ số kết thúc tìm kiếm.
  • Ví dụ:
text = "hello world, welcome to the world of python"

# Tìm vị trí đầu tiên của "world"
pos1 = text.find("world") # Kết quả: 6
print(f"Vị trí đầu tiên của 'world': {pos1}")

# Tìm "world" bắt đầu từ chỉ số 10
pos2 = text.find("world", 10) # Kết quả: 28
print(f"Vị trí của 'world' sau chỉ số 10: {pos2}")

# Tìm một xâu không tồn tại
pos3 = text.find("galaxy") # Kết quả: -1
print(f"Vị trí của 'galaxy': {pos3}")

4. Trực quan hóa và gỡ lỗi với Thonny

Môi trường Thonny là một công cụ hữu hiệu để quan sát cách các phương thức xử lý xâu hoạt động. Ta có thể sử dụng trình gỡ lỗi (debugger) để theo dõi sự thay đổi của các biến qua từng bước.

Xét đoạn mã sau:

line = "  Data Science and Machine Learning  "
line = line.strip() # Loại bỏ khoảng trắng ở hai đầu
parts = line.split(" and ")
result = " & ".join(parts)

Khi chạy đoạn mã này với trình gỡ lỗi của Thonny, ta có thể thấy:

  1. Biến line ban đầu chứa các khoảng trắng thừa.
  2. Sau khi thực thi line.strip(), một đối tượng xâu mới được tạo ra và gán lại cho line, không còn khoảng trắng ở hai đầu.
  3. Sau lệnh split(” and “), biến parts trở thành một danh sách [‘Data Science’, ‘Machine Learning’].
  4. Cuối cùng, biến result được tạo ra bằng cách nối các phần tử trong parts với xâu ” & “.

5. Bài tập vận dụng

5.1. Bài tập 1: Chuẩn hóa họ tên

5.1.1. Đề bài

Viết một chương trình nhận vào một xâu là họ tên của một người, có thể chứa nhiều khoảng trắng thừa và không viết hoa đúng quy cách. Chương trình cần chuẩn hóa xâu này sao cho:

  • Loại bỏ các khoảng trắng thừa ở đầu, cuối và giữa các từ.
  • Viết hoa chữ cái đầu của mỗi từ và viết thường các chữ cái còn lại.
  • Input: nGUYEn vAN biNH
    Output: Nguyen Van Binh

5.1.2. Lời giải và phân tích

  • Phân tích thuật toán:
    1. Sử dụng phương thức strip() để loại bỏ khoảng trắng ở hai đầu xâu.
    2. Sử dụng phương thức split() không có tham số để tách xâu thành một danh sách các từ. Bước này cũng tự động xử lý các khoảng trắng thừa ở giữa.
    3. Duyệt qua danh sách các từ, với mỗi từ, sử dụng phương thức capitalize() (hoặc lower() rồi xử lý ký tự đầu) để chuẩn hóa viết hoa.
    4. Sử dụng phương thức join() với ký tự phân cách là một khoảng trắng (” “) để nối các từ đã chuẩn hóa thành một xâu kết quả.
  • Cài đặt:
# Nhận dữ liệu đầu vào
full_name_raw = input("Nhập họ tên: ")

# Bước 1 & 2: Tách xâu thành danh sách các từ và loại bỏ khoảng trắng thừa
# split() không tham số sẽ tự động xử lý các khoảng trắng liên tiếp
words = full_name_raw.split()

# Bước 3: Chuẩn hóa viết hoa cho từng từ
normalized_words = []
for word in words:
    # capitalize() viết hoa ký tự đầu và viết thường các ký tự còn lại
    normalized_words.append(word.capitalize())

# Bước 4: Nối các từ đã chuẩn hóa lại
normalized_name = " ".join(normalized_words)

# In kết quả
print("Họ tên đã chuẩn hóa:", normalized_name)

5.2. Bài tập 2: Tìm và thay thế

5.2.1. Đề bài

  • Viết chương trình nhận vào 3 xâu: text, old_sub, và new_sub. Chương trình cần tìm tất cả các lần xuất hiện của xâu old_sub trong text và thay thế chúng bằng xâu new_sub.

    Input:
    text: One fish, two fish, red fish, blue fish.
    old_sub: fish
    new_sub: bird

    Output:
    One bird, two bird, red bird, blue bird.

5.2.2. Lời giải và Phân tích

  • Phân tích thuật toán:
    Python cung cấp một phương thức tích hợp sẵn rất mạnh mẽ cho bài toán này là replace(). Phương thức này sẽ quét toàn bộ xâu và thay thế tất cả các lần xuất hiện của một xâu con bằng một xâu con khác.
  • Cài đặt:
# Nhận dữ liệu đầu vào
text = input("Nhập văn bản gốc: ")
old_sub = input("Nhập xâu con cần thay thế: ")
new_sub = input("Nhập xâu con mới: ")

# Sử dụng phương thức replace() để thực hiện thay thế
# replace() trả về một xâu mới đã được thay thế
new_text = text.replace(old_sub, new_sub)

# In kết quả
print("Văn bản sau khi thay thế:", new_text)

6. Các lưu ý và lỗi thường gặp

  • Tính bất biến của xâu (Immutability): Đây là đặc tính quan trọng nhất cần nhớ. Xâu trong Python là bất biến, nghĩa là không thể thay đổi nội dung của một xâu sau khi đã tạo ra. Các phương thức như replace(), upper(), strip() không thay đổi xâu gốc mà trả về một xâu mới đã được sửa đổi. Việc gán my_string[0] = ‘A’ sẽ gây ra lỗi TypeError.
  • Sự khác biệt giữa find() và index(): Phương thức index() có chức năng tương tự find(), nhưng nếu không tìm thấy xâu con, index() sẽ gây ra lỗi ValueError làm dừng chương trình, trong khi find() sẽ trả về -1 một cách an toàn.
  • split(‘ ‘) vs. split(): Khi tách xâu có nhiều khoảng trắng liên tiếp, my_string.split() sẽ xử lý đúng và cho ra các từ, trong khi my_string.split(‘ ‘) sẽ tạo ra các xâu rỗng trong danh sách kết quả.

7. Tổng kết

Bài học này đã giới thiệu các công cụ và kỹ thuật cơ bản nhưng vô cùng thiết yếu để xử lý kiểu dữ liệu xâu trong Python. Các thao tác chính bao gồm cắt lát (slicing) để trích xuất dữ liệu và các phương thức split(), join(), find(), replace() để phân tách, kết hợp và biến đổi xâu. Việc hiểu rõ tính bất biến của xâu và lựa chọn phương thức phù hợp là chìa khóa để giải quyết hiệu quả các bài toán liên quan đến xử lý văn bản.

Để lại một bình luận

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *