在处理75万行文本的去重任务时,效率是关键。以下是一些优化Go语言文本去重效率的策略:
使用map
来存储已经出现的文本行,因为map
的查找和插入操作的平均时间复杂度是O(1)。
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
file, err := os.Open("input.txt")
if err != nil {
panic(err)
}
defer file.Close()
uniqueLines := make(map[string]bool)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
uniqueLines[line] = true
}
if err := scanner.Err(); err != nil {
panic(err)
}
outputFile, err := os.Create("output.txt")
if err != nil {
panic(err)
}
defer outputFile.Close()
writer := bufio.NewWriter(outputFile)
for line := range uniqueLines {
fmt.Fprintln(writer, line)
}
writer.Flush()
}
如果文本文件非常大,可以考虑将文件分成多个部分,并行处理每个部分,最后再合并结果。
package main
import (
"bufio"
"fmt"
"os"
"sync"
)
func processFileSegment(file *os.File, start, end int64, wg *sync.WaitGroup, resultChan chan<- map[string]bool) {
defer wg.Done()
uniqueLines := make(map[string]bool)
file.Seek(start, 0)
scanner := bufio.NewScanner(file)
var currentPos int64 = start
for scanner.Scan() {
line := scanner.Text()
uniqueLines[line] = true
currentPos += int64(len(line) + 1) // +1 for newline character
if currentPos >= end {
break
}
}
resultChan <- uniqueLines
}
func main() {
file, err := os.Open("input.txt")
if err != nil {
panic(err)
}
defer file.Close()
fileInfo, err := file.Stat()
if err != nil {
panic(err)
}
fileSize := fileInfo.Size()
numSegments := 4
segmentSize := fileSize / int64(numSegments)
var wg sync.WaitGroup
resultChan := make(chan map[string]bool, numSegments)
for i := 0; i < numSegments; i++ {
wg.Add(1)
start := int64(i) * segmentSize
end := start + segmentSize
if i == numSegments-1 {
end = fileSize
}
go processFileSegment(file, start, end, &wg, resultChan)
}
wg.Wait()
close(resultChan)
uniqueLines := make(map[string]bool)
for segment := range resultChan {
for line := range segment {
uniqueLines[line] = true
}
}
outputFile, err := os.Create("output.txt")
if err != nil {
panic(err)
}
defer outputFile.Close()
writer := bufio.NewWriter(outputFile)
for line := range uniqueLines {
fmt.Fprintln(writer, line)
}
writer.Flush()
}
使用bufio
包中的Scanner
来逐行读取文件,而不是一次性读取整个文件到内存中。
如果内存是一个瓶颈,可以考虑使用外部排序和归并的方法,将文件分成多个小块,分别去重后再合并。
如果允许一定的误判率,可以使用Bloom Filter来快速判断一个元素是否已经存在,从而减少内存使用。
package main
import (
"bufio"
"fmt"
"os"
"github.com/willf/bloom"
)
func main() {
file, err := os.Open("input.txt")
if err != nil {
panic(err)
}
defer file.Close()
filter := bloom.New(1000000, 5) // Adjust parameters based on expected number of items and false positive rate
scanner := bufio.NewScanner(file)
outputFile, err := os.Create("output.txt")
if err != nil {
panic(err)
}
defer outputFile.Close()
writer := bufio.NewWriter(outputFile)
for scanner.Scan() {
line := scanner.Text()
if !filter.TestString(line) {
filter.AddString(line)
fmt.Fprintln(writer, line)
}
}
if err := scanner.Err(); err != nil {
panic(err)
}
writer.Flush()
}
如果文本数据非常大,可以考虑将数据导入数据库(如SQLite),利用数据库的去重功能。
package main
import (
"database/sql"
"fmt"
"log"
"os"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, err := sql.Open("sqlite3", "./unique_lines.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
_, err = db.Exec("CREATE TABLE IF NOT EXISTS lines (line TEXT PRIMARY KEY)")
if err != nil {
log.Fatal(err)
}
file, err := os.Open("input.txt")
if err != nil {
log.Fatal(err)
}
defer file.Close()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := scanner.Text()
_, err := db.Exec("INSERT OR IGNORE INTO lines (line) VALUES (?)", line)
if err != nil {
log.Fatal(err)
}
}
if err := scanner.Err(); err != nil {
log.Fatal(err)
}
outputFile, err := os.Create("output.txt")
if err != nil {
log.Fatal(err)
}
defer outputFile.Close()
writer := bufio.NewWriter(outputFile)
rows, err := db.Query("SELECT line FROM lines")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
var line string
if err := rows.Scan(&line); err != nil {
log.Fatal(err)
}
fmt.Fprintln(writer, line)
}
writer.Flush()
}
通过使用高效的数据结构、并行处理、优化I/O操作、内存优化、Bloom Filter以及数据库等方法,可以显著提高Go语言处理75万行文本去重的效率。根据具体的应用场景和资源限制,可以选择合适的优化策略。