How to use strconv package

The strconv package provides functions to convert strings to integers, floats, and booleans, and vice versa.

The bridge between text and types

You are writing a command-line tool that accepts a port number. The flag parser hands you the string "8080". Your networking code demands an integer to open the socket. You are reading a CSV file where the price column contains "19.99". Your tax calculation needs a float. You receive a configuration value "true" from a user, but your logic branch requires a boolean. Input arrives as text. Logic runs on types. You need a reliable bridge between the two worlds.

The strconv package provides that bridge. It converts strings to and from Go's primitive types: integers, floats, and booleans. It also handles formatting values back into strings for display or serialization. The package is part of the standard library, so you always have it available. You import it with import "strconv".

Input is untrusted. A string can contain anything. A user can type letters where numbers belong. A file can have a malformed value. A network packet can be corrupted. strconv functions never panic on bad input. They return an error. You must check the error. If you ignore the error and use the value, you risk silent data corruption or runtime panics later. The pattern is always the same: call the converter, check the error, use the value.

Input arrives as text. Logic runs on types. You need a reliable bridge between the two worlds.

How the parser works

Think of strconv like a customs checkpoint at a border. The string is a traveler arriving with a suitcase full of characters. The converter is the officer who checks the visa, inspects the contents, and decides if the traveler can enter the country of int, float64, or bool. If the visa is valid and the suitcase matches the requirements, the officer stamps the passport and lets the traveler through. If the suitcase contains contraband like letters in a numeric field, the officer turns the traveler away and writes a report. In Go terms, the report is an error. You never assume the string is safe. You always check the error.

The parser scans the string character by character. It skips leading whitespace. It looks for an optional sign. It accumulates digits or decimal components. It checks for overflow against the target type's limits. If everything is valid, it returns the typed value. If it hits an invalid character or the value is too large, it stops and returns an error. The error message tells you exactly what went wrong.

Trust the bit size. Let the parser enforce the bounds.

Minimal example

Here is the core pattern: parse a string, check the error, use the value.

package main

import (
	"fmt"
	"strconv"
)

func main() {
	// Parse string to int. Atoi is shorthand for "ASCII to integer".
	// It returns the value and an error if the string is not a valid integer.
	port, err := strconv.Atoi("8080")
	if err != nil {
		// Handle the error immediately. Never ignore it.
		fmt.Println("Invalid port:", err)
		return
	}
	// Use the typed value.
	fmt.Println("Opening port", port)

	// Convert int back to string for display.
	// Itoa is shorthand for "integer to ASCII".
	label := strconv.Itoa(port)
	fmt.Println("Label:", label)
}

Most editors run gofmt on save. Trust the formatting. Argue logic, not indentation.

Realistic example

Here is how strconv fits into a real HTTP handler that processes query parameters.

package main

import (
	"fmt"
	"net/http"
	"strconv"
)

// handleItems processes a request and parses limit and offset parameters.
func handleItems(w http.ResponseWriter, r *http.Request) {
	// Extract raw strings from query parameters.
	// Query params always arrive as strings.
	limitStr := r.URL.Query().Get("limit")
	offsetStr := r.URL.Query().Get("offset")

	// Default values if parameters are missing.
	limit := 10
	offset := 0

	// Parse limit only if the user provided a value.
	if limitStr != "" {
		parsed, err := strconv.Atoi(limitStr)
		if err != nil {
			// Return a clear error message to the client.
			http.Error(w, "Invalid limit parameter", http.StatusBadRequest)
			return
		}
		limit = parsed
	}

	// Parse offset similarly.
	if offsetStr != "" {
		parsed, err := strconv.Atoi(offsetStr)
		if err != nil {
			http.Error(w, "Invalid offset parameter", http.StatusBadRequest)
			return
		}
		offset = parsed
	}

	// Convert back to string for response headers.
	// Itoa is efficient for simple integer-to-string conversion.
	fmt.Fprintf(w, "Limit: %s, Offset: %s", strconv.Itoa(limit), strconv.Itoa(offset))
}

The if err != nil check is verbose by design. The community accepts the boilerplate because it makes the unhappy path visible. You see the error handling right next to the operation. This prevents silent failures.

Validate early. Fail fast. Return clear errors.

Deep dive into parsing

strconv.Atoi is a convenience wrapper. It calls strconv.ParseInt with base 10 and bit size 0. The bit size 0 tells the parser to use the default size for int, which matches the system architecture. On a 64-bit machine, that is 64 bits. On a 32-bit machine, it is 32 bits. This makes Atoi portable across architectures without hardcoding the size.

When you need more control, use strconv.ParseInt. The function signature is ParseInt(s string, base int, bitSize int). The base argument specifies the numeric base. Base 10 is decimal. Base 16 is hexadecimal. Base 2 is binary. Base 0 means auto-detect: a 0x prefix means hex, a 0 prefix means octal, otherwise decimal. The bitSize argument specifies the result type. Valid values are 0, 8, 16, 32, and 64. If you pass bit size 8, the parser rejects any value larger than 127. This is a safety feature. It prevents silent truncation. If you parse a user-supplied ID and store it in a byte, you should use bit size 8. The parser will catch values that are too large before you try to cast them. The error message is parsing "256": value out of range. This is better than casting and losing data.

