插件窝 干货文章 Go语言文本去重:如何优化75万行文本的去重效率?

Go语言文本去重:如何优化75万行文本的去重效率?

err line scanner outputFile 166    来源:    2025-03-15

在处理75万行文本的去重任务时,效率是关键。以下是一些优化Go语言文本去重效率的策略:

1. 使用高效的数据结构

使用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()
}

2. 并行处理

如果文本文件非常大,可以考虑将文件分成多个部分,并行处理每个部分,最后再合并结果。

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()
}

3. 使用更高效的I/O操作

使用bufio包中的Scanner来逐行读取文件,而不是一次性读取整个文件到内存中。

4. 内存优化

如果内存是一个瓶颈,可以考虑使用外部排序和归并的方法,将文件分成多个小块,分别去重后再合并。

5. 使用Bloom Filter

如果允许一定的误判率,可以使用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()
}

6. 使用数据库

如果文本数据非常大,可以考虑将数据导入数据库(如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万行文本去重的效率。根据具体的应用场景和资源限制,可以选择合适的优化策略。