Menu

Chuỗi trong R: paste, sprintf, gsub, substr và hơn nữa

Mọi thứ bạn cần để làm việc với văn bản trong R: tạo chuỗi, nối bằng paste và paste0, định dạng bằng sprintf, tìm kiếm bằng gsub, grepl và strsplit.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Tạo chuỗi trong R

Chuỗi (string) trong R là văn bản nằm giữa hai dấu nháy. Nháy kép và nháy đơn đều dùng được và có nghĩa y hệt nhau; quy ước là dùng nháy kép, chuyển sang nháy đơn khi bản thân văn bản chứa dấu nháy kép:

Ký tự thoát bằng dấu gạch chéo ngược hoạt động như ở hầu hết các ngôn ngữ: \" cho dấu nháy nguyên văn, \n cho xuống dòng, \t cho tab, \\ cho chính dấu gạch chéo ngược.

Giờ là lỗi mà người mới học R nào cũng mắc đúng một lần: hỏi độ dài của chuỗi bằng length().

nchar() đếm ký tự, ra 5. length() đếm phần tử của vector, và một chuỗi đơn là vector có một phần tử, nên nó trả về 1. Trong R mọi thứ đều là vector, kể cả văn bản, và mọi hàm trong trang này đều lặng lẽ xử lý theo từng phần tử trên cả vector character. Đó là một tính năng, khi bạn hết ngạc nhiên vì nó.

Nối chuỗi: paste() và paste0()

R không có + cho chuỗi. Phép nối là paste(), mặc định nối các đối số bằng một dấu cách, và paste0(), nối liền không có gì xen giữa:

Vì paste được vector hóa, đưa cho nó một vector sẽ tạo ra nhiều chuỗi cùng lúc: đây là cách bạn sinh tên tệp, nhãn và mã định danh chỉ trong một dòng:

Còn đối số collapse làm điều ngược lại: nó gộp cả một vector thành một chuỗi duy nhất với ký tự phân cách bạn chọn:

Hãy nhớ sự phân chia: sep điều khiển phần nối giữa các đối số, collapse điều khiển phần nối giữa các phần tử của một vector. Bạn có thể dùng cả hai trong một lệnh gọi.

Định dạng với sprintf()

Khi cần định dạng số bên trong văn bản (số chữ số thập phân cố định, độ rộng có đệm), paste() hết sức và sprintf() vào cuộc. Hàm này dùng mã định dạng kiểu C: %s cho chuỗi, %d cho số nguyên, %f cho số thập phân:

%.1f nghĩa là "một chữ số thập phân", %.3f là ba, và %05d đệm số 0 cho đủ năm chữ số. %% kép tạo ra một dấu phần trăm nguyên văn. sprintf() cũng được vector hóa, nên nó có thể định dạng cả một cột giá trị trong một lệnh gọi: xem nhập và xuất để biết cách kết hợp sprintf() + cat() khi in báo cáo.

Đổi chữ hoa thường và cắt chuỗi: toupper(), tolower(), substr()

Đổi chữ hoa thường làm đúng như tên gọi:

tolower() thật sự hữu ích khi chuẩn hóa dữ liệu lộn xộn trước khi so sánh: "Yes", "YES" và "yes" đều thành cùng một giá trị.

substr(x, start, stop) lấy một đoạn theo vị trí ký tự, đếm từ 1 (R đánh chỉ số từ 1 ở mọi nơi):

Cả hai đầu đều được tính: vị trí từ 1 đến 11 cho "Programming".

Tìm và thay thế: sub(), gsub() và grepl()

sub() thay lần xuất hiện đầu tiên của một mẫu; gsub() ("global substitute") thay tất cả:

