Go'da string ile sayı arasında dönüşüm strconv paketiyle yapılır: strconv.Atoi("42") string'i int değerine, strconv.Itoa(42) ise int değerini string'e çevirir. Taban veya bit boyutu seçmeniz gerekiyorsa ParseInt, ParseUint, ParseFloat ve ParseBool; ters yönde FormatInt, FormatFloat ve FormatBool kullanılır. Ayrıştırma fonksiyonları başarısız olduğunda *strconv.NumError tipinde bir hata döndürür.

Bu yazıda her fonksiyonun hangi girdileri kabul ettiğini, taban 0'ın önekleri nasıl yorumladığını, bit boyutu argümanının ne işe yaradığını, aralık dışı değerlerde ne döndüğünü, Türkçe ondalık virgülün neden hata verdiğini ve string(n) dönüşümünün yaygın tuzağını Go 1.27.3 ile çalıştırılmış örneklerle anlatıyoruz. Temel sayı tipleri ve tip dönüşümü kuralları temel veri tipleri yazısında, dizinin tamamı Go öğrenme rehberinde yer alıyor.

Neden string(n) değil de strconv?

Neden string(n) değil de strconv?

Go örtük tip dönüşümü yapmaz; "yaş: " + 30 derlenmez. Bir tamsayıyı string(n) ile dönüştürmek ise derlenir ama beklediğiniz şeyi yapmaz: dil kurallarına göre tamsayıdan string'e dönüşüm sayının rakamlarını değil, o kod noktasındaki tek karakteri üretir. Bu yüzden 65 sayısı "65" değil "A" olur. go vet bu kullanımı yakalar:

go
package main

import "fmt"

func main() {
	n := 65
	fmt.Println(string(n))
}
text
main.go:7:14: conversion from int to string yields a string of one rune, not a string of digits

Program yine de çalışır ve A yazar, çünkü go run vet denetimini kendiliğinden çalıştırmaz. Karakter elde etmek gerçekten amacınızsa niyetinizi string(rune(n)) ile açıkça belirtin; sayının rakamlarını istiyorsanız strconv.Itoa veya fmt.Sprint kullanın:

go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	n := 65
	fmt.Println(string(rune(n)))
	fmt.Println(strconv.Itoa(n))
	fmt.Println(fmt.Sprint(n))

	buf := []byte("id=")
	buf = strconv.AppendInt(buf, 42, 10)
	buf = append(buf, ' ')
	buf = strconv.AppendBool(buf, true)
	buf = append(buf, ' ')
	buf = strconv.AppendQuote(buf, "ç")
	fmt.Println(string(buf))
	fmt.Println(strconv.IntSize)
}

// Çıktı:
// A
// 65
// 65
// id=42 true "ç"
// 64

fmt.Sprint her tipi kabul ettiği için esnektir; strconv fonksiyonları ise tek bir tipe özeldir ve ne yaptıkları imzalarından anlaşılır. Append ile başlayan sürümler sonucu var olan bir bayt dilimine ekler; bir tamponda satır oluştururken ayrı ayrı string üretmek zorunda kalmazsınız. strconv.IntSize ise int ve uint tiplerinin bit boyutunu verir; bu örneğin çalıştığı amd64 makinede 64'tür. fmt ile biçimlendirme seçenekleri fmt yazısında anlatılıyor.

Atoi ve Itoa: en sık kullanılan ikili

Belgeye göre Atoi(s), ParseInt(s, 10, 0) çağrısının sonucunun int tipine dönüştürülmüş halidir. Yani yalnızca onluk tabanda, isteğe bağlı bir + veya - işaretiyle başlayan rakamları kabul eder. Baştaki veya sondaki boşluk, harf ya da boş string ErrSyntax üretir. Değer int tipine sığmıyorsa ErrRange döner ve sonuç olarak tipin en büyük (veya en küçük) değeri verilir. Itoa(i) ise FormatInt(int64(i), 10) ile aynıdır ve hiç hata döndürmez.

go
package main

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
)

