← Все этапы
05 · ПРАКТИКАУрок 11 из 1260 мин

Проект: отчёт расходов из CSV

Параллельно анализируем выгрузки, проверяем строки и сравниваем расходы по файлам.

Каждая выгрузка — отдельный отчёт

CSV содержит записи с полями. Для каждой строки проверяйте наличие категории и целочисленную неотрицательную сумму; повреждённый CSV возвращайте как ошибку. Независимые документы можно анализировать параллельно, собирая готовые сводки в одном месте.

package main

import (
	"encoding/csv"
	"fmt"
	"io"
	"log"
	"strconv"
	"strings"
	"sync"
)

type summary struct { cents, invalid int }

func summarizeCSV(text string) (summary, error) {
	var out summary
	reader := csv.NewReader(strings.NewReader(text))
	reader.FieldsPerRecord = -1
	if _, err := reader.Read(); err != nil {
		return out, fmt.Errorf("read CSV header: %w", err)
	}
	for {
		record, err := reader.Read()
		if err == io.EOF {
			return out, nil
		}
		if err != nil {
			return out, fmt.Errorf("read CSV record: %w", err)
		}
		if len(record) != 2 || strings.TrimSpace(record[0]) == "" {
			out.invalid++
			continue
		}
		cents, err := strconv.Atoi(record[1])
		if err != nil || cents < 0 {
			out.invalid++
			continue
		}
		out.cents += cents
	}
}

func main() {
	documents := []string{
		"category,cents\nfood,1200\ntravel,300\nfood,invalid",
		"category,cents\nbooks,800",
		"category,cents\nrefund,-100",
	}
	results := make(chan summary, len(documents))
	var workers sync.WaitGroup
	for _, document := range documents {
		workers.Add(1)
		go func(text string) {
			defer workers.Done()
			item, err := summarizeCSV(text)
			if err != nil {
				log.Printf("summarize CSV: %v", err)
				return
			}
			results <- item
		}(document)
	}
	workers.Wait()
	close(results)
	totalCents, rejected := 0, 0
	for item := range results {
		totalCents += item.cents
		rejected += item.invalid
	}
	fmt.Println("расходы в центах:", totalCents, "отклонённых записей:", rejected)
}

Анализируйте строки внутри одного CSV последовательно, а разные выгрузки запускайте в отдельных горутинах. Возвращайте сумму и число отклонённых строк как локальный результат, не используя общий изменяемый счётчик.

Практика

Попробуйте сами

Обработайте параллельно два CSV-текста с заголовком category,cents: первый содержит food,1200, travel,300, food,invalid; второй — books,800, refund,-100. Отклоняйте пустую категорию, отрицательное и нечисловое значение. Выведите общий итог и число отклонённых строк.

Проверка результата

Как понять, что получилось

На учебных строках с суммами 1200, 300 и 800 центов общий итог равен 2300; нечисловая и отрицательная записи дают ровно два отклонения. Повторный запуск даёт тот же результат.

Проект: отчёт расходов из CSV | Go | WebSchool · Go