---
title: "Листы книги: NewSheet, навигация и удаление"
description: "Excelize листы: NewSheet с индексом, SetActiveSheet, навигация GetSheetName и GetSheetMap, переименование, копирование и удаление. Ловушки имён вкладок."
course: "Excel в Go: чтение и генерация xlsx на excelize"
url: "https://golangmaster.ru/courses/excelize/lessons/03-listy-knigi/"
published: "2026-09-19"
updated: "2026-09-21"
minutes: 16
---

# Листы книги: NewSheet, навигация и удаление

*Курс «Excel в Go: чтение и генерация xlsx на excelize», урок 3 из 25. ~16 минут. Обновлено 21.09.2026. Источник: https://golangmaster.ru/courses/excelize/lessons/03-listy-knigi/*

NewSheet в excelize — первый шаг от черновика с Sheet1 к книге, которую приятно открывать: листы Отчёт, Детализация и Справочник вместо безликой вкладки по умолчанию. В этом уроке разберём excelize листы со всех сторон: создание с индексами, активную вкладку, навигацию через GetSheetName и GetSheetMap, переименование, копирование и удаление — плюс ограничения имён, о которых Excel расскажет только в диалоге ошибки.

Откройте файл из прошлого урока: в книге ровно один лист с именем Sheet1, и выглядит это как черновик, а не как готовый продукт. Бизнес ждёт структуру: сводка на первой вкладке, детализация по дням на второй, справочники и примечания в конце. Заодно вспомним, что листы — это не просто вкладки для красоты: на них ссылаются формулы, диаграммы и настройки печати, а порядок вкладок определяет, что первым увидит человек, который файл откроет. В excelize весь этот слой закрывается горсткой методов дескриптора File — создание, активация, навигация, переименование, копирование, удаление — и все они следуют одной схеме: работа с листами идёт по именам и индексам, и путаница между ними источник половины ошибок новичков. В этом уроке разложим оба измерения по полочкам: где API ждёт имя, где индекс, что возвращает NewSheet в актуальных версиях и почему удаление листа — операция с последствиями, а не с кнопкой undo.

### NewSheet: новый лист и его индекс

newsheet excelize-вызов выглядит так: f.NewSheet("Отчёт") — и в актуальных версиях v2 возвращает пару значений: индекс нового листа и ошибку. Индекс — это позиция листа в книге, нумерация начинается с единицы: свежая книга имеет Sheet1 с индексом 1, и первый же NewSheet получит индекс 2. Ошибка возвращается, когда имя невалидно — пустая строка, запрещённые символы или дубликат существующего листа; в старых туториалах сигнатура без ошибки, поэтому код из статей пятилетней давности не скомпилируется — та же история, что с путями импорта, только здесь меняется число возвращаемых значений. Порядок вкладок в файле соответствует порядку создания: кто первым сделал NewSheet, тот раньше в списке. Переставлять вкладки руками excelize не даёт — если порядок важен, создавайте листы в нужной последовательности или переименовывайте существующие, а не плодите новые. И запомните: NewSheet не делает лист активным — открытие файла покажет ту вкладку, которую вы назначили активной отдельно, о чём дальше.

### Книга с тремя листами

```go
f := 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.

> **DeleteSheet удаляет по имени, а не по индексу.** 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! в формулах.

### Переименование, копирование и чтение карты

```go
f := 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?

- ✅ Индекс нового листа и ошибку
- Только индекс без ошибки
- Имя созданного листа
- Ничего — только изменяет книгу

Сигнатура v2.8.0 и новее — (int, error): индекс листа в книге и ошибка на невалидном или дублирующемся имени. В старых туториалах сигнатура без ошибки — их код не скомпилируется против современных версий библиотеки.

### Проверка: удаление листа

**Вопрос:** Какой параметр принимает f.DeleteSheet?

- ✅ Имя листа
- Индекс листа
- Указатель на File
- Оба варианта — индекс или имя

DeleteSheet принимает строку с именем листа — индекс не подойдёт. Перед удалением проверяйте ссылки: формулы и диаграммы других листов, указывающие на удаляемый, сломаются с #REF!, а undo в файле, который вы генерируете кодом, не существует.

### Что выведет программа?

```go
f := excelize.NewFile()

f.NewSheet("Отчёт")

name := f.GetSheetName(1)

fmt.Println(name)
```

**Что выведет программа?**

- ✅ Sheet1
- Отчёт
- Пустую строку
- Панику про невалидный индекс

Индексы листов начинаются с единицы, и первый лист свежей книги — Sheet1. NewSheet("Отчёт") получил индекс 2, а GetSheetName(1) вернул имя первого листа. Паники нет и для невалидного индекса — метод вернул бы пустую строку.

### Создайте и активируйте лист

Допишите создание листа с проверкой ошибки и его активацию.

```go
idx, err := f.___("Отчёт")

if err != nil {
    panic(err)
}

f.___(idx)
```

Подсказка: Создание — NewSheet, назначение открытой по умолчанию вкладки — SetActiveSheet.

Правильные ответы в пропусках: `NewSheet`, `SetActiveSheet`

### Соберите работу с листом

Расставьте строки в порядке выполнения: создание книги, новый лист, обработка ошибки, активация, сохранение, закрытие.

Правильный порядок строк:

1. `f := excelize.NewFile()`
2. `idx, err := f.NewSheet("Отчёт")`
3. `if err != nil { panic(err) }`
4. `f.SetActiveSheet(idx)`
5. `err = f.SaveAs("workbook.xlsx")`
6. `f.Close()`

### Карточки: листы книги

- **NewSheet** — Создаёт лист, возвращает индекс и ошибку. Индексы с единицы: Sheet1 — первый. Ошибка на дубликате и запрещённых символах.
- **SetActiveSheet** — Назначает вкладку, которая откроется при открытии файла. Принимает индекс — сохраняйте значение из NewSheet.
- **GetSheetMap** — Карта индекс-имя по всей книге: структура файла одним вызовом. Надёжнее обратного поиска индекса по имени.
- **CopySheet** — Копирует лист по индексам: источник и существующая цель. Данные, формулы и оформление переносятся, картинки и диаграммы — нет.
- **DeleteSheet** — Удаляет лист по имени. Undo нет; формулы ссылающиеся на лист, становятся #REF!.
- **Ограничения имён** — Максимум 31 символ; запрещены : \ / ? * [ ] и имя History. Санитизация имён в одном месте кода экономит отладку.

Структура книги теперь в ваших руках: NewSheet создаёт вкладки с индексами, SetActiveSheet решает, что увидит читатель первым, GetSheetMap показывает всю книгу, а RenameSheet и CopySheet превращают ручную сборку отчётов в цикл. Дальше — данные: в уроке про [запись ячеек](/courses/excelize/lessons/04-zapis-yacheek/) разберём SetCellValue и типы значений, которые excelize кладёт в xlsx, включая ловушку длинных номеров, а затем научимся читать чужие файлы через GetCellValue в уроке про [чтение ячеек](/courses/excelize/lessons/05-chtenie-yacheek/). Защита готовых вкладок от случайных правок — отдельная тема: [ProtectSheet с паролем](/courses/excelize/lessons/16-zashchita-lista/) рассматривается ближе к середине курса. Если план на будущее — отчётная система с вкладками и диаграммами, гляньте заранее [итоговый проект](/courses/excelize/lessons/25-itogovyj-proekt/), куда все эти методы соберутся вместе. Домашнее задание — собрать книгу из трёх листов с осмысленными именами.

## Коротко: что запомнить

- 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.
