Go学习第9天:并发编程 + 文件操作 + 正则表达式

📅 2026/6/16 23:22:57
Go学习第9天:并发编程 + 文件操作 + 正则表达式
Go 语言并发编程 文件操作 正则表达式目录一、Go 并发编程1.1 核心基础概念1.2 Goroutine 协程1.2.1 基本语法1.2.2 协程等待sync.WaitGroup1.2.3 踩坑1.3 Channel 通道1.3.1 分类与基础语法1. 无缓冲通道同步通道2. 有缓冲通道1.3.2 通道关闭 遍历1.3.3 单向通道只读/只写1.3.4 通道踩坑1.4 select 多路监听1.5 同步锁解决数据竞争1.6 并发常见问题二、文件与目录操作2.1 常用函数速查表2.2 基础文件操作2.2.1 创建 打开 关闭文件2.2.2 读取文件方式1一次性读取小文件方式2逐行读取大文件bufio2.2.3 写入文件方式1整体覆盖写入方式2追加写入os.O_APPEND方式3缓冲写入大批量数据2.3 目录操作2.4 辅助操作2.5 文件操作踩坑三、正则表达式3.1 核心函数3.2 常用正则元字符3.3 实战示例3.3.1 匹配校验如账号、手机号3.3.2 提取所有匹配内容3.3.3 正则替换3.3.4 字符串分割3.4 正则踩坑四、速记表本文整理Go 并发编程、文件读写操作、正则表达式三大实用模块每个章节包含语法说明、完整可运行示例、核心规则、高频踩坑适配 VSCode Go Modules 开发环境兼顾基础使用与实战场景。目录并发编程Goroutine、Channel、Select、同步工具文件与目录操作正则表达式知识点速记一、Go 并发编程Go 原生支持高并发核心组件为Goroutine轻量级协程和Channel通道搭配select、sync包实现协程同步、通信与资源安全区别于传统线程模型开销极低。1.1 核心基础概念GoroutineGo 运行时管理的轻量级执行单元由go关键字启动数千上万个协程也可高效调度。Channel协程之间专用通信管道实现数据传递与同步推荐以通信代替共享内存规避多线程数据竞争。GMP 调度模型Go 底层调度机制G协程、M系统线程、P逻辑处理器开发者无需手动管理。常见并发问题死锁、数据竞争。1.2 Goroutine 协程1.2.1 基本语法使用go 函数名(参数)即可启动一个新协程主协程main退出后所有子协程会直接终止。package(main)importfmtimporttime// 普通函数funcsayHello(){fori:0;i5;i{fmt.Println(Hello)time.Sleep(100*time.Millisecond)}}funcmain(){gosayHello()// 启动子协程// 主协程循环fori:0;i5;i{fmt.Println(Main)time.Sleep(100*time.Millisecond)}}运行特点输出顺序随机两个协程交替执行。1.2.2 协程等待sync.WaitGroup单纯go启动协程无法等待执行完毕sync.WaitGroup专门用于批量等待多个协程结束。核心方法Add(n)设置/增加等待计数器Done()协程执行完毕计数器减 1Wait()阻塞主协程直到计数器为 0。示例packagemainimport(fmtsync)funcworker(idint,wg*sync.WaitGroup){deferwg.Done()// 函数结束自动计数减1fmt.Printf(协程 %d 执行完毕\n,id)}funcmain(){varwg sync.WaitGroup// 启动3个协程fori:1;i3;i{wg.Add(1)// 计数器1goworker(i,wg)}wg.Wait()// 等待所有协程完成fmt.Println(所有协程执行结束)}1.2.3 踩坑忘记调用Add()/Done()导致Wait()永久阻塞或提前退出WaitGroup必须传指针值传递会拷贝对象计数失效主函数提前退出子协程会被强制终止。1.3 Channel 通道通道是协程间的数据载体使用chan关键字声明make创建通过-完成收发。1.3.1 分类与基础语法1. 无缓冲通道同步通道创建ch : make(chan 数据类型)规则发送方、接收方必须同时就绪否则互相阻塞。packagemainimportfmtfuncsum(s[]int,cchanint){total:0for_,v:ranges{totalv}c-total// 向通道发送数据}funcmain(){s:[]int{7,2,8,-9,4,0}c:make(chanint)gosum(s[:len(s)/2],c)gosum(s[len(s)/2:],c)x,y:-c,-c// 从通道接收数据fmt.Println(xy)}2. 有缓冲通道创建ch : make(chan 类型, 缓冲区大小)规则缓冲区未满时发送不阻塞缓冲区满/无数据时阻塞。packagemainimportfmtfuncmain(){// 缓冲区大小为2ch:make(chanint,2)ch-1ch-2fmt.Println(-ch)fmt.Println(-ch)}1.3.2 通道关闭 遍历close(ch)关闭通道关闭后不可再发送数据但仍可接收for range遍历通道通道关闭后循环自动退出通道不关闭会永久阻塞。示例packagemainimportfmtfuncfib(nint,cchanint){x,y:0,1fori:0;in;i{c-x x,yy,xy}close(c)// 关闭通道}funcmain(){c:make(chanint,10)gofib(10,c)// 遍历通道forval:rangec{fmt.Println(val)}}1.3.3 单向通道只读/只写限制通道方向提升代码安全性chan- int只写通道仅发送数据-chan int只读通道仅接收数据。1.3.4 通道踩坑向已关闭通道发送数据 → 运行 panic接收无数据、无缓冲且无发送方的通道 → 死锁重复关闭通道 → panic遍历未关闭的通道 → 永久阻塞。1.4 select 多路监听select用于同时监听多个通道语法类似switch多个case同时就绪随机选一个执行所有 case 未就绪无default则阻塞有default直接执行默认分支。示例packagemainimportfmtfuncfib(c,quitchanint){x,y:0,1for{select{casec-x:x,yy,xycase-quit:// 接收退出信号fmt.Println(协程退出)return}}}funcmain(){c:make(chanint)quit:make(chanint)gofunc(){fori:0;i10;i{fmt.Println(-c)}quit-0// 发送退出信号}()fib(c,quit)}1.5 同步锁解决数据竞争多个协程同时读写同一共享变量会产生数据竞争使用sync.Mutex互斥锁、sync.RWMutex读写锁保护资源。Lock()加锁进入临界区Unlock()解锁释放资源。packagemainimport(fmtsync)varcountintvarmu sync.Mutexfuncadd(wg*sync.WaitGroup){deferwg.Done()mu.Lock()// 加锁countmu.Unlock()// 解锁}funcmain(){varwg sync.WaitGroupfori:0;i10;i{wg.Add(1)goadd(wg)}wg.Wait()fmt.Println(count)}1.6 并发常见问题死锁所有协程互相等待无任何协程继续执行诱因通道收发不匹配、锁未释放、循环等待。数据竞争多协程同时读写共享变量解决方案使用通道通信 或 互斥锁。二、文件与目录操作Go 依靠标准库完成文件/目录读写、创建、删除、遍历等操作核心库os底层文件/目录操作主流bufio带缓冲读写优化大文件 I/O 性能io通用读写接口path/filepath跨平台路径处理ioutilGo1.16 已弃用功能迁移至os/io。2.1 常用函数速查表库/函数作用os.Create(name)创建文件存在则清空内容os.Open(name)只读打开文件os.OpenFile()自定义模式读写/追加打开文件os.ReadFile(name)一次性读取整个文件小文件首选os.WriteFile()一次性写入文件覆盖os.Remove()删除文件/空目录os.Mkdir()创建单层目录os.MkdirAll()递归创建多级目录bufio.Scanner逐行读取大文件bufio.Writer缓冲写入提升效率filepath.Join()跨平台拼接路径2.2 基础文件操作2.2.1 创建 打开 关闭文件规则文件打开后必须关闭推荐搭配defer延迟关闭避免文件句柄泄漏。packagemainimport(logos)funcmain(){// 创建文件file,err:os.Create(test.txt)iferr!nil{log.Fatal(err)}deferfile.Close()// 函数退出前自动关闭文件log.Println(文件创建成功)// 只读打开文件f2,err:os.Open(test.txt)iferr!nil{log.Fatal(err)}deferf2.Close()}2.2.2 读取文件方式1一次性读取小文件packagemainimport(fmtos)funcmain(){data,err:os.ReadFile(test.txt)iferr!nil{fmt.Println(读取失败,err)return}fmt.Println(string(data))// 字节切片转字符串}方式2逐行读取大文件bufiopackagemainimport(bufiofmtos)funcmain(){file,err:os.Open(test.txt)iferr!nil{fmt.Println(err)return}deferfile.Close()scanner:bufio.NewScanner(file)forscanner.Scan(){fmt.Println(scanner.Text())// 逐行输出}// 检查读取错误iferr:scanner.Err();err!nil{fmt.Println(读取异常,err)}}2.2.3 写入文件方式1整体覆盖写入packagemainimport(fmtos)funcmain(){content:[]byte(Hello Go 文件操作)err:os.WriteFile(test.txt,content,0644)iferr!nil{fmt.Println(err)}}方式2追加写入os.O_APPENDpackagemainimport(fmtos)funcmain(){// 追加只写文件权限 0644file,err:os.OpenFile(test.txt,os.O_APPEND|os.O_WRONLY,0644)iferr!nil{fmt.Println(err)return}deferfile.Close()_,_file.WriteString(\n追加新内容)}方式3缓冲写入大批量数据packagemainimport(bufiofmtos)funcmain(){file,err:os.Create(write.txt)iferr!nil{fmt.Println(err)return}deferfile.Close()writer:bufio.NewWriter(file)_,_writer.WriteString(缓冲写入内容\n)writer.Flush()// 刷新缓冲区数据落地磁盘}2.3 目录操作packagemainimport(logos)funcmain(){// 1. 创建单层目录err:os.Mkdir(mydir,0755)iferr!nil{log.Println(err)}// 2. 递归创建多级目录erros.MkdirAll(a/b/c,0755)iferr!nil{log.Println(err)}// 3. 读取目录下文件/子目录entries,err:os.ReadDir(.)iferr!nil{log.Fatal(err)}for_,entry:rangeentries{fmt.Println(entry.Name(),entry.IsDir())}// 4. 删除空目录/文件_os.Remove(mydir)// 递归删除目录及所有内容_os.RemoveAll(a)}2.4 辅助操作判断文件是否存在if_,err:os.Stat(test.txt);os.IsNotExist(err){fmt.Println(文件不存在)}文件复制使用io.Copy跨平台路径统一使用filepath.Join(dir, file.txt)拼接路径兼容 Windows / Linux / Mac。2.5 文件操作踩坑打开文件后忘记 Close长期运行导致文件句柄泄漏追加文件未使用os.O_APPEND直接覆盖原有内容缓冲写入后未执行Flush()数据留在缓冲区磁盘无内容权限数字0644/0755必须以0开头写法错误导致权限异常区分os.Remove仅删空目录/文件和os.RemoveAll递归删除。三、正则表达式Go 使用标准库regexp实现正则用于字符串匹配、查找、替换、分割适合表单校验、文本提取等场景。3.1 核心函数regexp.MustCompile(正则)编译正则出错直接 panic常用regexp.Compile(正则)编译正则返回错误严谨场景MatchString()判断字符串是否完全匹配规则FindString()获取第一个匹配结果FindAllString()获取所有匹配结果ReplaceAllString()正则替换Split()按正则分割字符串。3.2 常用正则元字符符号作用.匹配任意单个字符不含换行\d数字[0-9]\w字母、数字、下划线\s空白符空格/制表符/换行前面字符出现 1 次及以上*前面字符出现 0 次及以上?前面字符出现 0 或 1 次^字符串开头$字符串结尾[]匹配括号内任意字符Go 中推荐使用反引号包裹正则避免\二次转义。3.3 实战示例3.3.1 匹配校验如账号、手机号packagemainimport(fmtregexp)funcmain(){// 匹配纯字母数字reg:regexp.MustCompile(^[a-zA-Z0-9]$)str1:Go123str2:Go123fmt.Println(reg.MatchString(str1))// truefmt.Println(reg.MatchString(str2))// false}3.3.2 提取所有匹配内容packagemainimport(fmtregexp)funcmain(){// 提取所有数字reg:regexp.MustCompile(\d)str:苹果3个香蕉5斤res:reg.FindAllString(str,-1)fmt.Println(res)// [3 5]}3.3.3 正则替换packagemainimport(fmtregexp)funcmain(){// 把多个空格替换为单个空格reg:regexp.MustCompile(\s)str:Hello World GonewStr:reg.ReplaceAllString(str, )fmt.Println(newStr)// Hello World Go}3.3.4 字符串分割packagemainimport(fmtregexp)funcmain(){reg:regexp.MustCompile(,)str:apple,banana,orangeparts:reg.Split(str,-1)fmt.Println(parts)}3.4 正则踩坑普通双引号内\需要转义为\\优先使用反引号正则编译建议全局一次执行不要循环内反复Compile性能差MustCompile正则语法错误会直接崩溃正式环境优先用Compile 错误判断复杂正则会大幅降低性能大数据场景尽量改用普通字符串处理。四、速记表模块核心要点高频坑点并发go启动协程channel协程通信select监听多通道WaitGroup等待Mutex加锁通道死锁、协程计数错误、锁忘记释放文件操作os为主bufio做缓冲defer关闭文件0644权限Append追加句柄泄漏、缓冲未刷新、覆盖/追加混淆正则regexp包原生字符串Compile/MustCompile转义符问题、循环编译正则、复杂正则性能低