Menu
Coddy logo textTech

Итоги — Парсер файлов

Часть раздела Объектно-ориентированное программирование путешествия по GO на Coddy — урок 58 из 107.

challenge icon

Задание

Легко

Давайте создадим парсер конфигурационных файлов, который объединит все техники обработки ошибок из этой главы. Ваш парсер будет обрабатывать несколько сценариев сбоев с использованием пользовательских типов ошибок, сигнальных ошибок (sentinel errors), оборачивания ошибок и правильной очистки ресурсов с помощью 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 — возвращает "ready", если parsed равно true, иначе "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
}

Все уроки раздела Объектно-ориентированное программирование