func main() {
	n, err := strconv.Atoi("-42")
	fmt.Println(n+1, err)

	s := strconv.Itoa(2026)
	fmt.Println(s+"!", len(s))

	for _, girdi := range []string{"12a", "", " 42", "99999999999999999999"} {
		n, err := strconv.Atoi(girdi)
		fmt.Printf("%q => %d | %v\n", girdi, n, err)
	}

	n, err = strconv.Atoi(strings.TrimSpace(" 42\n"))
	fmt.Println(n, err)

	_, err = strconv.Atoi("on")
	var ne *strconv.NumError
	if errors.As(err, &ne) {
		fmt.Println(ne.Func, ne.Num, ne.Err)
	}
	fmt.Println(errors.Is(err, strconv.ErrSyntax))
}

// Çıktı:
// -41 <nil>
// 2026! 4
// "12a" => 0 | strconv.Atoi: parsing "12a": invalid syntax
// "" => 0 | strconv.Atoi: parsing "": invalid syntax
// " 42" => 0 | strconv.Atoi: parsing " 42": invalid syntax
// "99999999999999999999" => 9223372036854775807 | strconv.Atoi: parsing "99999999999999999999": value out of range
// 42 <nil>
// Atoi on invalid syntax
// true

Hata mesajı fonksiyonun adını, girdiyi ve nedeni birlikte verdiği için loglarda ayrıca bağlam eklemeye gerek kalmaz. NumError struct'ının üç alanı vardır: Func başarısız olan fonksiyonun adı, Num girdinin kendisi, Err ise nedeni (ErrSyntax, ErrRange ya da başka bir hata). NumError bir Unwrap metoduna sahip olduğu için errors.Is(err, strconv.ErrSyntax) doğrudan çalışır. Formdan veya dosyadan gelen değerlerde önce strings.TrimSpace uygulamak çoğu "invalid syntax" hatasını önler. Hata sarmalama ve errors.As kullanımı hata yönetimi yazısında ayrıntılı anlatılıyor.

ParseInt ve ParseUint: taban ve bit boyutu

ParseInt ve ParseUint: taban ve bit boyutu: Atoi(s), ParseInt(s, taban, bit), ParseUint(s, taban, bit), ParseFloat(s, bit)

ParseInt(s, taban, bitBoyutu) iki ek argüman alır. Taban 0 veya 2 ile 36 arasında olabilir. Taban 0 verildiğinde gerçek taban işaretten sonraki önekten çıkarılır: 0b için 2, 0 veya 0o için 8, 0x için 16, aksi halde 10. Belge, Go'nun tamsayı sabitlerindeki gibi alt çizgi ayırıcılara yalnızca taban 0 ile izin verildiğini belirtir. Bit boyutu 0, 8, 16, 32 veya 64 olabilir ve sırasıyla int, int8, int16, int32, int64 tiplerine karşılık gelir; sonucun sığması gereken tipi söyler. Dönen değer her zaman int64 tipindedir, ama verdiğiniz bit boyutuna sığdığı garanti olduğundan dar tipe veri kaybı olmadan dönüştürebilirsiniz.

go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	a, _ := strconv.ParseInt("ff", 16, 64)
	b, _ := strconv.ParseInt("-101", 2, 64)
	c, _ := strconv.ParseInt("0x1F", 0, 64)
	d, _ := strconv.ParseInt("1_000_000", 0, 64)
	e, _ := strconv.ParseInt("0o17", 0, 64)
	fmt.Println(a, b, c, d, e)

	_, err := strconv.ParseInt("1_000", 10, 64)
	fmt.Println(err)

	v, err := strconv.ParseInt("300", 10, 8)
	fmt.Println(v, err)
	var k int8 = int8(v)
	fmt.Println(k)

	u, err := strconv.ParseUint("-5", 10, 64)
	fmt.Println(u, err)

	x, _ := strconv.ParseInt("2147483647", 10, 32)
	fmt.Println(int32(x))

	fmt.Println(strconv.FormatInt(255, 2), strconv.FormatInt(-255, 16), strconv.FormatUint(35, 36))
}

// Çıktı:
// 255 -5 31 1000000 15
// strconv.ParseInt: parsing "1_000": invalid syntax
// 127 strconv.ParseInt: parsing "300": value out of range
// 127
// 0 strconv.ParseUint: parsing "-5": invalid syntax
// 2147483647
// 11111111 -ff z

