如何使用Golang将大文件等分为4个文件且保证数据顺序不变
问题根因
- 原有实现将所有读取到的行全部写入同一个公共通道,4个工作协程是随机竞争消费通道内的行数据,没有任何顺序保证,最终分块的内容自然会乱序
- 原有逻辑是按行数近似拆分,无法保证每个分块大小固定为25MB
优化实现
下面的实现既可以保证分块内数据顺序和原文件一致,也能严格按照你要求的25MB大小拆分,且不会把单行内容切分到两个分块中:
package main import ( "bufio" "log" "os" "sync" "github.com/google/uuid" ) const chunkSize = 25 * 1024 * 1024 // 单块大小25MB func main() { // 先确保chunks目录存在 err := os.MkdirAll("chunks", 0755) if err != nil { log.Fatal("创建chunks目录失败:", err) } file, err := os.Open("sampletest.txt") if err != nil { log.Fatal("打开源文件失败:", err) } defer file.Close() scanner := bufio.NewScanner(file) var wg sync.WaitGroup var currentChunk string var currentSize int for scanner.Scan() { line := scanner.Text() + "\n" lineSize := len(line) // 加入当前行后超过单块大小,就先写入现有块,再开新块 if currentSize+lineSize > chunkSize && currentSize > 0 { wg.Add(1) go writeChunk(currentChunk, &wg) currentChunk = "" currentSize = 0 } currentChunk += line currentSize += lineSize } // 处理最后不足25MB的剩余内容 if currentSize > 0 { wg.Add(1) go writeChunk(currentChunk, &wg) } if err := scanner.Err(); err != nil { log.Fatal("读取源文件失败:", err) } // 等待所有分块写入完成再退出 wg.Wait() } func writeChunk(data string, wg *sync.WaitGroup) { defer wg.Done() file, err := os.Create("chunks/" + uuid.New().String() + ".txt") if err != nil { log.Println("创建分块文件失败:", err) return } defer file.Close() _, err = file.WriteString(data) if err != nil { log.Println("写入分块文件失败:", err) } }
实现说明
- 按原文件顺序逐行读取,在内存中攒够25MB就异步写入新的分块文件,全局顺序不会乱
- 单块大小判断时会留足当前行的空间,不会把一行拆到两个分块里
- 用WaitGroup等待所有分块写入完成再退出程序,不会丢数据
- 如果你需要严格得到4个分块,哪怕最后一块不足25MB也凑4块,可以调整判断逻辑,固定攒够前3块各25MB,剩下的内容都放第4块即可
小提示:本地文件读取属于IO密集型操作,单协程顺序读取的效率远高于多协程随机读取,上面的实现兼顾了读取效率和写入并发,是处理这类文件拆分场景的最优选择。
内容的提问来源于stack exchange,提问作者Krishna Chaitanya
相关产品推荐
相关产品推荐

