Go
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions and Methods
-
Section 4: Concurrency
-
Section 5: Packages and Tooling
-
Section 6: More Standard Library
-
Section 7: Building Services
-
Section 8: Advanced Go
-
Section 9: Go in the Cloud-Native Ecosystem
-
Section 10: Data Structures and Algorithms in Go
-
Section 11: Testing and Deployment
-
Section 12: Practical Projects
-
Section 13: More Standard Library Practice
-
Section 14: More Practice Projects
-
Section 15: Design Patterns in Go
-
Section 16: Interview Practice
-
Section 17: Package fmt In Depth
-
Section 18: Package strings and strconv
-
Section 19: Package os and io
-
Section 20: Package time
-
Section 21: Package sort and container
-
Section 22: Package encoding
-
Section 23: Package net/http In Depth
-
Section 24: Package context
-
Section 25: Package regexp and bytes
-
Section 26: Package errors In Depth
-
Section 27: Package crypto and hash
-
Section 28: Package flag and log
-
Section 29: Package sync In Depth
-
Section 30: More Practice Exercises
-
Section 31: Go Modules and Workspaces In Depth
-
Section 32: Generics Deep Dive (Go 1.18+)
-
Section 33: Testing Package In Depth
-
Section 34: More Interview and Whiteboard Practice
-
Section 35: Package math and unicode
-
Section 36: Package path and filepath
-
Section 37: Package database/sql
-
Section 38: Package text/template and html/template
-
Section 39: Package archive and compress
-
Section 40: Lower-Level net Package
-
Section 41: Package runtime and reflect
-
Section 42: Package embed and io/fs
-
Section 43: Go Assembly and CGO Basics
-
Section 44: Building CLIs and TUIs
-
Section 45: Go Performance Tuning
-
Section 46: More Real-World Projects
-
Section 47: Go in Production
-
Section 48: Go Design Patterns
-
Section 49: Go Interfaces Deep Dive
-
Section 50: Final Practice and Review
190: Working with File Extensions and Base Names
When you're building tools that touch the filesystem, you'll quickly realize that paths are messy. You've got absolute paths, relative paths, and the headache of different separators on Windows versus Linux. Instead of trying to split strings manually with strings.Split, we use the path/filepath package. It's designed to handle these cross-platform quirks for you.
How do I strip away the folder path to get just the filename?
If you have a path like /home/user/projects/app/config.yaml, you usually don't care about the directories; you just want config.yaml. For this, I always use filepath.Base(). It looks at the path and returns the last element.
package main
import (
"fmt"
"path/filepath"
)
func main() {
fullPath := "/var/log/system/auth.log"
filename := filepath.Base(fullPath)
fmt.Println(filename) // Output: auth.log
}
One thing to keep in mind: if the path ends in a separator, Base will return the name of the last directory. It's a helpful detail, but it can trip you up if you're expecting a file and accidentally pass it a directory path.
How do I actually isolate the file extension?
Checking file types is a common task—say, ensuring a user only uploaded a .png or .jpg. The filepath.Ext() function is the way to go here. Just be careful: Ext() returns the extension including the leading dot.
package main
import (
"fmt"
"path/filepath"
)
func main() {
file := "vacation_photo.jpg"
ext := filepath.Ext(file)
fmt.Println(ext) // Output: .jpg
if ext == ".jpg" || ext == ".jpeg" {
fmt.Println("This is a JPEG image!")
}
}
I've seen a lot of developers try to slice the string manually to remove that dot, but if you're just comparing the extension to a known value, it's easier to just include the dot in your comparison string.
What's the easiest way to get the filename without the extension?
This is where Go is surprisingly minimal. There isn't a single filepath.BaseWithoutExt() function. To get just the "stem" of the filename (like vacation_photo from vacation_photo.jpg), you have to combine Base() and Ext().
The trick is to get the base name, find the length of the extension, and then slice the base name string to remove that trailing portion.
package main
import (
"fmt"
"path/filepath"
)
func main() {
path := "/etc/nginx/nginx.conf"
base := filepath.Base(path) // "nginx.conf"
ext := filepath.Ext(path) // ".conf"
// We slice the base string from the start up to
// the total length minus the length of the extension.
nameWithoutExt := base[:len(base)-len(ext)]
fmt.Println(nameWithoutExt) // Output: nginx
}
I usually wrap this logic in a small helper function in my projects because I use it so often. It keeps the main business logic clean and prevents me from writing that slicing math over and over again.
📋 Practical Task
Exercise: Log File Archive Renamer
You are building a log rotation tool. Your task is to take a slice of full file paths and generate a new slice of "archive names." For every file, you must remove the directory path and the .log extension, and append _backup.bak to the end.
Example:
Input: "/var/log/syslog.log" → Output: "syslog_backup.bak"
Input: "C:\logs\app_error.log" → Output: "app_error_backup.bak"
Write a function ArchiveName(path string) string that implements this logic using the path/filepath package. Test it with a slice of at least three different paths to ensure it handles different folder depths correctly.
There are no comments for now.