Question

Go strings.HasPrefix and HasSuffix: Checking String Starts and Ends

gostring

Question

Why does Go not provide standard startswith and endswith functions? Are there built-in-library functions for checking whether a string begins or ends with a specified value?

Short Answer

Go does provide these operations in its standard library, but they are named strings.HasPrefix and strings.HasSuffix rather than startswith and endswith. You will learn how to use them, why they live in packages, and when to use the equivalent functions for byte slices.

Concept

Go keeps many operations outside of built-in types and organizes them into focused packages. String prefix and suffix checks are available in the standard strings package:

strings.HasPrefix(s, prefix)
strings.HasSuffix(s, suffix)

Both functions return a bool:

  • true when the requested text matches
  • false when it does not

For example, strings.HasPrefix("report.csv", "report") is true, and strings.HasSuffix("report.csv", ".csv") is also true.

The names use Go's usual exported-identifier style: HasPrefix and HasSuffix use capital letters because they are public functions. Go favors package functions for many operations rather than attaching a large collection of methods to every type. This keeps the core string type small while making related functionality easy to find in the strings package.

For []byte values, use the equivalent functions in the bytes package: bytes.HasPrefix and bytes.HasSuffix.

Mental Model

Think of a string as a label on a package.

  • A prefix is the beginning of the label.
  • A suffix is the ending of the label.

strings.HasPrefix asks: “Does this label begin with these characters?”

strings.HasSuffix asks: “Does this label end with these characters?”

For a file named photo.png, .png is a suffix. For a URL path such as /api/users, /api/ is a prefix.

Take Quiz

Syntax and Examples

Import the strings package, then pass the full string first and the text to look for second.

package main

import (
	"fmt"
	"strings"
)

func main() {
	filename := "invoice.pdf"

	isPDF := strings.HasSuffix(filename, ".pdf")
	startsWithInvoice := strings.HasPrefix(filename, "invoice")

	fmt.Println(isPDF)             // true
	fmt.Println(startsWithInvoice) // true
}

The function signatures are:

func HasPrefix(s, prefix string) bool
func HasSuffix(s, suffix string) bool

The comparison is exact and case-sensitive:

fmt.Println(strings.HasPrefix("Hello", "He")) // true
fmt.Println(strings.HasPrefix("Hello", "he")) // false

If case-insensitive matching is appropriate, normalize both strings first:

name := "PHOTO.PNG"
isPNG := strings.HasSuffix(strings.ToLower(name), ".png")
fmt.Println(isPNG) // true

Step by Step Execution

Consider this example:

package main

import (
	"fmt"
	"strings"
)

func main() {
	path := "/api/orders"
	isAPIPath := strings.HasPrefix(path, "/api/")
	isJSONFile := strings.HasSuffix(path, ".json")

	fmt.Println(isAPIPath)
	fmt.Println(isJSONFile)
}

Step by step:

  1. path stores "/api/orders".
  2. strings.HasPrefix(path, "/api/") compares the beginning of path with "/api/".
  3. The beginning matches, so isAPIPath becomes true.
  4. strings.HasSuffix(path, ".json") compares the end of path with ".json".
  5. The path ends in "orders", not ".json", so isJSONFile becomes false.
  6. The program prints:
true
false

Real World Use Cases

Prefix and suffix checks appear in many practical tasks:

  • HTTP routing: Check whether a request path begins with /api/ or /assets/.
  • File handling: Accept files ending in .json, .csv, or .go.
  • Configuration: Read environment variables with a prefix such as APP_.
  • Logging: Identify lines beginning with ERROR: or WARN:.
  • Protocol parsing: Detect a URL beginning with https://.
  • Data imports: Ignore temporary files ending with ~ or .tmp.

Example: process only JSON files.

if strings.HasSuffix(filename, ".json") {
	// Read and decode the JSON file.
}
Take Quiz

Real Codebase Usage

In production code, prefix and suffix checks are usually combined with validation and early returns.

Validate input early

func loadConfig(filename string) error {
	if !strings.HasSuffix(filename, ".json") {
		return fmt.Errorf("config file must have a .json extension")
	}

	// Continue only after validation succeeds.
	return nil
}

An early return prevents deeply nested if statements and makes invalid input handling clear.

Filter a collection

var jsonFiles []string

for _, filename := range filenames {
	if strings.HasSuffix(filename, ".json") {
		jsonFiles = append(jsonFiles, filename)
	}
}

Use the package that matches the data type

Network and file APIs often provide data as []byte. Avoid converting to string solely for a prefix check when you can use bytes.HasPrefix:

if bytes.HasPrefix(data, []byte("GET ")) {
	// Handle an HTTP GET request line.
}

