Откройте файл из прошлого урока: в книге ровно один лист с именем Sheet1, и выглядит это как черновик, а не как готовый продукт. Бизнес ждёт структуру: сводка на первой вкладке, детализация по дням на второй, справочники и примечания в конце. Заодно вспомним, что листы — это не просто вкладки для красоты: на них ссылаются формулы, диаграммы и настройки печати, а порядок вкладок определяет, что первым увидит человек, который файл откроет. В excelize весь этот слой закрывается горсткой методов дескриптора File — создание, активация, навигация, переименование, копирование, удаление — и все они следуют одной схеме: работа с листами идёт по именам и индексам, и путаница между ними источник половины ошибок новичков. В этом уроке разложим оба измерения по полочкам: где API ждёт имя, где индекс, что возвращает NewSheet в актуальных версиях и почему удаление листа — операция с последствиями, а не с кнопкой undo.
NewSheet: новый лист и его индекс#
newsheet excelize-вызов выглядит так: f.NewSheet("Отчёт") — и в актуальных версиях v2 возвращает пару значений: индекс нового листа и ошибку. Индекс — это позиция листа в книге, нумерация начинается с единицы: свежая книга имеет Sheet1 с индексом 1, и первый же NewSheet получит индекс 2. Ошибка возвращается, когда имя невалидно — пустая строка, запрещённые символы или дубликат существующего листа; в старых туториалах сигнатура без ошибки, поэтому код из статей пятилетней давности не скомпилируется — та же история, что с путями импорта, только здесь меняется число возвращаемых значений. Порядок вкладок в файле соответствует порядку создания: кто первым сделал NewSheet, тот раньше в списке. Переставлять вкладки руками excelize не даёт — если порядок важен, создавайте листы в нужной последовательности или переименовывайте существующие, а не плодите новые. И запомните: NewSheet не делает лист активным — открытие файла покажет ту вкладку, которую вы назначили активной отдельно, о чём дальше.
Книга с тремя листами
GOf := excelize.NewFile()
defer f.Close()
for _, name := range []string{"Отчёт", "Детализация", "Справочник"} {
if _, err := f.NewSheet(name); err != nil {
panic(err)
}
}
if err := f.SaveAs("workbook.xlsx"); err != nil {
panic(err)
}NewSheet в цикле создаёт три листа после дефолтного Sheet1: Отчёт получит индекс 2, Детализация — 3, Справочник — 4. Индексы из цикла не сохраняем — они нужны только там, где вы дальше работаете по ним. Ошибку проверяем честно: дубликат имени или запрещённые символы здесь не прощаются. Файл откроется на Sheet1 — активным листом мы его пока не меняли.
SetActiveSheet: какая вкладка откроется первой#
Активный лист — это вкладка, которую Excel покажет при открытии файла, и управляется он парой методов: f.SetActiveSheet(index) назначает, f.GetActiveSheetIndex() возвращает текущий индекс. Логика для отчётов простая и почти всегда одна: открываем то, ради чего файл генерировался. Сводка для директора — активной становится вкладка со сводкой, а не технический первый лист, на котором по инерции лежали данные для расчётов. Активный лист — не вопрос вкуса, а вопрос обслуживания: файл, который открывается на правильной вкладке, экономит каждому читателю пару кликов и вопрос в чат «а где смотреть». Практический нюанс: SetActiveSheet принимает именно индекс — то самое число из NewSheet, поэтому в реальном коде возвращаемое значение сохраняют в переменную и передают дальше. Если лист с индексом отсутствует, метод молча ничего не изменит: ошибки не будет, но и эффекта тоже — ещё одна причина хранить индексы осознанно, а не пересчитывать на пальцах. И раз уж называли листы русскими словами — так и оставляйте: имена вкладок кодируются в XML в UTF-8, и Отчёт откроется так же корректно, как Report.
f.DeleteSheet("Черновик") принимает имя листа — передать индекс вместо имени не получится. Последствий два: сначала убедитесь, что удаляете тот лист, потому что undo не будет; затем проверьте ссылки — формулы и диаграммы других листов, указывающие на удалённый, превратятся в #REF!, и Excel молча покажет битые значения.
Навигация: GetSheetName и GetSheetMap#
Навигация по книге строится на двух чтениях. f.GetSheetName(index) возвращает имя листа по индексу: единица — первый лист книги, и для невалидного индекса метод вернёт пустую строку, не паникуя. Обратное преобразование тоже нужно, но здесь аккуратнее: метод GetSheetIndex существует, однако его поведение на отсутствующих именах менялось между версиями, поэтому в новом коде я предпочитаю явный проход по f.GetSheetMap() — карте индекс-имя, по которой видно всю книгу целиком. Итерируясь по GetSheetMap, вы за один вызов получаете и количество листов, и их названия с индексами — этого хватает и для проверки, что файл сгенерировался с правильной структурой, и для поиска листа с нужным именем перед чтением данных. Такие проходы удобно оставлять в тестах генерации: структура файла — тоже контракт, и она должна проверяться, а не доверяться памяти. Запомните асимметрию: имя по индексу — тривиально, индекс по имени — с оговорками по версиям, поэтому архитектура кода, которая держит индексы из NewSheet в переменных, надёжнее архитектуры, которая ищет их обратно по строкам в рантайме.
Аптечка методов для листов#
- NewSheet(имя) — создаёт лист, возвращает индекс и ошибку (актуальные версии v2).
- SetActiveSheet(индекс) — назначает вкладку, которая откроется первой.
- GetActiveSheetIndex() — возвращает индекс активного листа.
- GetSheetName(индекс) — имя листа по индексу, пустая строка для невалидного индекса.
- GetSheetMap() — карта индекс-имя по всей книге: структура одним вызовом.
- RenameSheet(старое, новое) — переименование по именам.
- CopySheet(источник, цель) — копирование по индексам.
- DeleteSheet(имя) — удаление по имени, без undo и с эффектом #REF! в формулах.
Переименование, копирование и чтение карты
GOf := excelize.NewFile()
defer f.Close()
f.SetCellValue("Sheet1", "A1", "Итог за квартал")
if err := f.RenameSheet("Sheet1", "Сводка"); err != nil {
panic(err)
}
draft, err := f.NewSheet("Шаблон")
if err != nil {
panic(err)
}
if err := f.CopySheet(1, draft); err != nil {
panic(err)
}
for index, name := range f.GetSheetMap() {
fmt.Println(index, name)
}RenameSheet работает по именам: Sheet1 становится Сводкой, индекс при этом не меняется. CopySheet копирует содержимое листа с индекса 1 на существующий лист Шаблон — цель обязана существовать, поэтому сначала NewSheet. Обратите внимание на честную оговорку документации: картинки и диаграммы при копировании не переносятся, только данные, формулы и оформление ячеек. GetSheetMap в конце печатает структуру книги для проверки.
Ограничения имён листов и порядок вкладок#
Excel накладывает ограничения на имена листов, и excelize транслирует их в ошибки NewSheet и RenameSheet: имя короче 32 символов, то есть максимум 31; запрещены двоеточие, обратный и обычный слэш, вопросительный знак, звёздочка и квадратные скобки; нельзя пустое имя и нельзя History — резервное имя для журнала изменений общих книг. Звучит как список для зубрёжки, но всплывает ровно в двух местах: когда вы генерируете вкладки с датами вроде 2024/06 — слэш сломает создание, и когда названия приходят из пользовательских данных, где звёздочка встречается свободно. Санитизируйте имена одним местом в коде: функция, заменяющая запрещённые символы на дефис и обрезающая до 31 символа, стоит пять строк и экономит вечер отладки. Порядок вкладок при этом — порядок создания, и переименование его не меняет: Сводка останется на своей позиции, как ни назови. Поэтому последовательность NewSheet-вызовов — это и есть дизайн файла, и относиться к ней стоит как к части контракта отчёта.
Методы листов: сигнатуры и правила#
| Метод | Сигнатура | Что важно помнить |
|---|---|---|
| NewSheet | (имя string) (int, error) | индексы с единицы; ошибка на дубликате и запрещённых символах |
| SetActiveSheet | (индекс int) | не делает лист активным автоматически при создании |
| GetSheetName | (индекс int) string | пустая строка для невалидного индекса |
| GetSheetMap | () map[int]string | вся книга одним вызовом: индексы и имена |
| CopySheet | (источник, цель int) error | цель должна существовать; картинки и диаграммы не копируются |
| DeleteSheet | (имя string) error | по имени; формулы-ссылки превращаются в #REF! |
Кейс, который делает эти методы неакадемическими. Сервис генерировал ежемесячные отчёты: каждый месяц новый файл с листом на месяц, а через год накопилась книга-архив, где двенадцать почти одинаковых листов — сводка, детализация, методология. Ручная сборка такого файла — час монотонной работы и постоянные опечатки, поэтому её автоматизировали так: шаблонный лист со стилями и итоговыми формулами создаётся один раз, и на каждый месяц CopySheet копирует его под новым именем, после чего код заполняет ячейки данными периода. Отдельно приятная деталь: активным делается лист текущего месяца — бухгалтер открывает файл и сразу видит свежую вкладку, а не январь. Единственная ловушка, которая проявилась в продакшене: в шаблоне была логотип-картинка, и CopySheet её не перенёс — картинки в новый лист пришлось добавлять отдельно, потому что копирование, как сказано в документации, про данные и оформление, а не про рисунки. Прежде чем строить такую схему, проверьте свой шаблон: диаграммы и изображения не переживут копирование.
Песочница сайта не пишет файлы на диск, поэтому книгу с листами открывайте локально: go run, затем Excel или LibreOffice — глазами убедитесь, что вкладки на месте и активная та, которую назначили. Интерактивы урока тренируют сигнатуры и порядок вызовов без запуска.
Проверка: NewSheet
Что возвращает f.NewSheet("Отчёт") в актуальных версиях excelize v2?
Проверка: удаление листа
Какой параметр принимает f.DeleteSheet?
Что выведет программа?
Прочитайте код и выберите, что он выведет:
f := excelize.NewFile()
f.NewSheet("Отчёт")
name := f.GetSheetName(1)
fmt.Println(name)Создайте и активируйте лист
Допишите создание листа с проверкой ошибки и его активацию.
Соберите работу с листом
Расставьте строки в порядке выполнения: создание книги, новый лист, обработка ошибки, активация, сохранение, закрытие.
Строки
Ваша программа
Нажимайте на строки слева, чтобы собрать программу
Карточки: листы книги
Структура книги теперь в ваших руках: NewSheet создаёт вкладки с индексами, SetActiveSheet решает, что увидит читатель первым, GetSheetMap показывает всю книгу, а RenameSheet и CopySheet превращают ручную сборку отчётов в цикл. Дальше — данные: в уроке про запись ячеек разберём SetCellValue и типы значений, которые excelize кладёт в xlsx, включая ловушку длинных номеров, а затем научимся читать чужие файлы через GetCellValue в уроке про чтение ячеек. Защита готовых вкладок от случайных правок — отдельная тема: ProtectSheet с паролем рассматривается ближе к середине курса. Если план на будущее — отчётная система с вкладками и диаграммами, гляньте заранее итоговый проект, куда все эти методы соберутся вместе. Домашнее задание — собрать книгу из трёх листов с осмысленными именами.
Песочница урока
Редактируйте код и запускайте его прямо здесь — интерпретатор работает в браузере, ничего устанавливать не нужно. За каждый запуск — +2 XP.
Запустите код, чтобы увидеть вывод.
Коротко: что запомнить
- NewSheet возвращает индекс и ошибку; индексы листов начинаются с единицы.
- SetActiveSheet назначает вкладку, которую Excel откроет первой, — обычно сводку, а не технический лист.
- Навигация: GetSheetName по индексу, GetSheetMap для структуры всей книги.
- RenameSheet и DeleteSheet работают по именам, CopySheet — по индексам существующих листов.
- CopySheet не переносит картинки и диаграммы — их добавляют отдельно.
- Имена листов: до 31 символа, без : \ / ? * [ ], имя History запрещено.
Домашнее задание
Соберите программу, которая создаёт книгу с четырьмя листами: переименуйте Sheet1 в Сводка, добавьте Детализация и Справочник через NewSheet, сделайте Сводку активной и сохраните файл. Распакуйте workbook.xlsx и проверьте по xl/workbook.xml, что порядок вкладок соответствует порядку создания.
Личные заметкиПусто
Вопросы и ответы по уроку
Как переставить листы в нужном порядке?
Прямого метода перемещения вкладок excelize не предоставляет — порядок соответствует порядку создания. Планируйте последовательность NewSheet-вызовов под желаемую структуру или создавайте листы заранее в нужном порядке и заполняйте позже; переименование существующих листов порядок не меняет.
Что будет, если создать лист с существующим именем?
В актуальных версиях NewSheet вернёт ошибку — дубликаты имён запрещены на уровне формата Excel. Обрабатывайте её явно: попытка молча проигнорировать ошибку приведёт к книге без ожидаемого листа и заполнению данных в несуществующий дескриптор.
Почему CopySheet не скопировал диаграмму и логотип?
Так задокументировано: копируются данные, формулы и оформление ячеек, а изображения, диаграммы и рисунки — нет. Схема «шаблон плюс копии» работает, если графику добавляют в каждую копию отдельно через AddPicture и AddChart.
Термины курса
Понятия, на которых строится этот курс, — короткие определения в глоссарии. Наведите на термин, чтобы вспомнить, откройте карточку для подробностей и ссылок на практику.
Изучайте дальше
Следующая ступень учебного пути
Фоновые задачи в Go: планировщик на robfig/cron
Курс по cron в Go: robfig/cron v3, синтаксис выражений, пояса, wrappers, конкурентность и graceful shutdown. 25 уроков с примерами и проектом.
- Собирать планировщик на robfig/cron v3 и запускать задачи по расписанию
- Читать и писать cron-выражения: пять полей, спецсимволы, @daily
- Управлять жизненным циклом: Start, Stop, Entry, Remove
- Держать часовые пояса и секундные расписания под контролем