В следующем уроке будет рассмотрен процесс конвертирования числовых, строковых и булевых типов данных в Golang. Для дальнейшей работы важно иметь о них представление. При необходимости использовать несколько переменных разного типа, нужно заранее конвертировать их значения в одинаковый тип.
Содержание статьи
- Можно ли смешивать типы в Golang?
- Конвертация числовых типов данных
- На что обратить внимание при конвертации типов
- Конвертация строк в Golang
- Статические типы данных
- Конвертация булевых значений
Представьте, что вы пришли в магазин, где нужно сделать покупки по списку жены. Первым пунктом значится молоко, но больше ничего не уточняется — коровье, козье, соевое? Оно должно быть органическим, обезжиренным, 1%, 2%, цельным, конденсированным? Сколько пакетов? Станете ли вы звонить жене для уточнения?
Рекомендуем вам супер TELEGRAM канал по Golang где собраны все материалы для качественного изучения языка. Удивите всех своими знаниями на собеседовании! 😎
Мы публикуем в паблике ВК и Telegram качественные обучающие материалы для быстрого изучения Go. Подпишитесь на нас в ВК и в Telegram. Поддержите сообщество Go программистов.
Жене наверняка будет действовать на нервы, если вы будете звонить ей из-за каждой мелочи. Какая капуста? Картофель белый или красный? Один или два килограмма? С другой стороны, если вы возьмете продукты на свое усмотрение и вернетесь домой с шоколадным молоком и чипсами, все закончится не очень хорошо.
Если представить, что в данной ситуации ваша жена программист, а вы компилятор, какой подход будет оптимальным со стороны Golang?
Можно ли смешивать типы в Golang?
Поведение переменной зависит от ее типа. Числа могут складываться, а строки объединяться. Для объединения двух строк нужно использовать оператор плюс:
1 |
countdown := "Launch in T minus " + "10 seconds." |
При попытке объединить число и строку компилятор Go выведет ошибку:
1 |
countdown := "Launch in T minus " + 10 + " seconds." // Ошибка операции: несовпадение типов string и int |
В некоторых других языках программирования при попытке оперировать переменными разных типов производится попытка угадать намерения разработчика. К примеру, JavaScript и PHP могут вычесть 1 из строки «10»:
1 |
"10" - 1 // 9 в JavaScript и PHP |
Компилятор Go не примет "10" - 1
и выдаст ошибку несовпадения типов. В Go вначале нужно конвертировать строку "10"
в тип integer. Функция Atoi
из пакета strconv
выполнит конвертацию, однако если строка не содержит реального числа, появится ошибка. К моменту, когда ошибка будет исправлена, версия Go достигнет длины в четыре строки, что не очень удобно.
Учитывая все вышесказанное, если "10"
является вводом пользователя или данными, полученными из внешнего источника, JavaScript и PHP также должны сделать проверку на реальность числа.
В языках, где тип принуждается, поведение кода менее предсказуемо, особенно для тех, кто не знаком с мириадами неясных поведений. Оператор плюс (+) в Java и JavaScript переводит числа в строки, конкатенируя их, в то время как PHP складывает числовые значения:
1 |
"10" + 2 // "102" в JavaScript или Java, 12 в PHP |
При попытке сделать нечто подобное в Go выведется ошибка.
Другим примером несовпадения типов является попытка сделать вычисления между целыми и вещественными числами. Числа с плавающей запятой вроде 365.2425 представлены вещественными типами float
, а целые числа Go сопоставляет с типами integer
:
1 2 3 4 |
age := 41 marsDays := 687 // age и marsDays являются целыми числами integer earthDays := 365.2425 // earthDays является вещественным типом float fmt.Println("I am", age*earthDays/marsDays, "years old on Mars.") // Ошибка операции: несовпадение типов |
Если бы все три переменные были целыми числами, вычисления были бы успешными, однако тогда значение переменной earthDays
равнялось бы 365, а не 365.2425. Альтернативно, вычисления стали бы успешными, если бы переменные age
и marsDays
принадлежали к вещественному типу (41.0 и 687.0). Go не делает предположения касательно того, что вам нужно, но вы сами можете конвертировать типы. Это будет рассмотрено далее.
Вопрос для проверки:
Что будет результатом операции "10" - 1
в Go?
Конвертация числовых типов данных в Golang
Конвертация типов в Go является очень прямым. Если для вычисления нужно, чтобы тип целочисленной переменной age
стал вещественным, требуется указать это следующим образом:
1 2 |
age := 41 marsAge := float64(age) |
Переменные разных типов не смешиваются, однако с помощью конвертации типа вычисления в следующем листинге сработают:
1 2 3 4 5 6 7 |
age := 41 marsAge := float64(age) marsDays := 687.0 earthDays := 365.2425 marsAge = marsAge * earthDays / marsDays fmt.Println("I am", marsAge, "years old on Mars.") // Выводит: I am 21.797587336244543 years old on Mars. |
Также можно конвертировать float в integer. Однако десятичная часть после точки при этом уберется, без округления:
1 |
fmt.Println(int(earthDays)) // Выводит: 365 |
Конвертация типов требуется для неподписанных (unsigned) и подписанных (signed) целочисленных типов, а также для типов с разными размерами. Всегда лучше конвертировать в тип с большим диапазоном, к примеру, из int8
в int32
. Другие целочисленные конвертации не обходятся без рисков. uint32
может содержать значение в 4 миллиарда, однако int32
поддерживает числа не более 2 миллиардов. Также int
может содержать отрицательное число, а uint
нет.
Есть причина, по которой Go требует, чтобы конвертация типа точно значилась в коде. Каждый раз при использовании конвертации типа нужно учитывать возможные последствия.
Вопросы для проверки:
- Как можно конвертировать переменную
red
в неподписанный 8-битный целочисленный тип? - Каким будет результат сравнения
age > marsAge
?
На что обратить внимание при конвертации типов Go
В 1996 году беспилотная ракета Arianne 5 сошла с траектории полета и взорвалась через 40 секунд после запуска. Причиной была ошибка конвертации типа из float64
в int16
со значением, превышающим 32 767 — максимальное значение, которое может содержать int16
. Необработанная ошибка оставила систему управления полетом без данных для траектории, из-за чего ракета отклонилась от курса, сломалась и в конечном итоге взорвалась.
Нам не известен код Arianne 5, мы также не являемся ракетостроителями, но давайте посмотрим, как Go разберется с похожей конвертацией. Если значение в диапазоне, как в следующем листинге, проблем не будет.
1 2 3 |
var bh float64 = 32767 var h = int16(bh) // TODO: добавить формулы ракетостроения fmt.Println(h) |
Если значение bh
равно 32 767, что слишком велико для int16
, результатом будет ожидаемый итог работы с целыми числами Go: тип переполняется, переходя к минимальному возможному значению для int16
а именно -32768
.
Язык Ada, использовавшийся для Arianne 5, действует иначе. Конвертация типа из float64
в int16
со значением, превышающим допустимый диапазон, привела к ошибке в программном обеспечении. Согласно отчету, рассматриваемое вычисление имело смысл только до момента взлета, поэтому в данной ситуации подход Go может быть лучше. Однако в большинстве случаев лучше избегать неточностей в данных.
Для определения того, приведет ли конвертация типа в int16
к недопустимому значению, пакет math
предоставляет константы min/max
:
1 2 3 |
if bh < math.MinInt16 || bh > math.MaxInt16 { // решает проблему значения за пределами допустимого диапазона } |
На заметку: Константы
min/max
являются нетипизированными, что допускает сравнениеbh
, вещественного значения, сMaxInt16
. Подробнее о нетипизированных константах можете прочитать в статье Пакет Big — Крупные числа в Golang и примеры их использования.
Задание для проверки:
Напишите код, который определяет, является ли переменная v
в пределах диапазона 8-битного неподписанного целочисленного типа.
Конвертация строк в Golang
Для конвертации типов rune
или byte
в string
можно использовать такой же синтаксис, что нужен для конвертации числовых типов. Это показано в следующем листинге. Результат аналогичен с тем, что получается при использования специального символа %c
, описанного в предыдущей статье про обработку строк, для отображения рун и байтов в виде символов.
1 2 3 4 5 |
var pi rune = 960 var alpha rune = 940 var omega rune = 969 var bang byte = 33 fmt.Print(string(pi), string(alpha), string(omega), string(bang)) // Выводит: πάω! |
Конвертация цифрового кода в строку работает также, как и в случае с типом integer
. В конечном итоге, rune
и byte
являются просто другими названиями для int32
и uint8
.
Для конвертирования чисел в string
, каждое число нужно сначала конвертировать в код, начинающийся с 48 для символа 0, и заканчивающийся на 57 для символа 9. К счастью, функция Itoa
пакета strconv
(string conversion, то есть конвертация строк) делает это за нас, как показано в следующем листинге:
1 2 3 4 |
countdown := 10 str := "Launch in T minus " + strconv.Itoa(countdown) + " seconds." fmt.Println(str) // Выводит: Launch in T minus 10 seconds. |
На заметку: Слово
Itoa
является аббревиатурой — integer to ASCII, то есть от целого числа к ASCII. Юникод является расширенным набором старого стандарта ASCII. Первые 128 кодов такие же — числа, используемые здесь, английские буквы и стандартные знаки препинания.
Другим способом конвертации числа в строку является использование Sprintf
, своеобразной напарницы Printf, что возвращает строку string
, но не отображает ее:
1 2 3 |
countdown := 9 str := fmt.Sprintf("Launch in T minus %v seconds.", countdown) fmt.Println(str) // Выводит: Launch in T minus 9 seconds. |
Есть еще один способ. В пакете strconv есть функция Atoi (ASCII to integer, то есть от ASCII к целому числу). Из-за того, что в строке могут быть какие-то кракозябры или слишком крупные числа, функция Atoi
может вернуть ошибку:
1 2 3 4 5 |
countdown, err := strconv.Atoi("10") if err != nil { // о нет, что-то пошло не так } fmt.Println(countdown) // Выводит: 10 |
Значение nil
для err
указывает, что ошибки нет, и все хорошо. В одном из следующих уроков это будет рассмотрено подробнее.
Вопрос для проверки:
Назовите две функции, что конвертируют целое число в строку.
Статические типы данных Golang
В Go при объявлении переменной ей присваивается тип, который в дальнейшем изменить нельзя. Это называется статической типизацией, которая облегчает оптимизацию, поэтому программы работают быстрее. При попытке использовать переменную со значением другого типа компилятор Go сообщит об ошибке:
1 2 3 4 |
var countdown = 10 countdown = 0.5 countdown = fmt.Sprintf("%v seconds", countdown) // Ошибка: переменная countdown может содержать только целые числа |
В языках вроде JavaScript, Python и Ruby вместо статической используется динамическая типизация. В данных языках у каждого значения есть ассоциируемый тип, и переменные могут содержать значения любого типа. Данные языки позволили бы countdown
изменить свой тип во время выполнения программы.
В Golang есть запасной выход на случай ситуаций, где тип непонятен. К пример, функция Println
принимает как строки, так и числовые типы float или integer. В следующих уроках функция Println
будет рассмотрена в деталях.
Конвертация булевых значений Golang
Группа функций Print
отображает булевы значения true
и false
в виде текста. В следующем примере используется функция Sprintf
для конвертации булевой переменной launch
в текст. Если сделать конвертацию булева типа в численный или текстовых, простой оператор if
подойдет лучше всего.
1 2 3 4 5 6 7 8 9 10 11 12 |
launch := false launchText := fmt.Sprintf("%v", launch) fmt.Println("Ready for launch:", launchText) // Выводит: Ready for launch: false var yesNo string if launch { yesNo = "yes" } else { yesNo = "no" } fmt.Println("Ready for launch:", yesNo) // Выводит: Ready for launch: no |
Обратная конвертация требует меньшее количество кода, так как вы можете присвоить результат условия напрямую переменной, как показано в следующем коде:
1 2 3 4 |
yesNo := "no" launch := (yesNo == "yes") fmt.Println("Ready for launch:", launch) // Выводит: Ready for launch: false |
При попытке конвертировать булево значение через конструкции вроде string(false)
, int(false)
, bool(1)
или bool("yes")
компилятор Go выведет сообщение об ошибке.
На заметку: В языках программирования без выделенного типа
bool
значения 1 и 0 зачастую соответствуют значениямtrue
иfalse
соответственно. Однако в Go у булева типа нет численных эквивалентов.
Вопрос для проверки:
Как можно конвертировать булев тип в целое число, чтобы 1 соответствовала значению true
, а 0 — false
?
Заключение
- Конвертация между типами точна, что позволяет избежать двусмысленности;
- Пакет
strconv
предоставляет функции для конвертации строк в другие типы и наоборот.
Итоговое задание для проверки:
Напишите программу, что конвертирует строки в булевы значения:
- Строки «true», «yes» или «1» соответствуют значению
true
; - Строки «false», «no» или «0» соответствуют значению
false
; - Для других значений выводит сообщение об ошибке.
Обратите внимание, что здесь можно использовать оператор switch, что принимает по несколько значений на случай case
.
Администрирую данный сайт с целью распространения как можно большего объема обучающего материала для языка программирования Go. В IT с 2008 года, с тех пор изучаю и применяю интересующие меня технологии. Проявляю огромный интерес к машинному обучению и анализу данных.
E-mail: vasile.buldumac@ati.utm.md
Образование
Технический Университет Молдовы (utm.md), Факультет Вычислительной Техники, Информатики и Микроэлектроники
- 2014 — 2018 Universitatea Tehnică a Moldovei, ИТ-Инженер. Тема дипломной работы «Автоматизация покупки и продажи криптовалюты используя технический анализ»
- 2018 — 2020 Universitatea Tehnică a Moldovei, Магистр, Магистерская диссертация «Идентификация человека в киберпространстве по фотографии лица»
Не могу понять, в Go обязательно переменная должна использоваться ?