Treat extensions carefully

A suffix check can be useful for preliminary filtering, but it does not prove file contents are safe or valid. For example, a file named image.png may not actually contain PNG data. Validate or parse the content when security or correctness matters.

Common Mistakes

Looking for methods on string

Go does not use a method call such as name.StartsWith(...) for this operation.

// Does not compile.
name := "report.csv"
if name.StartsWith("report") {
}

Use the package function instead:

if strings.HasPrefix(name, "report") {
}

Forgetting the import

import "strings"

Without this import, strings.HasPrefix is undefined.

Reversing the arguments

The full text is first; the prefix or suffix to test is second.

// Incorrect meaning: asks whether ".go" ends with "main.go".
strings.HasSuffix(".go", "main.go")

// Correct.
strings.HasSuffix("main.go", ".go")

Assuming comparisons ignore case

They are case-sensitive.

strings.HasSuffix("PHOTO.JPG", ".jpg") // false

Normalize deliberately when your domain requires case-insensitivity:

strings.HasSuffix(strings.ToLower("PHOTO.JPG"), ".jpg") // true

Using a suffix check as complete file validation

Checking a filename extension is not a security check and does not validate file contents. Parse the data or inspect its format when that guarantee is needed.

Forgetting empty-string behavior

Every string has the empty string as both a prefix and a suffix:

strings.HasPrefix("hello", "") // true
strings.HasSuffix("hello", "") // true

Validate an expected prefix or suffix first if an empty value would be invalid in your program.

Comparisons

NeedRecommended Go functionNotes
Check a string beginningstrings.HasPrefix(s, prefix)Exact, case-sensitive comparison
Check a string endingstrings.HasSuffix(s, suffix)Useful for extensions and markers
Check a []byte beginningbytes.HasPrefix(b, prefix)Avoids unnecessary string conversion
Check a []byte endingbytes.HasSuffix(b, suffix)Equivalent byte-slice operation
Find text anywherestrings.Contains(s, sub)Does not require a start or end match
Remove a known start oncestrings.TrimPrefix(s, prefix)Returns the original string if no match
Remove a known end oncestrings.TrimSuffix(s, suffix)Returns the original string if no match

HasPrefix and Contains answer different questions:

text := "user-admin-settings"

fmt.Println(strings.HasPrefix(text, "admin")) // false
fmt.Println(strings.Contains(text, "admin"))  // true

Use HasPrefix when position matters. Use Contains when the text may appear anywhere.

Take Quiz

Cheat Sheet

import "strings"

strings.HasPrefix(s, prefix) // bool
strings.HasSuffix(s, suffix) // bool
strings.HasPrefix("golang", "go") // true
strings.HasSuffix("main.go", ".go") // true

Rules:

  • First argument: complete string to inspect.
  • Second argument: required prefix or suffix.
  • Matching is case-sensitive.
  • An empty prefix or suffix matches every string.
  • Use bytes.HasPrefix and bytes.HasSuffix for []byte.
  • Use strings.TrimPrefix or strings.TrimSuffix when you need to remove a verified portion.
clean := strings.TrimPrefix("Bearer token123", "Bearer ")
// "token123"

FAQ

Does Go have startswith and endswith functions?

Yes, under different names: strings.HasPrefix and strings.HasSuffix.

Why are they not methods on Go strings?

Go commonly places operations in packages rather than adding many methods to basic types. String-related helpers are grouped in the strings package.

Are strings.HasPrefix and strings.HasSuffix case-sensitive?

Yes. Convert both values with strings.ToLower or strings.ToUpper if your use case requires case-insensitive matching.

What is the equivalent for byte slices in Go?

Use bytes.HasPrefix and bytes.HasSuffix for []byte values.

Does an empty prefix match a string in Go?

Yes. strings.HasPrefix(s, "") is true for every string, including an empty string.

Can I use HasSuffix to verify that a file is safe?

No. It only checks the filename text. Validate the file contents and apply appropriate security controls when needed.

How do I remove a prefix after checking it?

Use strings.TrimPrefix. It removes one matching prefix and otherwise returns the original string unchanged.

Take Quiz

Mini Project

Description

Build a small filename classifier for an upload or import tool. The program examines filenames and decides whether they are accepted data files, temporary files to ignore, or unsupported files. It demonstrates practical prefix and suffix checks with the strings package.

Goal

Classify a list of filenames using strings.HasPrefix and strings.HasSuffix.

Requirements

Use a list containing CSV, JSON, temporary, hidden, and unsupported filenames. Ignore names beginning with .. Ignore names ending with .tmp. Accept names ending with .csv or .json. Print a clear classification for every filename.

Take Quiz

Keep learning

Related questions

View more