Một chi tiết then chốt: mặc định, mẫu tìm kiếm là biểu thức chính quy (regex), không phải văn bản nguyên văn. Các ký tự regex như ., *, ( và ? có ý nghĩa đặc biệt: một dấu . trần khớp với bất kỳ ký tự nào. Khi bạn muốn khớp đúng nguyên văn, hãy truyền fixed = TRUE:

Dòng đầu cho a-b-c; dòng thứ hai cho -----, vì trong regex . khớp với từng ký tự một. Cho đến khi học regex, hãy coi fixed = TRUE là mặc định và bạn sẽ không bao giờ dính bẫy.

grepl() không thay thế mà kiểm tra từng phần tử có khớp không, trả về một vector logic. Vì vậy nó là công cụ chuẩn để lọc văn bản:

Kết quả đầu đánh dấu tên tệp nào chứa .csv; kết quả thứ hai dùng vector logic đó để chỉ giữ lại các tên khớp.

Tách và cắt khoảng trắng: strsplit() và trimws()

strsplit() cắt một chuỗi theo ký tự phân cách. Điểm kỳ quặc duy nhất: nó trả về một list, vì nó có thể tách nhiều chuỗi cùng lúc. Với một chuỗi đơn, lấy phần tử đầu tiên bằng [[1]]:

Còn trimws() loại bỏ khoảng trắng mà dữ liệu thực tế lúc nào cũng mang theo ở hai đầu:

Mặc định nó cắt cả hai đầu; which = "left" hoặc "right" chỉ cắt một phía (các tên này chỉ đầu và cuối chuỗi).

Lựa chọn thay thế: stringr

Mọi thứ ở trên đều là base R: luôn có sẵn, không cần cài đặt. Package stringr của tidyverse gói cùng các thao tác đó trong một cách đặt tên str_* nhất quán, với chuỗi luôn là đối số đầu tiên, điều mà nhiều người thấy dễ nhớ hơn (xem package để biết cách cài):

library(stringr)

str_detect(files, "csv")            # like grepl(), data first
str_replace_all(s, "good", "great") # like gsub(), arguments reordered
str_length("hello")                 # like nchar()

Dù vậy, các hàm chuỗi của base R vẫn đáng biết: bạn sẽ gặp chúng trong mọi script, mọi câu trả lời trên Stack Overflow và mọi thông báo lỗi. Hãy học tên của base trước; chuyển sang stringr khi thứ tự đối số thiếu nhất quán bắt đầu làm bạn khó chịu.

Điều cần nhớ

  • Chuỗi theo quy ước dùng nháy kép; nchar() đếm ký tự, length() đếm phần tử vector.
  • Nối bằng paste() / paste0(); sep nối các đối số, collapse gộp một vector thành một chuỗi.
  • sprintf() lo phần đầu ra có định dạng: %s, %d, %.2f.
  • sub() thay kết quả khớp đầu tiên, gsub() thay mọi kết quả khớp, grepl() kiểm tra có khớp không; tất cả mặc định dùng regex, nên hãy truyền fixed = TRUE để khớp nguyên văn.
  • strsplit() trả về list (lấy [[1]]); trimws() làm sạch đầu vào có khoảng trắng thừa.

Tiếp theo: đưa văn bản vào và ra khỏi chương trình, gồm in ra bằng print() và cat(), và đọc dữ liệu người dùng nhập.

Câu hỏi thường gặp

Làm sao để nối chuỗi trong R?

Dùng paste() hoặc paste0(), vì R không có + cho chuỗi. paste("data", "science") cho "data science" (mặc định cách nhau bằng dấu cách); paste0("data", "science") nối liền không có gì xen giữa. Dùng sep = để đổi ký tự phân cách và collapse = để gộp cả một vector thành một chuỗi.

Làm sao để lấy độ dài của chuỗi trong R?

nchar("hello") trả về 5, tức số ký tự. length("hello") trả về 1: trong R, length() đếm số phần tử của vector, và một chuỗi đơn là vector có một phần tử. Nhầm length() với nchar() là lỗi kinh điển của người mới bắt đầu.

sub() và gsub() trong R khác nhau thế nào?

Cả hai đều tìm và thay thế, nhưng sub() chỉ thay kết quả khớp đầu tiên, còn gsub() ("global sub") thay mọi kết quả khớp. Cả hai mặc định coi mẫu tìm kiếm là biểu thức chính quy (regular expression); truyền fixed = TRUE để khớp đúng nguyên văn.

Làm sao để tách chuỗi trong R?

strsplit(x, split) tách theo một mẫu, nhưng nó trả về một list (vì nó có thể tách nhiều chuỗi cùng lúc). Với một chuỗi đơn, hãy lấy phần tử đầu tiên: strsplit("a,b,c", ",")[[1]] cho vector character "a" "b" "c".

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu