当前位置: 首页 > news >正文

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

Go 语言:并发编程 + 文件操作 + 正则表达式

    • 目录
  • 一、Go 并发编程
    • 1.1 核心基础概念
    • 1.2 Goroutine 协程
      • 1.2.1 基本语法
      • 1.2.2 协程等待:sync.WaitGroup
      • 1.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:逐行读取(大文件,bufio)
      • 2.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 开发环境,兼顾基础使用与实战场景。

目录

  1. 并发编程(Goroutine、Channel、Select、同步工具)
  2. 文件与目录操作
  3. 正则表达式
  4. 知识点速记

一、Go 并发编程

Go 原生支持高并发,核心组件为Goroutine(轻量级协程)Channel(通道),搭配selectsync包实现协程同步、通信与资源安全,区别于传统线程模型,开销极低。

1.1 核心基础概念

  1. Goroutine:Go 运行时管理的轻量级执行单元,由go关键字启动,数千上万个协程也可高效调度。
  2. Channel:协程之间专用通信管道,实现数据传递与同步,推荐以通信代替共享内存,规避多线程数据竞争。
  3. GMP 调度模型:Go 底层调度机制(G=协程、M=系统线程、P=逻辑处理器),开发者无需手动管理。
  4. 常见并发问题:死锁数据竞争

1.2 Goroutine 协程

1.2.1 基本语法

使用go 函数名(参数)即可启动一个新协程,主协程(main)退出后,所有子协程会直接终止。

package(main)import"fmt"import"time"// 普通函数funcsayHello(){fori:=0;i<5;i++{fmt.Println("Hello")time.Sleep(100*time.Millisecond)}}funcmain(){gosayHello()// 启动子协程// 主协程循环fori:=0;i<5;i++{fmt.Println("Main")time.Sleep(100*time.Millisecond)}}

运行特点:输出顺序随机,两个协程交替执行。

1.2.2 协程等待:sync.WaitGroup

单纯go启动协程无法等待执行完毕,sync.WaitGroup专门用于批量等待多个协程结束
核心方法:

  • Add(n):设置/增加等待计数器;
  • Done():协程执行完毕,计数器减 1;
  • Wait():阻塞主协程,直到计数器为 0。

示例

packagemainimport("fmt""sync")funcworker(idint,wg*sync.WaitGroup){deferwg.Done()// 函数结束自动计数减1fmt.Printf("协程 %d 执行完毕\n",id)}funcmain(){varwg sync.WaitGroup// 启动3个协程fori:=1;i<=3;i++{wg.Add(1)// 计数器+1goworker(i,&wg)}wg.Wait()// 等待所有协程完成fmt.Println("所有协程执行结束")}

1.2.3 踩坑

  1. 忘记调用Add()/Done(),导致Wait()永久阻塞或提前退出;
  2. WaitGroup必须传指针,值传递会拷贝对象,计数失效;
  3. 主函数提前退出,子协程会被强制终止。

1.3 Channel 通道

通道是协程间的数据载体,使用chan关键字声明,make创建,通过<-完成收发。

1.3.1 分类与基础语法

1. 无缓冲通道(同步通道)

创建:ch := make(chan 数据类型)
规则:发送方、接收方必须同时就绪,否则互相阻塞。

packagemainimport"fmt"funcsum(s[]int,cchanint){total:=0for_,v:=ranges{total+=v}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(x+y)}
2. 有缓冲通道

创建:ch := make(chan 类型, 缓冲区大小)
规则:缓冲区未满时发送不阻塞;缓冲区满/无数据时阻塞。

packagemainimport"fmt"funcmain(){// 缓冲区大小为2ch:=make(chanint,2)ch<-1ch<-2fmt.Println(<-ch)fmt.Println(<-ch)}

1.3.2 通道关闭 & 遍历

  • close(ch):关闭通道,关闭后不可再发送数据,但仍可接收;
  • for range:遍历通道,通道关闭后循环自动退出(通道不关闭会永久阻塞)。

示例

packagemainimport"fmt"funcfib(nint,cchanint){x,y:=0,1fori:=0;i<n;i++{c<-x x,y=y,x+y}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 通道踩坑

  1. 已关闭通道发送数据 → 运行 panic;
  2. 接收无数据、无缓冲且无发送方的通道 → 死锁;
  3. 重复关闭通道 → panic;
  4. 遍历未关闭的通道 → 永久阻塞。

1.4 select 多路监听

select用于同时监听多个通道,语法类似switch

  1. 多个case同时就绪:随机选一个执行
  2. 所有 case 未就绪:无default则阻塞,有default直接执行默认分支。

示例

packagemainimport"fmt"funcfib(c,quitchanint){x,y:=0,1for{select{casec<-x:x,y=y,x+ycase<-quit:// 接收退出信号fmt.Println("协程退出")return}}}funcmain(){c:=make(chanint)quit:=make(chanint)gofunc(){fori:=0;i<10;i++{fmt.Println(<-c)}quit<-0// 发送退出信号}()fib(c,quit)}

1.5 同步锁(解决数据竞争)

多个协程同时读写同一共享变量会产生数据竞争,使用sync.Mutex(互斥锁)、sync.RWMutex(读写锁)保护资源。

  • Lock():加锁,进入临界区;
  • Unlock():解锁,释放资源。
packagemainimport("fmt""sync")varcountintvarmu sync.Mutexfuncadd(wg*sync.WaitGroup){deferwg.Done()mu.Lock()// 加锁count++mu.Unlock()// 解锁}funcmain(){varwg sync.WaitGroupfori:=0;i<10;i++{wg.Add(1)goadd(&wg)}wg.Wait()fmt.Println(count)}

1.6 并发常见问题

  1. 死锁:所有协程互相等待,无任何协程继续执行;
    诱因:通道收发不匹配、锁未释放、循环等待。
  2. 数据竞争:多协程同时读写共享变量;
    解决方案:使用通道通信 或 互斥锁。

二、文件与目录操作

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("log""os")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("fmt""os")funcmain(){data,err:=os.ReadFile("test.txt")iferr!=nil{fmt.Println("读取失败:",err)return}fmt.Println(string(data))// 字节切片转字符串}
方式2:逐行读取(大文件,bufio)
packagemainimport("bufio""fmt""os")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("fmt""os")funcmain(){content:=[]byte("Hello Go 文件操作")err:=os.WriteFile("test.txt",content,0644)iferr!=nil{fmt.Println(err)}}
方式2:追加写入(os.O_APPEND
packagemainimport("fmt""os")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("bufio""fmt""os")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("log""os")funcmain(){// 1. 创建单层目录err:=os.Mkdir("mydir",0755)iferr!=nil{log.Println(err)}// 2. 递归创建多级目录err=os.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 辅助操作

  1. 判断文件是否存在
if_,err:=os.Stat("test.txt");os.IsNotExist(err){fmt.Println("文件不存在")}
  1. 文件复制:使用io.Copy
  2. 跨平台路径:统一使用filepath.Join("dir", "file.txt")拼接路径,兼容 Windows / Linux / Mac。

2.5 文件操作踩坑

  1. 打开文件后忘记 Close,长期运行导致文件句柄泄漏;
  2. 追加文件未使用os.O_APPEND,直接覆盖原有内容;
  3. 缓冲写入后未执行Flush(),数据留在缓冲区,磁盘无内容;
  4. 权限数字0644/0755必须以0开头,写法错误导致权限异常;
  5. 区分os.Remove(仅删空目录/文件)和os.RemoveAll(递归删除)。

三、正则表达式

Go 使用标准库regexp实现正则,用于字符串匹配、查找、替换、分割,适合表单校验、文本提取等场景。

3.1 核心函数

  1. regexp.MustCompile(正则):编译正则,出错直接 panic(常用);
  2. regexp.Compile(正则):编译正则,返回错误(严谨场景);
  3. MatchString():判断字符串是否完全匹配规则;
  4. FindString():获取第一个匹配结果;
  5. FindAllString():获取所有匹配结果;
  6. ReplaceAllString():正则替换;
  7. Split():按正则分割字符串。

3.2 常用正则元字符

符号作用
.匹配任意单个字符(不含换行)
\d数字[0-9]
\w字母、数字、下划线
\s空白符(空格/制表符/换行)
+前面字符出现 1 次及以上
*前面字符出现 0 次及以上
?前面字符出现 0 或 1 次
^字符串开头
$字符串结尾
[]匹配括号内任意字符

Go 中推荐使用反引号`包裹正则,避免\二次转义。

3.3 实战示例

3.3.1 匹配校验(如账号、手机号)

packagemainimport("fmt""regexp")funcmain(){// 匹配纯字母+数字reg:=regexp.MustCompile(`^[a-zA-Z0-9]+$`)str1:="Go123"str2:="Go@123"fmt.Println(reg.MatchString(str1))// truefmt.Println(reg.MatchString(str2))// false}

3.3.2 提取所有匹配内容

packagemainimport("fmt""regexp")funcmain(){// 提取所有数字reg:=regexp.MustCompile(`\d+`)str:="苹果3个,香蕉5斤"res:=reg.FindAllString(str,-1)fmt.Println(res)// [3 5]}

3.3.3 正则替换

packagemainimport("fmt""regexp")funcmain(){// 把多个空格替换为单个空格reg:=regexp.MustCompile(`\s+`)str:="Hello World Go"newStr:=reg.ReplaceAllString(str," ")fmt.Println(newStr)// Hello World Go}

3.3.4 字符串分割

packagemainimport("fmt""regexp")funcmain(){reg:=regexp.MustCompile(`,`)str:="apple,banana,orange"parts:=reg.Split(str,-1)fmt.Println(parts)}

3.4 正则踩坑

  1. 普通双引号内\需要转义为\\,优先使用`反引号;
  2. 正则编译建议全局一次执行,不要循环内反复Compile(性能差);
  3. MustCompile正则语法错误会直接崩溃,正式环境优先用Compile+ 错误判断;
  4. 复杂正则会大幅降低性能,大数据场景尽量改用普通字符串处理。

四、速记表

模块核心要点高频坑点
并发go启动协程;channel协程通信;select监听多通道;WaitGroup等待;Mutex加锁通道死锁、协程计数错误、锁忘记释放
文件操作os为主,bufio做缓冲;defer关闭文件;0644权限;Append追加句柄泄漏、缓冲未刷新、覆盖/追加混淆
正则regexp包;`原生字符串;Compile/MustCompile转义符问题、循环编译正则、复杂正则性能低
http://www.zskr.cn/news/1538107.html

相关文章:

  • 2026镇江防水公司推荐|全域正规屋面防水/SBS防水/彩钢瓦防腐翻新5家合规企业排行榜+避坑攻略 - 资讯快报
  • 定制儿童餐具生产厂家 - 资讯快报
  • 终极指南:如何通过AES密钥逆向工程《鸣潮》游戏模组开发
  • 黑苹果终极简化方案:OpCore Simplify一键生成OpenCore EFI完整指南
  • 终极指南:如何用foobox-cn打造专业级foobar2000音乐播放器界面
  • QMan PFDR内存配置与设备树节点详解:嵌入式网络处理性能优化
  • 换了电脑或重装系统后Git推送失败?快速恢复Gitee/SSH访问权限的3个关键步骤
  • 2026亲测推荐:901树脂实践案例分享 - 资讯快报
  • SolidWorks第四部分_直接实体建模特征10_移动面操作
  • 华为云INSPIRE大会全解读:面向Agent时代的完整AI基础设施全家桶
  • QQ机器人-Astrbot搭配NapCat框架插件文件发送问题 - windows99
  • 2026福州大平层装修公司哪家靠谱?最新排行榜与避坑指南 - 资讯快报
  • Python ezdxf:7天掌握DXF文件处理的完整指南
  • 3个技术突破:Path of Building PoE2如何解决流放之路2角色构建的复杂性问题
  • 面试逆袭攻略:Java面试常见陷阱与应对策略
  • 单词背了很多,英文文章还是读不懂?
  • AI落地三重刻度:业务偏移、人力节省与自主迭代
  • CARLA四大交通模拟模块原理与协同实战指南
  • 告别手速焦虑:大麦自动抢票工具终极指南,轻松获取心仪演出门票
  • 熵码匠艺:用熵减思维重构代码质量与长期可维护性
  • LLM六维能力评估体系:面向真实业务场景的可落地压力测试
  • C#字符串内存分配与驻留池原理实战
  • Input Leap终极教程:如何用一套键盘鼠标控制多台电脑
  • 深入解析NXP PXS20 MCU:SSCM系统配置与STM定时器实战指南
  • 光电效应实验避坑指南:暗电流、本底电流和遏止电压,新手最容易搞错的三个点
  • 北京周边上门回收邮票纪念币,整册邮品工艺品当场结算 - 深鉴新闻
  • 软解析器自定义协议开发指南:从XML配置到网络数据包解析实战
  • 中国 PG 在全球排第几?这场直播给出了答案
  • 洛阳三家老牌清真涮牛肚门店实地对比测评 - 资讯快报
  • 《Python程序设计》实验四实验报告