Việc phân tích và lấy giá trị tham số từ chuỗi URL là tác vụ phổ biến khi phát triển ứng dụng iOS. Thay vì tách chuỗi thủ công bằng các phương thức cắt ký tự, bạn có thể tận dụng NSRegularExpression để thực hiện việc này một cách chính xác và tối ưu hiệu năng.
Dưới đây là phương thức tiện ích được triển khai dưới dạng Category của NSString, cho phép trích xuất giá trị dựa trên tên tham số truyền vào:
- (NSString *)extractValueForParameter:(NSString *)paramKey {
if (!paramKey.length || !self.length) return nil;
NSError *regexError = nil;
// Mẫu regex: tìm tham số đứng đầu chuỗi hoặc sau dấu ?/&, bắt giá trị cho đến khi gặp & hoặc hết chuỗi
NSString *pattern = [NSString stringWithFormat:@"(?:^|[?&])%@=([^&]*)", paramKey];
NSRegularExpression *regexEngine = [NSRegularExpression regularExpressionWithPattern:pattern
options:NSRegularExpressionCaseInsensitive
error:®exError];
if (regexError) {
NSLog(@"Lỗi khởi tạo regex: %@", regexError.localizedDescription);
return nil;
}
NSRange searchRange = NSMakeRange(0, self.length);
NSArray<NSTextCheckingResult *> *foundMatches = [regexEngine matchesInString:self
options:0
range:searchRange];
for (NSTextCheckingResult *match in foundMatches) {
// Nhóm 0 là toàn bộ chuỗi khớp, nhóm 1 là giá trị nằm trong cặp ngoặc đơn ()
NSRange captureRange = [match rangeAtIndex:1];
if (captureRange.location != NSNotFound) {
return [self substringWithRange:captureRange];
}
}
return nil;
}
Bộ máy regex được khởi tạo thông qua phương thức lớp hoặc phương thức khởi tạo tiêu chuẩn:
+ (nullable NSRegularExpression *)regularExpressionWithPattern:(NSString *)pattern
options:(NSRegularExpressionOptions)options
error:(NSError **)error;
- (nullable instancetype)initWithPattern:(NSString *)pattern
options:(NSRegularExpressionOptions)options
error:(NSError **)error;
Tham số options nhận các giá trị từ enum NSRegularExpressionOptions, cho phép tùy chỉnh hành vi khớp mẫu:
NSRegularExpressionCaseInsensitive: Bỏ qua phân biệt hoa/thường.NSRegularExpressionAllowCommentsAndWhitespace: Bỏ qua khoảng trắng và phần ghi chú sau dấu#trong biểu thức.NSRegularExpressionIgnoreMetacharacters: Coi toàn bộ mẫu như chuỗi văn bản thuần túy, vô hiệu hóa ký tự đặc biệt.NSRegularExpressionDotMatchesLineSeparators: Cho phép dấu.khớp cả ký tự xuống dòng.NSRegularExpressionAnchorsMatchLines: Cho phép^và$khớp đầu/cuối mỗi dòng thay vì toàn bộ chuỗi.NSRegularExpressionUseUnixLineSeparators: Chỉ công nhận\nlà ký tự ngắt dòng duy nhất.NSRegularExpressionUseUnicodeWordBoundaries: Áp dụng chuẩn Unicode TR#29 để xác định ranh giới từ.
Quá trình tìm kiếm đồng bộ được thực thi qua phương thức:
- (NSArray<NSTextCheckingResult *> *)matchesInString:(NSString *)string
options:(NSMatchingOptions)options
range:(NSRange)range;
Tham số options ở đây thuộc kiểu NSMatchingOptions (khác với NSMatchingFlags thường xuất hiện trong block callback của phương thức duyệt bất đồng bộ). Đối với tác vụ trích xuất cơ bản, giá trị 0 là đủ. Kết quả trả về là mảng các đối tượng NSTextCheckingResult, mỗi đối tượng chứa thông tin vùng khớp (range) của toàn bộ mẫu và các nhóm bắt (capturing groups).
Trong biểu thức chính quy, nhóm có chỉ số 0 luôn đại diện cho toàn bộ đoạn văn bản khớp với mẫu. Các nhóm con được đánh số thứ tự từ 1 dựa trên vị trí mở ngoặc đơn (. Ví dụ minh họa kết quả khớp:
Chuỗi gốc: source=app_store&refId=8842&campaign=summer
Kết quả khớp cho tham số "refId":
- rangeAtIndex:0 → "refId=8842" (vị trí 13, độ dài 10)
- rangeAtIndex:1 → "8842" (vị trí 19, độ dài 4)
- rangeAtIndex:2 → NSNotFound (không tồn tại nhóm thứ 3)
Bằng cách truy xuất trực tiếp rangeAtIndex:1, bạn có thể cắt chính xác đoạn giá trị cần thiết mà không phải xử lý hậu kỳ các ký tự phân cách như = hay &. Cấu trúc sử dụng non-capturing group (?:...) cho phần tiền tố giúp giảm thiểu chỉ số nhóm, đồng thời tăng tốc độ thực thi của engine regex.