"300" sayısı 8 bitlik işaretli tamsayıya sığmadığı için ErrRange döndü ve değer olarak int8 tipinin en büyük değeri olan 127 verildi. ParseUint işaret önekine hiç izin vermez; -5 bu yüzden aralık hatası değil sözdizimi hatasıdır. FormatInt(i, taban) 2 ile 36 arasındaki tabanlarda çalışır ve 10'dan büyük basamaklar için küçük harf kullanır: 35 sayısı 36 tabanında z olur. Kullanıcıdan 0x önekli değerleri de kabul etmek istiyorsanız taban 0 ile ayrıştırın; yalnızca onluk sayı bekliyorsanız taban 10 yazmak, 010 gibi bir girdinin sekizlik olarak yorumlanmasını önler.

FonksiyonDönüş tipiNot
Atoi(s)int, errorParseInt(s, 10, 0) ile aynı
ParseInt(s, taban, bit)int64, errortaban 0 ise önek ve alt çizgi kabul eder
ParseUint(s, taban, bit)uint64, errorişaret öneki kabul etmez
ParseFloat(s, bit)float64, errorbit 32 veya 64; Inf ve NaN tanır
ParseBool(s)bool, error1, t, T, TRUE, true, True ve karşılıkları
Itoa(i)stringFormatInt(int64(i), 10) ile aynı
FormatInt(i, taban)stringtaban 2 ile 36 arası
FormatFloat(f, fmt, prec, bit)stringfmt biçim harfi, prec -1 en kısa gösterim
Quote(s), Unquote(s)string, errorGo string sabiti üretir ve çözer

ParseFloat ve FormatFloat: ondalıklı sayılar

ParseFloat(s, bitBoyutu) Go'nun ondalıklı sayı sabitleri sözdizimini kabul eder: 3.14, 1e3, onaltılık 0x1p-2 gibi. Bit boyutu 32 verilirse sonuç yine float64 tipindedir ama değeri float32 tipine kayıpsız dönüştürülebilir. Fonksiyon "NaN", "Inf" ve "Infinity" metinlerini büyük-küçük harf gözetmeden tanır. Değer tipin en büyük sayısını aşıyorsa ±Inf ve ErrRange döner. Türkiye'de en sık karşılaşılan sorun ondalık ayırıcıdır: strconv yerel ayar kullanmaz ve yalnızca noktayı tanır, bu yüzden "3,14" sözdizimi hatası verir.

go
package main

import (
	"fmt"
	"strconv"
	"strings"
)

func main() {
	f, err := strconv.ParseFloat("3.14", 64)
	fmt.Println(f, err)

	_, err = strconv.ParseFloat("3,14", 64)
	fmt.Println(err)

	g, _ := strconv.ParseFloat(strings.Replace("1249,90", ",", ".", 1), 64)
	fmt.Println(g)

	h, _ := strconv.ParseFloat("1e3", 64)
	inf, _ := strconv.ParseFloat("-Inf", 64)
	nan, _ := strconv.ParseFloat("nan", 64)
	fmt.Println(h, inf, nan)

	buyuk, err := strconv.ParseFloat("1e400", 64)
	fmt.Println(buyuk, err)

	v := 1234.5678
	fmt.Println(strconv.FormatFloat(v, 'f', 2, 64))
	fmt.Println(strconv.FormatFloat(v, 'f', -1, 64))
	fmt.Println(strconv.FormatFloat(v, 'e', 3, 64))
	fmt.Println(strconv.FormatFloat(v, 'g', 5, 64))
	fmt.Println(strconv.FormatFloat(0.1, 'f', -1, 32), strconv.FormatFloat(float64(float32(0.1)), 'f', -1, 64))
}

// Çıktı:
// 3.14 <nil>
// strconv.ParseFloat: parsing "3,14": invalid syntax
// 1249.9
// 1000 -Inf NaN
// +Inf strconv.ParseFloat: parsing "1e400": value out of range
// 1234.57
// 1234.5678
// 1.235e+03
// 1234.6
// 0.1 0.10000000149011612

