Конвертирование данных в Golang

В следующем уроке будет рассмотрен процесс конвертирования числовых, строковых и булевых типов данных в Golang. Для дальнейшей работы важно иметь о них представление. При необходимости использовать несколько переменных разного типа, нужно заранее конвертировать их значения в одинаковый тип.

Содержание статьи

Представьте, что вы пришли в магазин, где нужно сделать покупки по списку жены. Первым пунктом значится молоко, но больше ничего не уточняется — коровье, козье, соевое? Оно должно быть органическим, обезжиренным, 1%, 2%, цельным, конденсированным? Сколько пакетов? Станете ли вы звонить жене для уточнения?

Форум Гоферов

Мы работаем над форумом для программистов на Golang. Очень нужны модераторы которые хотят помочь с ответами для новичков и помочь в развитии Go-сообщества.

Go на Форум

Уроки, статьи и Видео

Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.

Go в ВК Go в Telegram

Жене наверняка будет действовать на нервы, если вы будете звонить ей из-за каждой мелочи. Какая капуста? Картофель белый или красный? Один или два килограмма? С другой стороны, если вы возьмете продукты на свое усмотрение и вернетесь домой с шоколадным молоком и чипсами, все закончится не очень хорошо.

Если представить, что в данной ситуации ваша жена программист, а вы компилятор, какой подход будет оптимальным со стороны Golang?

Можно ли смешивать типы в Golang?

Поведение переменной зависит от ее типа. Числа могут складываться, а строки объединяться. Для объединения двух строк нужно использовать оператор плюс:

При попытке объединить число и строку компилятор Go выведет ошибку:

В некоторых других языках программирования при попытке оперировать переменными разных типов производится попытка угадать намерения разработчика. К примеру, JavaScript и PHP могут вычесть 1 из строки «10»:

Компилятор Go не примет "10" - 1 и выдаст ошибку несовпадения типов. В Go вначале нужно конвертировать строку "10" в тип integer. Функция Atoi из пакета strconv выполнит конвертацию, однако если строка не содержит реального числа, появится ошибка. К моменту, когда ошибка будет исправлена, версия Go достигнет длины в четыре строки, что не очень удобно.

Учитывая все вышесказанное, если "10" является вводом пользователя или данными, полученными из внешнего источника, JavaScript и PHP также должны сделать проверку на реальность числа.

В языках, где тип принуждается, поведение кода менее предсказуемо, особенно для тех, кто не знаком с мириадами неясных поведений. Оператор плюс (+) в Java и JavaScript переводит числа в строки, конкатенируя их, в то время как PHP складывает числовые значения:

При попытке сделать нечто подобное в Go выведется ошибка.

Другим примером несовпадения типов является попытка сделать вычисления между целыми и вещественными числами. Числа с плавающей запятой вроде 365.2425 представлены вещественными типами float, а целые числа Go сопоставляет с типами integer:

Если бы все три переменные были целыми числами, вычисления были бы успешными, однако тогда значение переменной earthDays равнялось бы 365, а не 365.2425. Альтернативно, вычисления стали бы успешными, если бы переменные age и marsDays принадлежали к вещественному типу (41.0 и 687.0). Go не делает предположения касательно того, что вам нужно, но вы сами можете конвертировать типы. Это будет рассмотрено далее.

Вопрос для проверки:

Что будет результатом операции "10" - 1 в Go?

Конвертация числовых типов данных в Golang

Конвертация типов в Go является очень прямым. Если для вычисления нужно, чтобы тип целочисленной переменной age стал вещественным, требуется указать это следующим образом:

Переменные разных типов не смешиваются, однако с помощью конвертации типа вычисления в следующем листинге сработают:

Также можно конвертировать float в integer. Однако десятичная часть после точки при этом уберется, без округления:

Конвертация типов требуется для неподписанных (unsigned) и подписанных (signed) целочисленных типов, а также для типов с разными размерами. Всегда лучше конвертировать в тип с большим диапазоном, к примеру, из int8 в int32. Другие целочисленные конвертации не обходятся без рисков. uint32 может содержать значение в 4 миллиарда, однако int32 поддерживает числа не более 2 миллиардов. Также int может содержать отрицательное число, а uint нет.

Есть причина, по которой Go требует, чтобы конвертация типа точно значилась в коде. Каждый раз при использовании конвертации типа нужно учитывать возможные последствия.

Вопросы для проверки: 

  1. Как можно конвертировать переменную red в неподписанный 8-битный целочисленный тип?
  2. Каким будет результат сравнения age > marsAge?

На что обратить внимание при конвертации типов Go

В 1996 году беспилотная ракета Arianne 5 сошла с траектории полета и взорвалась через 40 секунд после запуска. Причиной была ошибка конвертации типа из float64 в int16 со значением, превышающим 32 767 — максимальное значение, которое может содержать int16. Необработанная ошибка оставила систему управления полетом без данных для траектории, из-за чего ракета отклонилась от курса, сломалась и в конечном итоге взорвалась.

