10、字符串、日期时间、文件操作
10、字符串、日期时间、文件操作
字符串与格式化(fmt、strconv、strings、bytes)
fmt —— 格式化输入输出
fmt 包提供格式化 I/O,主要用于打印到控制台、格式化字符串等。
常用输出函数
Print/Printf/Println:输出到标准输出。Sprintf:格式化并返回字符串,不直接打印。Fprintf:写入指定io.Writer(如文件、网络连接)。
name := "Alice"
age := 30
s := fmt.Sprintf("%s is %d years old", name, age) // "Alice is 30 years old"
fmt.Println(s)
常用格式化动词
| 动词 | 含义 |
|---|---|
%v |
默认格式(值的自然形式) |
%+v |
结构体时添加字段名 |
%#v |
Go 语法表示 |
%T |
值的类型 |
%d |
十进制整数 |
%s |
字符串 |
%q |
带引号的字符串 |
%f |
浮点数 |
%t |
布尔值 |
%p |
指针地址 |
输入函数
fmt.Scan/Scanf/Scanln:从标准输入读取。fmt.Fscan系列:从io.Reader读取。fmt.Sscan系列:从字符串读取。
strconv —— 字符串与基本类型转换
strconv 提供字符串和数字、布尔等类型的相互转换,比 fmt 效率更高,适合大量转换。
- 数字转字符串
s := strconv.Itoa(42) // "42"
s = strconv.FormatInt(255, 16) // "ff"(十六进制)
s = strconv.FormatFloat(3.14, 'f', 2, 64) // "3.14"
- 字符串转数字
i, err := strconv.Atoi("42") // 42, nil
i64, err := strconv.ParseInt("ff", 16, 64) // 255, nil
f, err := strconv.ParseFloat("3.14", 64) // 3.14, nil
- 布尔与字符串互转
b, _ := strconv.ParseBool("true") // true
s := strconv.FormatBool(true) // "true"
- 引号处理
strconv.Quote(s):返回带引号的字符串,转义内部特殊字符。strconv.Unquote(s):解析带引号的字符串。
strings —— 字符串操作
strings 包提供大量高效字符串函数,不会修改原字符串(字符串不可变),总是返回新字符串。
- 检查与比较
strings.Contains("golang", "go") // true
strings.HasPrefix("main.go", "main") // true
strings.HasSuffix("main.go", ".go") // true
strings.EqualFold("Go", "go") // true(忽略大小写)
- 查找
strings.Index("hello", "ll") // 2
strings.LastIndex("hello", "l") // 3
strings.Count("hello", "l") // 2
- 分割与连接
parts := strings.Split("a,b,c", ",") // ["a","b","c"]
joined := strings.Join(parts, "-") // "a-b-c"
// 按空格切割(连续空格被视为一个分隔符)
words := strings.Fields(" hello world ") // ["hello","world"]
- 大小写转换
strings.ToUpper("go") // "GO"
strings.ToLower("GO") // "go"
strings.Title("go lang") // "Go Lang"(已废弃,推荐使用 cases 包)
- 修剪
strings.TrimSpace(" hello ") // "hello"
strings.Trim("!!hello!!", "!") // "hello"
strings.TrimLeft("/path/", "/") // "path/"
- 替换
r := strings.Replace("oink oink oink", "oink", "moo", 2) // "moo moo oink"
r = strings.ReplaceAll("oink oink", "oink", "moo") // "moo moo"
- Builder(高效字符串拼接)
var b strings.Builder
b.WriteString("Hello")
b.WriteString(" World")
result := b.String() // "Hello World"
bytes —— 字节切片操作
bytes 包提供了与 strings 相似的操作,但面向 []byte 类型,适合处理二进制数据或高效字符串操作。
- 基本操作
b := []byte("hello")
bytes.Contains(b, []byte("ll")) // true
bytes.Equal(b, []byte("hello")) // true
bytes.Index(b, []byte("e")) // 1
- 分割与连接
parts := bytes.Split([]byte("a,b,c"), []byte(","))
joined := bytes.Join(parts, []byte("-")) // "a-b-c"
- 大小写、修剪、替换
类比 strings,如 bytes.ToUpper、bytes.TrimSpace、bytes.ReplaceAll。
- Buffer(可读写字节缓冲区)
var buf bytes.Buffer
buf.WriteString("Hello")
buf.WriteByte(' ')
buf.Write([]byte("World"))
fmt.Println(buf.String()) // "Hello World"
bytes.Buffer 实现了 io.Reader 和 io.Writer,非常适合在内存中构建或处理数据流。
字符串处理工具实战
目标:编写一个工具,统计单词频率、替换敏感词、提取数字等,综合运用上述包。
package main
import (
"fmt"
"strconv"
"strings"
"unicode"
)
// WordStats 统计每个单词出现次数
func WordStats(text string) map[string]int {
counts := make(map[string]int)
words := strings.Fields(text)
for _, w := range words {
// 去除标点符号,只保留字母数字(简化)
cleaned := strings.TrimFunc(w, func(r rune) bool {
return !unicode.IsLetter(r) && !unicode.IsNumber(r)
})
cleaned = strings.ToLower(cleaned)
if cleaned != "" {
counts[cleaned]++
}
}
return counts
}
// ReplaceSensitive 将敏感词替换为 ***
func ReplaceSensitive(text string, keywords []string) string {
result := text
for _, kw := range keywords {
result = strings.ReplaceAll(result, kw, "***")
}
return result
}
// ExtractNumbers 提取字符串中的所有整数
func ExtractNumbers(text string) []int {
var nums []int
fields := strings.Fields(text)
for _, f := range fields {
// 去除可能的逗号或点
cleaned := strings.TrimRight(f, ",.")
if n, err := strconv.Atoi(cleaned); err == nil {
nums = append(nums, n)
}
}
return nums
}
func main() {
sample := "The quick brown fox jumps over the lazy dog. Fox is quick!"
stats := WordStats(sample)
fmt.Println("Word frequencies:", stats)
filtered := ReplaceSensitive("badword1 and badword2 are not allowed", []string{"badword1", "badword2"})
fmt.Println("Filtered:", filtered)
nums := ExtractNumbers("There are 12 apples and 34 oranges, totaling 46.")
fmt.Println("Numbers:", nums) // [12 34 46]
}
OS、IO、文件读写与日志分析器
os —— 操作系统交互
os 包提供平台无关的操作系统功能,包括文件操作、环境变量、进程管理等。
文件操作
// 打开文件(只读)
f, err := os.Open("file.txt")
// 创建或打开文件(读写、权限)
f, err := os.OpenFile("output.txt", os.O_CREATE|os.O_WRONLY, 0644)
// 创建文件
f, err := os.Create("new.txt")
// 读取整个文件
data, err := os.ReadFile("file.txt")
// 写入文件
err := os.WriteFile("file.txt", data, 0644)
// 检查错误(os.IsNotExist)
if _, err := os.Stat("file.txt"); os.IsNotExist(err) {
fmt.Println("File does not exist")
}
目录与环境
os.Mkdir("dir", 0755)
os.MkdirAll("path/to/dir", 0755) // 递归创建
os.Getwd()
os.Chdir("dir")
home := os.Getenv("HOME")
io —— 通用 I/O 抽象
io 包提供了 io.Reader 和 io.Writer 等核心接口,是所有流处理的基础。
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
常用函数:
io.ReadAll(r):读取全部数据。io.Copy(dst, src):从源复制到目标,直到 EOF。io.Pipe():创建内存管道。
bufio —— 缓冲 I/O
bufio 包装 io.Reader 或 io.Writer,提供缓冲读写,尤其适合逐行处理大文件。
f, _ := os.Open("large.log")
defer f.Close()
scanner := bufio.NewScanner(f)
for scanner.Scan() {
line := scanner.Text()
// 处理每一行
}
if err := scanner.Err(); err != nil {
// 处理错误
}
bufio.Scanner 默认按行分割(ScanLines),也可以自定义分割函数。
日志文件分析器实战
需求:读取服务器日志文件,统计包含 “ERROR” 的行数,并列出最近的 5 条错误信息。
日志格式示例:
2025-01-01 10:00:00 INFO Starting server
2025-01-01 10:01:00 ERROR failed to connect to database
2025-01-01 10:02:00 WARN slow query detected
2025-01-01 10:03:00 ERROR timeout
代码实现:
package main
import (
"bufio"
"fmt"
"os"
"strings"
)
type LogEntry struct {
LineNumber int
Content string
}
func AnalyzeLog(filePath string) (errorCount int, lastErrors []LogEntry, err error) {
f, err := os.Open(filePath)
if err != nil {
return 0, nil, fmt.Errorf("open file: %w", err)
}
defer f.Close()
scanner := bufio.NewScanner(f)
lineNum := 0
var errors []LogEntry
for scanner.Scan() {
lineNum++
line := scanner.Text()
if strings.Contains(line, "ERROR") {
errorCount++
errors = append(errors, LogEntry{lineNum, line})
}
}
if err := scanner.Err(); err != nil {
return 0, nil, fmt.Errorf("scan file: %w", err)
}
// 取最后5条错误(若不足5条则全部)
if len(errors) > 5 {
errors = errors[len(errors)-5:]
}
return errorCount, errors, nil
}
func main() {
count, lastErrors, err := AnalyzeLog("server.log")
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Printf("Total ERROR count: %d\n", count)
fmt.Println("Last error entries:")
for _, e := range lastErrors {
fmt.Printf(" Line %d: %s\n", e.LineNumber, e.Content)
}
}
time 与日期处理
time 包提供时间测量、显示和时区转换功能。
时间的基本概念
time.Time:表示一个时间点,包含时区信息。time.Duration:表示纳秒级的时间间隔。
获取时间
now := time.Now() // 当前本地时间
utcNow := time.Now().UTC() // UTC 时间
构造时间
t := time.Date(2025, time.January, 1, 12, 0, 0, 0, time.UTC)
格式化与解析
Go 使用特定的参考时间 Mon Jan 2 15:04:05 MST 2006(即 2006-01-02 15:04:05)来定义格式布局,必须用这个参考时间的数字表示。
格式化(Time to string):
t := time.Now()
fmt.Println(t.Format("2006-01-02 15:04:05"))
fmt.Println(t.Format("2006/01/02 03:04:05 PM"))
fmt.Println(t.Format(time.RFC3339))
解析(String to time):
s := "2025-01-01 12:00:00"
t, err := time.Parse("2006-01-02 15:04:05", s)
// 带时区解析
t, err = time.Parse(time.RFC3339, "2025-01-01T12:00:00Z")
时区转换
loc, _ := time.LoadLocation("America/New_York")
nyTime := time.Now().In(loc)
fmt.Println(nyTime.Format("2006-01-02 15:04:05 MST"))
时间运算
now := time.Now()
future := now.Add(24 * time.Hour) // 加一天
diff := future.Sub(now) // time.Duration
fmt.Println(diff.Hours())
时间戳与转换
// 获取 Unix 时间戳(秒、毫秒、纳秒)
sec := time.Now().Unix()
msec := time.Now().UnixMilli()
nsec := time.Now().UnixNano()
// 从时间戳创建时间
t := time.Unix(sec, 0)
实战 —— 倒计时程序与时间戳转换工具
倒计时程序
package main
import (
"fmt"
"time"
)
func main() {
target := time.Date(2026, time.January, 1, 0, 0, 0, 0, time.Local)
fmt.Printf("Countdown to %s\n", target.Format("2006-01-02 15:04:05"))
for {
remaining := time.Until(target)
if remaining <= 0 {
fmt.Println("Time's up!")
break
}
days := int(remaining.Hours() / 24)
hours := int(remaining.Hours()) % 24
minutes := int(remaining.Minutes()) % 60
seconds := int(remaining.Seconds()) % 60
fmt.Printf("\r%02d days %02d:%02d:%02d", days, hours, minutes, seconds)
time.Sleep(1 * time.Second)
}
}
时间戳转换工具
package main
import (
"fmt"
"os"
"strconv"
"time"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: tsconv <unix_timestamp> or tsconv <ISO8601 string>")
return
}
input := os.Args[1]
// 尝试解析为 Unix 时间戳(秒)
if ts, err := strconv.ParseInt(input, 10, 64); err == nil {
t := time.Unix(ts, 0)
fmt.Println("Local:", t.Format("2006-01-02 15:04:05"))
fmt.Println("UTC:", t.UTC().Format(time.RFC3339))
return
}
// 尝试解析为常见格式字符串
layouts := []string{
time.RFC3339,
"2006-01-02 15:04:05",
"2006-01-02",
}
for _, layout := range layouts {
if t, err := time.Parse(layout, input); err == nil {
fmt.Println("Unix timestamp:", t.Unix())
fmt.Println("Local:", t.Local().Format("2006-01-02 15:04:05"))
return
}
}
fmt.Println("Unrecognized time format")
}
核心要点速记
- fmt:格式化打印和扫描,
%v、%+v、%#v常用。 - strconv:数字/字符串互转,
Atoi、Itoa、ParseFloat。 - strings:查找、分割、替换、修剪,
Builder高效拼接。 - bytes:
[]byte版字符串操作,Buffer充当可读写缓冲区。 - os:文件/目录操作、环境变量、进程退出。
- io:
Reader/Writer接口,Copy、ReadAll。 - bufio:缓冲扫描,
Scanner逐行读取大文件。 - time:参考时间
2006-01-02 15:04:05定义格式,时区转换、时间戳、倒计时。
浙公网安备 33010602011771号