FormatFloat(f, biçim, hassasiyet, bitBoyutu) dört argüman alır. Biçim harfi 'f' (üssüz ondalık), 'e' veya 'E' (bilimsel), 'g' veya 'G' (büyük üslerde bilimsel, diğerlerinde ondalık), 'b', 'x' ve 'X' olabilir. Hassasiyet 'e' ve 'f' için noktadan sonraki basamak sayısı, 'g' için en fazla anlamlı basamak sayısıdır. Özel değer -1, ParseFloat ile geri okunduğunda aynı sayıyı verecek en kısa gösterimi seçer. Son satırdaki karşılaştırma bit boyutunun önemini gösterir: aynı float32 değeri 32 bitlik kabul edilerek yazılınca 0.1, 64 bitlik bir değer gibi yazılınca 0.10000000149011612 olur.

Ayrıştırma yönünde de bir yuvarlama vardır. Belgeye göre metin sözdizimi açısından doğru ve geçerli bir sayıya yakınsa ParseFloat, IEEE 754 kurallarına göre yuvarlanmış en yakın ondalıklı sayıyı döndürür; yani "0.1" gibi ikilik sistemde tam gösterilemeyen değerler yaklaşık olarak saklanır. Dönen hatalar her zaman *NumError tipindedir ve Num alanı girdinin kendisini içerir. Bu nedenle bir yapılandırma dosyasındaki hatalı değeri kullanıcıya bildirirken hatanın metnini olduğu gibi göstermek çoğu zaman yeterlidir: hangi fonksiyonun, hangi metni, neden ayrıştıramadığı tek satırda yazılıdır. Tutarların kuruş hassasiyetinde saklanması gerekiyorsa değeri ayrıştırıp 100 ile çarpmak yerine kuruşları baştan tamsayı olarak ayrıştırmak yuvarlama sürprizlerini önler.

ParseBool, Quote ve Unquote

ParseBool yalnızca şu metinleri kabul eder: 1, t, T, TRUE, true, True ve 0, f, F, FALSE, false, False. "evet", "yes" veya "on" gibi değerler hata verir; bir yapılandırma dosyasında bunları desteklemek istiyorsanız kendi eşlemenizi yazmanız gerekir. Quote bir metni çift tırnaklı Go string sabitine çevirir ve kontrol karakterleriyle yazdırılamayan karakterleri kaçışlar. QuoteToASCII sonucun tamamen ASCII olmasını garanti eder ve ASCII dışı karakterleri \u ile yazar. Unquote tersini yapar: tek tırnaklı, çift tırnaklı veya ters tırnaklı bir Go sabitini çözer.

go
package main

import (
	"fmt"
	"strconv"
)

func main() {
	for _, s := range []string{"true", "T", "1", "FALSE", "evet", "yes"} {
		b, err := strconv.ParseBool(s)
		fmt.Println(s, b, err)
	}
	fmt.Println(strconv.FormatBool(false))

	q := strconv.Quote("Merhaba \"dünya\"\n\t")
	fmt.Println(q)
	fmt.Println(strconv.QuoteToASCII("Gün ışığı"))
	fmt.Println(strconv.QuoteRune('ş'))

	u, err := strconv.Unquote(`"satır\nsonu"`)
	fmt.Printf("%q %v\n", u, err)
	_, err = strconv.Unquote("tırnaksız")
	fmt.Println(err)
}

// Çıktı:
// true true <nil>
// T true <nil>
// 1 true <nil>
// FALSE false <nil>
// evet false strconv.ParseBool: parsing "evet": invalid syntax
// yes false strconv.ParseBool: parsing "yes": invalid syntax
// false
// "Merhaba \"dünya\"\n\t"
// "G\u00fcn \u0131\u015f\u0131\u011f\u0131"
// 'ş'
// "satır\nsonu" <nil>
// invalid syntax

Quote Türkçe harfleri olduğu gibi bırakır, çünkü bunlar yazdırılabilir karakterlerdir; yalnızca tırnak, satır sonu ve sekme kaçışlandı. fmt paketindeki %q fiili de aynı kuralları kullanır. Unquote, tırnaksız bir girdide yalın invalid syntax hatası döndürür; bu hata NumError ile sarılmaz.

Mini örnek: komut satırı argümanlarını doğrulamak