Нам не известен код Arianne 5, мы также не являемся ракетостроителями, но давайте посмотрим, как Go разберется с похожей конвертацией. Если значение в диапазоне, как в следующем листинге, проблем не будет.

Если значение bh равно 32 767, что слишком велико для int16, результатом будет ожидаемый итог работы с целыми числами Go: тип переполняется, переходя к минимальному возможному значению для int16 а именно -32768.

Язык Ada, использовавшийся для Arianne 5, действует иначе. Конвертация типа из float64 в int16 со значением, превышающим допустимый диапазон, привела к ошибке в программном обеспечении. Согласно отчету, рассматриваемое вычисление имело смысл только до момента взлета, поэтому в данной ситуации подход Go может быть лучше. Однако в большинстве случаев лучше избегать неточностей в данных.

Для определения того, приведет ли конвертация типа в int16 к недопустимому значению, пакет math предоставляет константы min/max:

На заметку: Константы min/max являются нетипизированными, что допускает сравнение bh, вещественного значения, с MaxInt16. Подробнее о нетипизированных константах можете прочитать в статье Пакет Big — Крупные числа в Golang и примеры их использования.

Задание для проверки:

Напишите код, который определяет, является ли переменная v в пределах диапазона 8-битного неподписанного целочисленного типа.

Конвертация строк в Golang

Для конвертации типов rune или byte в string можно использовать такой же синтаксис, что нужен для конвертации числовых типов. Это показано в следующем листинге. Результат аналогичен с тем, что получается при использования специального символа %c , описанного в предыдущей статье про обработку строк, для отображения рун и байтов в виде символов.

Конвертация цифрового кода в строку работает также, как и в случае с типом integer. В конечном итоге, rune и byte являются просто другими названиями для int32 и uint8.

Для конвертирования чисел в string, каждое число нужно сначала конвертировать в код, начинающийся с 48 для символа 0, и заканчивающийся на 57 для символа 9. К счастью, функция Itoa пакета strconv (string conversion, то есть конвертация строк) делает это за нас, как показано в следующем листинге:

На заметку: Слово Itoa является аббревиатурой — integer to ASCII, то есть от целого числа к ASCII. Юникод является расширенным набором старого стандарта ASCII. Первые 128 кодов такие же — числа, используемые здесь, английские буквы и стандартные знаки препинания.

Другим способом конвертации числа в строку является использование Sprintf, своеобразной напарницы Printf, что возвращает строку string, но не отображает ее:

Есть еще один способ. В пакете strconv есть функция Atoi (ASCII to integer, то есть от ASCII к целому числу). Из-за того, что в строке могут быть какие-то кракозябры или слишком крупные числа, функция Atoi может вернуть ошибку:

Значение nil для err указывает, что ошибки нет, и все хорошо. В одном из следующих уроков это будет рассмотрено подробнее.

Вопрос для проверки:

Назовите две функции, что конвертируют целое число в строку.

Статические типы данных Golang

В Go при объявлении переменной ей присваивается тип, который в дальнейшем изменить нельзя. Это называется статической типизацией, которая облегчает оптимизацию, поэтому программы работают быстрее. При попытке использовать переменную со значением другого типа компилятор Go сообщит об ошибке:

В языках вроде JavaScript, Python и Ruby вместо статической используется динамическая типизация. В данных языках у каждого значения есть ассоциируемый тип, и переменные могут содержать значения любого типа. Данные языки позволили бы countdown изменить свой тип во время выполнения программы.

В Golang есть запасной выход на случай ситуаций, где тип непонятен. К пример, функция Println принимает как строки, так и числовые типы float или integer. В следующих уроках функция Println будет рассмотрена в деталях.

Конвертация булевых значений Golang

Группа функций Print отображает булевы значения true и false в виде текста. В следующем примере используется функция Sprintf для конвертации булевой переменной launch в текст. Если сделать конвертацию булева типа в численный или текстовых, простой оператор if подойдет лучше всего.

Обратная конвертация требует меньшее количество кода, так как вы можете присвоить результат условия напрямую переменной, как показано в следующем коде:

При попытке конвертировать булево значение через конструкции вроде string(false), int(false), bool(1) или bool("yes") компилятор Go выведет сообщение об ошибке.

На заметку: В языках программирования без выделенного типа bool значения 1 и 0 зачастую соответствуют значениям true и false соответственно. Однако в Go у булева типа нет численных эквивалентов.

Вопрос для проверки:

Как можно конвертировать булев тип в целое число, чтобы 1 соответствовала значению true, а 0 — false?

Заключение

  1. Конвертация между типами точна, что позволяет избежать двусмысленности;
  2. Пакет strconv предоставляет функции для конвертации строк в другие типы и наоборот.

Итоговое задание для проверки:

Напишите программу, что конвертирует строки в булевы значения:

  • Строки «true», «yes» или «1» соответствуют значению true;
  • Строки «false», «no» или «0» соответствуют значению false;
  • Для других значений выводит сообщение об ошибке.

Обратите внимание, что здесь можно использовать оператор switch, что принимает по несколько значений на случай case.