tetu式

ゲームと音楽・作曲の自己満足と悩みどころの多いプログラムのブログ。

PHPあがりでGo言語のint→stringキャスト

仕事で使う可能性が出てきたので最近Go言語を勉強してます。
サーバーサイドで高速な処理が期待できる言語だそうですが、ゲーム用のライブラリが出ていたりするなど
(本来の用途とは違うだろうけど)フロントサイドのプログラムもできるみたいです。


さて、表題の内容について。
今までの仕事でjavascriptphpを多く使ってきたこともあり、Go言語のような静的型付け言語は結構久々なのです。
phpは動的型付け言語ですがAJAXとかでjavascriptJSON形式で値を返して
javascript側でJSON解析して計算し直すみたいな処理をしようとした時、

// json {"val1": 8, "val2": 8}
let r = json.val1 + json.val2;	// 文字結合で88にするつもりが加算演算で16になる

文字列結合と加算が同じ符号であるため、意図しない結果になったりします。
phpだと文字列結合と加算は別々の符号なので問題ないですが・・・
こういう時、phpjavascriptで明示的に型を指定する必要があります。

例えばphpだと

<?php
$i = 8;
$val1 = (string)$i;	// $val1に文字列の8を代入

こんな書き方で数値型の$iを文字列型にキャストしてくれます。
こんなノリをGo言語でやろうとするとうまくいかなかったり・・・

package main

import (
	"fmt"
	"strconv"
)

func main() {
	str1 := "6"
	str2 := "2"
	var val1, val2, res int
	val1, _ = strconv.Atoi(str1)
	val2, _ = strconv.Atoi(str2)

	res = val1 + val2
	str3 := string(res)

	fmt.Println(res)					// 8を出力
	fmt.Printf("%T\n", res)				// int を出力
	fmt.Println(str3)					// 空欄になっちゃう(エラーにならない)
	fmt.Printf("%T\n", str3)				// string を出力
	fmt.Println(strconv.Itoa(res))			// 8を出力
	fmt.Printf("%T\n", strconv.Itoa(res))	// string を出力

	fmt.Println(len(str3))	// 1 を出力

	var str4 string
	fmt.Println(len(str4))	// 0 を出力
}

Go言語にもphpに似たような書き方で 型名(変数) という書き方でキャストする方法があります。
とはいえ基本的には int型 / int型 の計算結果をfloat型にするなど、上位型へのキャストに使うものです。
文字列型のstringからは根本的に内容が違うせいか、数字オンリーの内容でも int(string) のキャストはエラーになります。
なので標準ライブラリのstrconvを使ってAtoiで数字オンリーの文字列をint型に変換しています。

逆に string(int) のキャストの場合はエラーが出ません。
しかし、str3 にそのキャストした内容を入れて表示してみましたが、結果には何も表示されません。
strconv.Itoaの方法は想定通りの変換です。

じゃぁ string(res) で戻ってくる値には何が入っているのでしょう?
str4 のように宣言して何もしてないものはlen()で調べても0が戻ってくるので
何も入っていないというわけではなさそうです。

ちょっと計算式を変えてみましょう。

package main

import (
	"fmt"
	"strconv"
)

func main() {
	str1 := "6"
	str2 := "8"
	var val1, val2, res int
	val1, _ = strconv.Atoi(str1)
	val2, _ = strconv.Atoi(str2)

	res = val1 * val2	// 6*8に変更
	str3 := string(res)

	fmt.Println(res)		// 48を出力
	fmt.Println(str3)		// 0を出力
	fmt.Println(len(str3))	// 1 を出力
}

str3自体は0が出力され、len()では1が戻ってきました。
48と0って完全に違う数値じゃん、と思ったのですが、この組み合わせは無関係ではございませんでした・・・

ASCIIコード表によるとコード48には数字の0が対応しています。

つまり string(int) で変換される中身にはコード数値が入り、 fmt.Println() でコードを解析して文字表示してるってことですね。
8がただの空欄になってしまったのはたまたま制御文字(バックスペース)だったので何もありませんでしたが、
10を入れたら制御文字(改行)となり、 fmt.Println() の改行も含めて2行の空欄ができました。

まとめ:int→stringキャストには大人しく strconv.Itoa(int) を使用する

おまけ:fmt.Sprint(変数) を使うと汎用的にstring変換できる

参考:
Go言語(Golang) はまりどころと解決策