strconv.ParseFloat works similarly. The signature is ParseFloat(s string, bitSize int). The bitSize argument is 32 for float32 or 64 for float64. The parser reads the string into a high-precision internal representation and then rounds to the target precision. This means parsing a string to float32 and then converting to float64 gives the same result as parsing directly to float64 and then converting to float32. The rounding happens at the target precision. The parser handles scientific notation, exponents, and special values like +Inf, -Inf, and NaN.

strconv.ParseBool is surprisingly permissive. It accepts "1", "t", "T", "TRUE", "true", and "True" as true. It accepts "0", "f", "F", "FALSE", "false", and "False" as false. Any other string returns an error. If you pass "yes", the parser rejects it with parsing "yes": invalid syntax. This flexibility helps with configuration files but can surprise you if you expect strict validation. Read the docs for ParseBool. It is more permissive than you think.

Trust the bit size. Let the parser enforce the bounds.

Formatting and advanced patterns

Converting values back to strings is the reverse operation. strconv.Itoa converts an integer to a decimal string. strconv.FormatInt gives you control over the base. strconv.FormatFloat gives you control over the format verb and precision.

strconv.FormatFloat supports format verbs. The verb 'f' produces decimal notation without exponents. The verb 'e' produces scientific notation. The verb 'g' uses the shorter of 'e' or 'f'. The verb 'x' produces hexadecimal floating-point notation. The precision argument controls the number of digits after the decimal point. A precision of -1 uses the minimum number of digits necessary to represent the value exactly. This is the default behavior for simple conversions. If you need fixed decimal places for currency, use precision 2 and verb 'f'.

When you are building a response buffer or a log line in a byte slice, allocating intermediate strings creates garbage. strconv.AppendInt writes the digits directly into a byte slice and returns the extended slice. This avoids the allocation entirely. strconv.AppendFloat works the same way for floats.

package main

import (
	"fmt"
	"strconv"
)

func main() {
	// Pre-allocate a buffer to hold the result.
	buf := make([]byte, 0, 64)

	// AppendInt writes the integer to the buffer and returns the new slice.
	// This avoids allocating a temporary string.
	buf = strconv.AppendInt(buf, 12345, 10)

	// AppendFloat works the same way for floats.
	// Verb 'f' with precision -1 uses minimal digits.
	buf = strconv.AppendFloat(buf, 3.14, 'f', -1, 64)

	// Result is []byte("123453.14").
	fmt.Println(string(buf))
}

strconv.Quote takes a string and returns a double-quoted Go literal with escapes. strconv.Unquote does the reverse. This is useful when you need to serialize a string safely for shell execution or JSON-like structures without importing encoding/json.

package main

import (
	"fmt"
	"strconv"
)

func main() {
	// Quote adds double quotes and escapes special characters.
	// Useful for generating safe shell arguments or Go literals.
	safe := strconv.Quote("hello\nworld")
	fmt.Println(safe)

	// Unquote removes quotes and processes escapes.
	// Returns an error if the string is malformed.
	original, err := strconv.Unquote(safe)
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	fmt.Println(original)
}

strconv.Unquote returns an UnquoteError if the string is malformed. The error contains the original string and the reason for failure. Common reasons include mismatched quotes, invalid escape sequences, or invalid UTF-8 data. The error message looks like invalid syntax: "unterminated string". You can check the error type to distinguish between a syntax error and a data error.

Avoid allocations in hot paths. Append to buffers.

Pitfalls and errors

If you parse a number that exceeds the type's range, you get an overflow error. strconv.Atoi("99999999999999999999") fails with parsing "99999999999999999999": value out of range. The string is valid syntax, but the value is too big. The parser catches this before you can store the value.

Base 0 auto-detection can be a trap. If you pass base 0 and the string is "010", the parser interprets it as octal, resulting in 8. If you expected decimal 10, you get the wrong value. Always be explicit about the base when parsing configuration data. Use base 10 for decimal numbers. Use base 16 for hex. Never rely on base 0 unless you control the input format and expect prefixes.

Performance matters in tight loops. fmt.Sprintf("%d", i) works, but it carries overhead. fmt supports verbs, widths, precisions, and multiple arguments. strconv.Itoa(i) does one thing: convert int to string. It allocates a new string, but the allocation is optimized. If you are in a tight loop converting millions of numbers, strconv wins. If you are building a log line with mixed types, fmt is clearer. The rule is simple: use strconv for single-value conversion. Use fmt for composition.

Read the docs for ParseBool. It is more permissive than you think.

Decision matrix

Use strconv.Atoi when you need a quick integer conversion from base 10 and want the default architecture size. Use strconv.ParseInt when you need to specify the bit size or parse non-decimal bases like hex or binary. Use strconv.Itoa when you need to convert an integer to a decimal string for display or logging. Use strconv.FormatInt when you need to format an integer in a specific base or with custom padding. Use strconv.ParseFloat when you need to convert a string to a floating-point number with specified precision. Use strconv.FormatFloat when you need precise control over floating-point formatting, such as scientific notation or fixed decimal places. Use strconv.ParseBool when you need to parse boolean flags from configuration files that may use varied representations. Use strconv.AppendInt when you are building a byte slice buffer and want to avoid string allocations. Use strconv.Quote when you need to generate a safe, escaped string literal for shell or code generation. Use fmt.Sprintf when you are building complex strings that mix types and formatting, rather than converting a single value.

Where to go next