Pratikte strconv çoğunlukla dışarıdan gelen metinleri doğrularken kullanılır: komut satırı argümanları, ortam değişkenleri, form alanları. Aşağıdaki kalıp, bir port numarasını hem sözdizimi hem aralık açısından kontrol eder. Bit boyutu olarak 16 vermek, 65535'ten büyük değerleri doğrudan ErrRange ile reddeder:

go
package main

import (
	"errors"
	"fmt"
	"strconv"
)

func portOku(s string) (uint16, error) {
	n, err := strconv.ParseUint(s, 10, 16)
	if errors.Is(err, strconv.ErrRange) {
		return 0, fmt.Errorf("port 0-65535 aralığında olmalı: %s", s)
	}
	if err != nil {
		return 0, fmt.Errorf("port bir sayı olmalı: %w", err)
	}
	return uint16(n), nil
}

func main() {
	for _, s := range []string{"8080", "70000", "http", "443"} {
		p, err := portOku(s)
		fmt.Println(p, err)
	}
}

// Çıktı:
// 8080 <nil>
// 0 port 0-65535 aralığında olmalı: 70000
// 0 port bir sayı olmalı: strconv.ParseUint: parsing "http": invalid syntax
// 443 <nil>

Komut satırı bayraklarını tek tek ayrıştırmak yerine flag paketinin flag.Int veya flag.Duration fonksiyonlarını kullanabilirsiniz; bu fonksiyonlar dönüşümü ve hata mesajını sizin için yapar. Ayrıntılar flag paketi yazısında.

Sık yapılan hatalar

Sık yapılan hatalar: Tamsayıyı string(n) ile string'e çevirmek. Sonuç rakamlar değil tek bir..., Atoi hatasını yok sayıp _ ile atmak. Geçersiz girdide 0, aralık dışı girdide...
  • Tamsayıyı string(n) ile string'e çevirmek. Sonuç rakamlar değil tek bir karakterdir; strconv.Itoa kullanın.
  • Atoi hatasını yok sayıp _ ile atmak. Geçersiz girdide 0, aralık dışı girdide tipin sınır değeri döner ve program yanlış veriyle devam eder.
  • Girdideki boşlukları temizlemeden ayrıştırmak. " 42" ve "42\n" sözdizimi hatasıdır.
  • Ondalık virgüllü metni doğrudan ParseFloat fonksiyonuna vermek.
  • ParseInt sonucunu bit boyutunu kontrol etmeden daha dar bir tipe dönüştürmek. Bit boyutu 64 iken int8(v) sessizce taşar; dar tipe dönüştürecekseniz bit boyutunu o tipe göre verin.
  • Alt çizgili sayıları taban 10 ile ayrıştırmaya çalışmak. Alt çizgi yalnızca taban 0 ile kabul edilir.

Sık Sorulan Sorular

Atoi ile ParseInt arasındaki fark nedir?

Atoi(s), ParseInt(s, 10, 0) çağrısının kısa yoludur ve sonucu doğrudan int olarak verir. Farklı bir taban, belirli bir bit boyutu ya da 0x gibi önekler gerekiyorsa ParseInt kullanın; sonuç int64 tipinde döner.

float64 değerini iki basamakla string yapmanın yolu nedir?

strconv.FormatFloat(v, 'f', 2, 64) veya fmt.Sprintf("%.2f", v). İkisi de yuvarlar; örnekte 1234.5678 değeri 1234.57 oldu. Gereken en az basamakla yazmak için hassasiyet olarak -1 verin.

Hatanın sözdizimi mi aralık mı olduğunu nasıl anlarım?

errors.Is(err, strconv.ErrSyntax) ve errors.Is(err, strconv.ErrRange) ile. Hatanın hangi fonksiyondan ve hangi girdiden geldiğini görmek için errors.As ile *strconv.NumError alıp Func ve Num alanlarına bakabilirsiniz.

JSON'daki sayıları da strconv ile mi çözmeliyim?

Hayır, encoding/json sayıları struct alanının tipine göre kendisi dönüştürür. Sayı JSON'da string olarak geliyorsa alan etiketine ,string seçeneğini ekleyebilirsiniz; ayrıntılar encoding/json yazısında.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Go öğrenme rehberi · Go temel veri tipleri · fmt ile biçimlendirme · Go hata yönetimi · flag ile komut satırı argümanları · string, rune ve byte