Описание типов пакета string языка программирования Go
type Builder
type Builder struct {
// содержит отфильтрованные или неэкспортируемые поля
}
Builder используется для эффективного построения строки с помощью методов Builder.Write. Он минимизирует копирование памяти. Нулевое значение готово к использованию. Не копируйте Builder, отличное от нуля.
Пример
package main
import (
"fmt"
"strings"
)
func main() {
var b strings.Builder
for i := 3; i >= 1; i-- {
fmt.Fprintf(&b, "%d...", i)
}
b.WriteString("ignition")
fmt.Println(b.String())
}
Output:
3...2...1...ignition
Объяснение Builder
Builder
из пакета strings
- это специальный тип для эффективного построения строк в Go. Он особенно полезен, когда вам нужно собрать строку из множества частей, так как он минимизирует копирование памяти и аллокации.
Основные преимущества Builder:
- Эффективность памяти - избегает лишних копирований данных
- Простота использования - предоставляет удобные методы для добавления содержимого
- Готовность к использованию - нулевое значение (
var b strings.Builder
) сразу готово к работе
Основные методы:
Write()
- добавляет байтыWriteString()
- добавляет строкуWriteByte()
- добавляет один байтWriteRune()
- добавляет руну (Unicode символ)String()
- возвращает собранную строкуReset()
- очищает builder для повторного использованияLen()
- возвращает текущую длинуCap()
- возвращает текущую емкостьGrow()
- заранее резервирует память
Примеры использования:
1. Базовый пример
package main
import (
"fmt"
"strings"
)
func main() {
var builder strings.Builder
builder.WriteString("Hello, ")
builder.WriteString("World!")
builder.WriteByte(' ')
builder.WriteRune('😊')
result := builder.String()
fmt.Println(result) // Hello, World! 😊
}
2. Эффективная конкатенация множества строк
func joinStrings(words []string) string {
var builder strings.Builder
// Заранее выделяем память для приблизительного размера
total := 0
for _, w := range words {
total += len(w)
}
builder.Grow(total)
for _, word := range words {
builder.WriteString(word)
}
return builder.String()
}
3. Построение JSON или HTML
func buildHTMLPage(title, body string) string {
var builder strings.Builder
builder.Grow(len(title) + len(body) + 50) // Примерный расчет
builder.WriteString("<html><head><title>")
builder.WriteString(title)
builder.WriteString("</title></head><body>")
builder.WriteString(body)
builder.WriteString("</body></html>")
return builder.String()
}
4. Повторное использование Builder
var builder strings.Builder
func logMessage(msg string) string {
builder.Reset() // Очищаем для повторного использования
builder.WriteString("[LOG] ")
builder.WriteString(time.Now().Format("2006-01-02 15:04:05"))
builder.WriteString(": ")
builder.WriteString(msg)
return builder.String()
}
Почему Builder лучше обычной конкатенации?
При обычной конкатенации строк с помощью +
:
s := "a" + "b" + "c" + "d"
Каждая операция создает новую строку, что приводит к лишним аллокациям памяти.
Builder же:
- Накапливает данные в буфере
- Увеличивает буфер только когда нужно
- Копирует данные минимальное количество раз
Когда использовать strings.Builder?
- Когда нужно собрать строку из многих частей
- Когда важна производительность (особенно в циклах)
- Когда заранее известен приблизительный размер результата (можно использовать Grow)
- Когда нужно минимизировать аллокации памяти
Builder особенно полезен в высоконагруженных участках кода, где важна эффективность работы со строками.
func (*Builder) Cap
func (b *Builder) Cap() int
Cap возвращает емкость базового байтового слайса builder. Это общее пространство, выделенное для строчки, которая создается, и включает в себя все уже записанные байты.
func (*Builder) Grow
func (b *Builder) Grow(n int)
Grow увеличивает емкость b, если это необходимо, чтобы гарантировать пространство для еще n байтов. После Grow(n) в b можно записать как минимум n байтов без дополнительного выделения памяти. Если n отрицательно, Grow вызывает панику.
func (*Builder) Len
func (b *Builder) Len() int
Len возвращает количество накопленных байтов; b.Len() == len(b.String()).
func (*Builder) Reset
func (b *Builder) Reset()
Reset сбрасывает Builder в пустое состояние.
func (*Builder) String
func (b *Builder) String() string
String возвращает накопленную строку.
func (*Builder) Write
func (b *Builder) Write(p []byte) (int, error)
Write добавляет содержимое p в буфер b. Write всегда возвращает len(p), nil.
func (*Builder) WriteByte
func (b *Builder) WriteByte(c byte) error
WriteByte добавляет байт c в буфер b. Возвращаемая ошибка всегда равна nil.
func (*Builder) WriteRune
func (b *Builder) WriteRune(r rune) (int, error)
WriteRune добавляет UTF-8-кодировку кодовой точки Unicode r в буфер b. Возвращает длину r и ошибку nil.
func (*Builder) WriteString
func (b *Builder) WriteString(s string) (int, error)
WriteString добавляет содержимое s в буфер b. Возвращает длину s и ошибку nil.
type Reader
type Reader struct {
// содержит отфильтрованные или неэкспортируемые поля
}
Reader реализует интерфейсы io.Reader, io.ReaderAt, io.ByteReader, io.ByteScanner, io.RuneReader, io.RuneScanner, io.Seeker и io.WriterTo, читая из строки. Нулевое значение для Reader работает как Reader пустой строки.
Объяснение Reader
Reader
из пакета strings
- это тип, который реализует множество интерфейсов для чтения из строки как из потока данных. Это мощный инструмент, когда вам нужно работать со строкой как с последовательностью байт или рун, поддерживающий различные операции чтения и навигации.
Основные особенности Reader:
- Чтение строки как потока - позволяет обрабатывать строку постепенно
- Реализация множества интерфейсов - совместим со стандартными интерфейсами ввода-вывода Go
- Поддержка позиционирования - можно перемещаться по строке
- Нулевое значение готово к работе -
var r strings.Reader
работает с пустой строкой
Реализуемые интерфейсы:
io.Reader
- базовое чтение байтовio.ReaderAt
- чтение с определенной позицииio.ByteReader
- чтение отдельных байтовio.ByteScanner
- чтение и возврат байтовio.RuneReader
- чтение рун (Unicode символов)io.RuneScanner
- чтение и возврат рунio.Seeker
- перемещение по строкеio.WriterTo
- запись содержимого в io.Writer
Основные методы:
Len() int
- количество непрочитанных байтовRead(b []byte) (n int, err error)
- чтение байтов в срезReadAt(b []byte, off int64) (n int, err error)
- чтение с определенной позицииReadByte() (byte, error)
- чтение одного байтаUnreadByte() error
- возврат последнего прочитанного байтаReadRune() (ch rune, size int, err error)
- чтение одной руныUnreadRune() error
- возврат последней прочитанной руныSeek(offset int64, whence int) (int64, error)
- перемещение по строкеWriteTo(w io.Writer) (n int64, err error)
- запись всего содержимого в WriterReset(s string)
- сброс Reader для чтения новой строки
Примеры использования:
1. Базовое чтение
package main
import (
"fmt"
"strings"
)
func main() {
r := strings.NewReader("Пример строки")
buf := make([]byte, 7)
n, err := r.Read(buf)
fmt.Printf("Прочитано %d байт: %q\n", n, buf[:n])
// Прочитано 7 байт: "Пример"
}
2. Чтение рун
func printRunes(s string) {
r := strings.NewReader(s)
for {
ch, size, err := r.ReadRune()
if err != nil {
break
}
fmt.Printf("%c (%d байт)\n", ch, size)
}
}
// Для строки "Привет" выведет:
// П (2 байт)
// р (2 байт)
// и (2 байт)
// в (2 байт)
// е (2 байт)
// т (2 байт)
3. Использование Seek для навигации
func seekExample() {
r := strings.NewReader("Hello, World!")
// Пропускаем "Hello, "
r.Seek(7, io.SeekStart)
b, _ := r.ReadByte()
fmt.Printf("Первый байт после Seek: %c\n", b) // W
}
4. Чтение с определенной позиции (ReadAt)
func readAtExample() {
r := strings.NewReader("abcdefghij")
buf := make([]byte, 3)
r.ReadAt(buf, 4)
fmt.Println(string(buf)) // efg
}
5. Запись содержимого в другой Writer
func writeToExample(w io.Writer) {
r := strings.NewReader("Данные для записи")
r.WriteTo(w) // Запишет всю строку в w
}
6. Комбинирование методов
func processString(s string) {
r := strings.NewReader(s)
// Читаем первые 5 байт
prefix := make([]byte, 5)
r.Read(prefix)
// Читаем оставшуюся часть по рунам
for {
ch, _, err := r.ReadRune()
if err != nil {
break
}
// Обработка руны...
}
}
Когда использовать strings.Reader?
- Когда нужно читать строку по частям - как из потока
- Для совместимости с API, ожидающими io.Reader - многие функции принимают io.Reader
- Когда нужен произвольный доступ к частям строки - через ReadAt/Seek
- Для обработки Unicode текста - через ReadRune
- Когда нужно повторно “проиграть” часть данных - через UnreadByte/UnreadRune
Преимущества перед работой со строкой напрямую:
- Постепенная обработка - не нужно загружать всю строку в память
- Стандартные интерфейсы - совместимость с другими пакетами
- Гибкость навигации - возможность перемещаться по строке
- Безопасность - Reader неизменяем, исходная строка защищена от модификаций
Reader особенно полезен при парсинге, обработке больших строк или когда вам нужно интегрировать строку в систему, работающую с потоками данных.
func NewReader
func NewReader(s string) *Reader
NewReader возвращает новый Reader, читающий из s. Он похож на bytes.NewBufferString, но более эффективен и не поддается записи.
func (*Reader) Len
func (r *Reader) Len() int
Len возвращает количество байтов непрочитанной части строки.
func (*Reader) Read
func (r *Reader) Read(b []byte) (n int, err error)
Read реализует интерфейс io.Reader.
func (*Reader) ReadAt
func (r *Reader) ReadAt(b []byte, off int64) (n int, err error)
ReadAt реализует интерфейс io.ReaderAt.
func (*Reader) ReadByte
func (r *Reader) ReadByte() (byte, error)
ReadByte реализует интерфейс io.ByteReader.
func (*Reader) ReadRune
func (r *Reader) ReadRune() (ch rune, size int, err error)
ReadRune реализует интерфейс io.RuneReader.
func (*Reader) Reset
func (r *Reader) Reset(s string)
Reset сбрасывает Reader для чтения из s.
func (*Reader) Seek
func (r *Reader) Seek(offset int64, whence int) (int64, error)
Seek реализует интерфейс io.Seeker.
func (*Reader) Size
func (r *Reader) Size() int64
Size возвращает исходную длину базовой строки. Size — это количество байтов, доступных для чтения с помощью Reader.ReadAt. Возвращаемое значение всегда одинаково и не зависит от вызовов других методов.
func (*Reader) UnreadByte
func (r *Reader) UnreadByte() error
UnreadByte реализует интерфейс io.ByteScanner.
func (*Reader) UnreadRune
func (r *Reader) UnreadRune() error
UnreadRune реализует интерфейс io.RuneScanner.
func (*Reader) WriteTo
func (r *Reader) WriteTo(w io.Writer) (n int64, err error)
WriteTo реализует интерфейс io.WriterTo.
type Replacer
type Replacer struct {
// содержит отфильтрованные или неэкспортируемые поля
}
Replacer заменяет список строк на замены. Он безопасен для одновременного использования несколькими goroutines.
Объяснение Replacer
Replacer
- это мощный инструмент для замены подстрок в Go, который оптимизирован для многократного использования и безопасен для конкурентного использования в горутинах.
Основные характеристики
- Горутин-безопасность: Можно использовать из нескольких горутин одновременно
- Оптимизированная производительность: Быстрее, чем последовательные вызовы
strings.Replace()
- Поддержка множественных замен: Может обрабатывать множество замен за один проход по строке
- Кэширование: Сохраняет оптимизированное состояние для повторного использования
Создание Replacer
Создается с помощью strings.NewReplacer()
, который принимает пары “старая строка - новая строка”:
replacer := strings.NewReplacer(
"старое1", "новое1",
"старое2", "новое2",
// ...
)
Основные методы
Replace(input string) string
Заменяет все вхождения заданных подстрок в входной строке:
func main() {
r := strings.NewReplacer("мир", "Go", "Привет", "Здравствуй")
result := r.Replace("Привет, мир!")
fmt.Println(result) // "Здравствуй, Go!"
}
WriteString(w io.Writer, s string) (n int, err error)
Записывает строку с выполненными заменами непосредственно в io.Writer
:
func writeReplaced(w io.Writer) {
r := strings.NewReplacer("\n", "\\n", "\t", "\\t")
r.WriteString(w, "Строка с\nпереносами\tи табуляцией")
// Запишет: "Строка с\nпереносами\tи табуляцией"
}
Примеры использования
1. Экранирование специальных символов
func escapeHTML(s string) string {
replacer := strings.NewReplacer(
"&", "&",
"<", "<",
">", ">",
"\"", """,
)
return replacer.Replace(s)
}
2. Транслитерация
func transliterate(s string) string {
return strings.NewReplacer(
"щ", "shh", "ш", "sh", "ч", "ch",
"я", "ya", "ю", "yu", "ж", "zh",
).Replace(s)
}
3. Множественные замены в большом тексте
func processText(text string) string {
replacer := strings.NewReplacer(
"красный", "синий",
"яблоко", "апельсин",
"собака", "кошка",
)
return replacer.Replace(text)
}
4. Обработка логов
func cleanLog(log string) string {
return strings.NewReplacer(
"\n", " | ",
"\t", " ",
"\r", "",
).Replace(log)
}
Особенности производительности
- Однопроходная обработка: Все замены выполняются за один проход по строке
- Оптимизация для повторного использования: Созданный
Replacer
кэширует свои внутренние структуры - Эффективность для большого числа замен: Лучше использовать один
Replacer
со многими заменами, чем несколько вызововReplace
Рекомендации по использованию
- Для многократного использования: Создавайте
Replacer
один раз и используйте многократно - Для большого числа замен: Когда нужно сделать более 2-3 замен в одной строке
- В конкурентной среде: Когда замены могут выполняться из нескольких горутин
- Для записи в Writer: Когда нужно сразу записывать результат в выходной поток
Сравнение с другими методами замены
Метод | Многопоточность | Множественные замены | Производительность | Повторное использование |
---|---|---|---|---|
strings.Replace() |
Нет | Нет (одна замена) | Средняя | Нет |
regexp.Regexp.ReplaceAllString() |
Да (с sync) | Да | Низкая (для простых замен) | Да |
strings.Replacer |
Да | Да | Высокая | Да |
Replacer
особенно полезен в веб-серверах, обработчиках запросов и других высоконагруженных приложениях, где требуется выполнять одни и те же замены многократно.
func NewReplacer
func NewReplacer(oldnew ...string) *Replacer
NewReplacer возвращает новый Replacer из списка пар старых и новых строк. Замены выполняются в том порядке, в котором они появляются в целевой строке, без перекрывающихся совпадений. Сравнение старых строк выполняется в порядке аргументов.
NewReplacer вызывает панику, если ему передано нечетное количество аргументов.
Пример
package main
import (
"fmt"
"strings"
)
func main() {
r := strings.NewReplacer("<", "<", ">", ">")
fmt.Println(r.Replace("This is <b>HTML</b>!"))
}
Output:
This is <b>HTML</b>!
func (*Replacer) Replace
func (r *Replacer) Replace(s string) string
Replace возвращает копию s со всеми выполненными заменами.
func (*Replacer) WriteString
func (r *Replacer) WriteString(w io.Writer, s string) (n int, err error)
WriteString записывает s в w со всеми выполненными заменами.