Ôn tập: trình phân tích tệp
Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 58 trên 107.
Thử thách
DễHãy xây dựng một trình phân tích cú pháp tệp cấu hình kết hợp tất cả các kỹ thuật xử lý lỗi trong chương này. Trình phân tích cú pháp sẽ xử lý nhiều tình huống lỗi bằng các kiểu lỗi tùy chỉnh, lỗi sentinel, bọc lỗi và dọn dẹp phù hợp bằng defer.
Bạn sẽ sắp xếp code trong ba tệp:
errors.go: Định nghĩa cơ sở hạ tầng lỗi cho trình phân tích cú pháp.Tạo hai lỗi sentinel:
ErrEmptyContentvới thông báo"file content is empty"ErrInvalidFormatvới thông báo"invalid configuration format"
Tạo kiểu lỗi tùy chỉnh
ParseErrorvới các trườngLine(int) vàMessage(string). Phương thứcError()của kiểu này sẽ trả về:parse error at line [Line]: [Message]parser.go: Xây dựng trình phân tích cú pháp cấu hình với cách xử lý lỗi theo từng lớp.Tạo một struct
ConfigParservới trường booleanparsedđể theo dõi việc phân tích cú pháp đã hoàn tất thành công hay chưa.Triển khai các phương thức sau:
NewConfigParser() *ConfigParser: tạo trình phân tích cú pháp mới vớiparsedđược đặt thànhfalseParseLine(lineNum int, line string) error: kiểm tra một dòng. Nếu dòng không chứa ký tự=, trả về*ParseErrorvới số dòng và thông báo"missing key-value separator". Nếu không, trả vềnil.Parse(content string) error: phương thức phân tích cú pháp chính, thực hiện các việc sau:- Trả về
ErrEmptyContentnếu nội dung trống - Trả về
ErrInvalidFormatnếu nội dung không chứa ít nhất một ký tự xuống dòng - Tách nội dung theo ký tự xuống dòng và gọi
ParseLinecho từng dòng, số dòng bắt đầu từ 1 - Nếu
ParseLinetrả về lỗi, bọc lỗi đó bằng"parsing configuration: %w"rồi trả về - Đặt
parsedthànhtruekhi thành công và trả vềnil
- Trả về
Status() string: trả về"ready"nếuparsedlà true, nếu không thì trả về"not parsed"
main.go: Đọc nội dung cấu hình và minh họa cách kiểm tra lỗi toàn diện.Đọc hai dòng đầu vào tạo thành nội dung cấu hình khi ghép lại bằng một ký tự xuống dòng. Dùng
deferđể luôn in trạng thái của trình phân tích cú pháp ở cuối, bất kể thành công hay thất bại.Sau khi thử phân tích cú pháp, kiểm tra mọi lỗi bằng cả
errors.Is()vàerrors.As()để đưa ra phản hồi cụ thể tùy theo lỗi xảy ra.
Các đầu vào sau sẽ được cung cấp:
- Dòng 1: Dòng đầu tiên của nội dung cấu hình
- Dòng 2: Dòng thứ hai của nội dung cấu hình
Xử lý lỗi bằng các thông báo cụ thể:
- Nếu
errors.Is()tìm thấyErrEmptyContent: inError: No configuration provided - Nếu
errors.Is()tìm thấyErrInvalidFormat: inError: Configuration must have multiple lines - Nếu
errors.As()tìm thấy một*ParseError: in toàn bộ lỗi đã bọc, sau đó xuống dòng và inFix line [Line]: [Message] - Nếu phân tích cú pháp thành công: in
Configuration parsed successfully
Sau khi xử lý kết quả, dù thành công hay gặp lỗi, trạng thái được in bằng defer sẽ xuất hiện trên dòng mới: Parser status: [status]
Ví dụ, với host=localhost và port=8080, đầu ra sẽ là:
Configuration parsed successfully
Parser status: readyVới host=localhost và invalid line, đầu ra sẽ là:
parsing configuration: parse error at line 2: missing key-value separator
Fix line 2: missing key-value separator
Parser status: not parsedTự làm thử
package main
import (
"bufio"
"errors"
"fmt"
"os"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Đọc dòng đầu tiên của cấu hình
line1, _ := reader.ReadString('\n')
line1 = line1[:len(line1)-1] // Xóa ký tự xuống dòng ở cuối
// Đọc dòng thứ hai của cấu hình
line2, _ := reader.ReadString('\n')
if len(line2) > 0 && line2[len(line2)-1] == '\n' {
line2 = line2[:len(line2)-1]
}
// Nối hai dòng bằng ký tự xuống dòng để tạo nội dung cấu hình
content := line1 + "\n" + line2
// Tạo bộ phân tích cú pháp mới
parser := NewConfigParser()
// TODO: Dùng defer để luôn in ra trạng thái của bộ phân tích cú pháp ở cuối
// Định dạng: "Parser status: [status]"
// TODO: Thử phân tích nội dung
// TODO: Xử lý lỗi bằng errors.Is() và errors.As()
// - Nếu errors.Is() tìm thấy ErrEmptyContent: in ra "Error: No configuration provided"
// - Nếu errors.Is() tìm thấy ErrInvalidFormat: in ra "Error: Configuration must have multiple lines"
// - Nếu errors.As() tìm thấy một *ParseError: in ra toàn bộ lỗi đã được bọc,
// sau đó, in "Fix line [Line]: [Message]" trên dòng mới
// - Nếu phân tích cú pháp thành công: in ra "Configuration parsed successfully"
_ = errors.Is // gợi ý: dùng errors.Is cho các lỗi sentinel
_ = errors.As // gợi ý: dùng errors.As cho các kiểu lỗi tùy chỉnh
_ = fmt.Println
}
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng OOP trong Go
Tệp bên ngoàiWorkspace và module trong GoPackage và importTên được export và không được exportGiới thiệu về OOP trong GoStruct như lớpĐịnh nghĩa phương thức cho structReceiver kiểu con trỏ và kiểu giá trịKhởi tạo structHàm khởi tạoÔn tập: máy tính đơn giản4Interface
Giới thiệu về interfaceTriển khai ngầm địnhInterface như một hợp đồngInterface rỗng (any)Khẳng định kiểuswitch kiểuKết hợp interfaceInterface Stringer và ErrorÔn tập: Máy tính hình học7Tính đóng gói
Trường được xuất và không được xuấtĐóng gói ở cấp packagePhương thức getter và setterẨn thông tin trong GoÔn tập: hồ sơ học sinh10Generics (Go 1.18 trở lên)
Giới thiệu về GenericsTham số kiểuRàng buộc kiểuStruct tổng quátGiải pháp thay thế cho phương thức tổng quátÔn tập: tập hợp tổng quát13Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếMẫu SingletonMẫu FactoryMẫu Abstract FactoryMẫu ObserverMẫu Strategy2Tìm hiểu chuyên sâu về kiểu dữ liệu và struct
Kiểu cơ bản và kiểu tổng hợpĐịnh nghĩa kiểu dữ liệu tùy chỉnhThẻ structStruct ẩn danhStruct lồng nhauGiá trị zero và giá trị mặc địnhÔn tập: sổ liên lạc5Ưu tiên kết hợp hơn kế thừa
Vì sao Go không có kế thừaNhập môn nhúng structTự động nâng phương thứcNhúng nhiều structNhúng và kết tậpChe khuất phương thức được nhúngÔn tập: hệ phân cấp nhân viên8Xử lý lỗi và lập trình hướng đối tượng
Giao diện errorCác kiểu lỗi tùy chỉnhBọc lỗi với fmt.ErrorfLỗi sentinelerrors.Is() và errors.As()Panic, defer và recoverÔn tập: trình phân tích tệp3Con trỏ và bộ nhớ
Cơ bản về con trỏ trong GoCon trỏ đến structTruyền theo giá trị và tham chiếuHàm new()Thu gom rác trong GoÔn tập: xây dựng danh sách liên kết6Đa hình trong Go
Đa hình qua interfaceĐịnh kiểu vịt trong GoQuy tắc thỏa mãn interfaceCác tập hợp đa hìnhTiêm phụ thuộcÔn tập: bộ xử lý thanh toán9Lập trình đồng thời và lập trình hướng đối tượng
Cơ bản về goroutineChannel và giao tiếpChannel có bộ đệm và không có bộ đệmCâu lệnh selectsync.Mutex và sync.RWMutexsync.WaitGroupThiết kế struct an toàn luồngÔn tập: worker poolTự luyện tập: Trình biên dịch Go online