Menu
CoddyTech

回顾:文件解析器

本课属于 Coddy GO 学习旅程的“面向对象编程”单元。第 58 课,共 107 课。

challenge icon

挑战

简单

让我们构建一个配置文件解析器,把本章介绍的所有错误处理技巧结合起来。你的解析器将使用自定义错误类型、哨兵错误、错误包装,以及通过 defer 正确执行清理操作,来处理多种失败情况。

你将把代码分成三个文件:

  • errors.go:为解析器定义错误基础结构。

    创建两个哨兵错误:

    • ErrEmptyContent,错误消息为 "file content is empty"
    • ErrInvalidFormat,错误消息为 "invalid configuration format"

    创建自定义错误类型 ParseError,包含 Line(int)和 Message(string)字段。它的 Error() 方法应返回:parse error at line [Line]: [Message]

  • parser.go:构建配置解析器,并分层处理错误。

    创建一个 ConfigParser 结构体,其中包含一个 parsed 布尔字段,用于记录解析是否成功完成。

    实现以下方法:

    • NewConfigParser() *ConfigParser:创建一个新的解析器,并将 parsed 设为 false
    • ParseLine(lineNum int, line string) error:验证单行内容。如果该行不包含 = 字符,则返回一个 *ParseError,其中包含行号和错误消息 "missing key-value separator"。否则返回 nil。
    • Parse(content string) error:主要的解析方法,需要:
      • 如果内容为空,则返回 ErrEmptyContent
      • 如果内容中不包含至少一个换行符,则返回 ErrInvalidFormat
      • 按换行符拆分内容,并对每一行调用 ParseLine(行号从 1 开始)
      • 如果 ParseLine 返回错误,则使用 "parsing configuration: %w" 包装该错误并返回
      • 如果解析成功,则将 parsed 设为 true 并返回 nil
    • Status() string:如果 parsed 为 true,则返回 "ready",否则返回 "not parsed"
  • main.go:读取配置内容,并演示如何全面检查错误。

    读取两行输入,将它们用换行符连接起来,组成配置内容。使用 defer 确保无论成功还是失败,最后都会打印解析器的状态。

    尝试解析后,同时使用 errors.Is() 和 errors.As() 检查错误,并根据错误原因提供具体反馈。

输入内容如下:

  • 第 1 行:配置内容的第一行
  • 第 2 行:配置内容的第二行

请使用以下具体消息处理错误:

  • 如果 errors.Is() 找到 ErrEmptyContent:打印 Error: No configuration provided
  • 如果 errors.Is() 找到 ErrInvalidFormat:打印 Error: Configuration must have multiple lines
  • 如果 errors.As() 找到 *ParseError:打印完整的包装错误,然后在新的一行打印 Fix line [Line]: [Message]
  • 如果解析成功:打印 Configuration parsed successfully

处理完结果(成功或错误)后,延迟打印的状态应显示在新的一行:Parser status: [status]

例如,输入 host=localhost 和 port=8080 时,输出应为:

Configuration parsed successfully
Parser status: ready

输入 host=localhost 和 invalid line 时,输出应为:

parsing configuration: parse error at line 2: missing key-value separator
Fix line 2: missing key-value separator
Parser status: not parsed

亲手试试

package main

import (
	"bufio"
	"errors"
	"fmt"
	"os"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// 读取配置的第一行
	line1, _ := reader.ReadString('\n')
	line1 = line1[:len(line1)-1] // 移除末尾的换行符
	
	// 读取配置的第二行
	line2, _ := reader.ReadString('\n')
	if len(line2) > 0 && line2[len(line2)-1] == '\n' {
		line2 = line2[:len(line2)-1]
	}
	
	// 用换行符连接这两行,形成配置内容
	content := line1 + "\n" + line2
	
	// 创建一个新的解析器
	parser := NewConfigParser()
	
	// TODO:使用 defer,确保最后输出解析器的状态
	// 格式:"Parser status: [status]"
	
	// TODO:尝试解析内容
	
	// TODO:使用 errors.Is() 和 errors.As() 处理错误
	// 如果 errors.Is() 找到 ErrEmptyContent:输出 "Error: No configuration provided"
	// 如果 errors.Is() 找到 ErrInvalidFormat:输出 "Error: Configuration must have multiple lines"
	// 如果 errors.As() 找到 *ParseError:输出完整的包装错误,
	//   然后在新的一行打印 "Fix line [Line]: [Message]"
	// - 如果解析成功:打印 "Configuration parsed successfully"
	
	_ = errors.Is // 提示:用 errors.Is 处理哨兵错误
	_ = errors.As // 提示:用 errors.As 处理自定义错误类型
	_ = fmt.Println
}

面向对象编程中的所有课

自己练习: Go 在线编译器