Чтение строк в текстовом файле, сортировка, затем перезапись файла

Я пытаюсь написать функцию go, которая будет читать строки в текстовом файле, сортировать их (по алфавиту) и перезаписывать их обратно в файл. Прямо сейчас я могу по существу эмулировать cat, но я не могу манипулировать содержимым элементов в read_line.

func sort() {

    ff, _ := os.OpenFile(file, os.O_RDWR, 0666)
    f := bufio.NewReader(ff)
    for {
        read_line, _ := f.ReadString('\n')
        fmt.Print(read_line)
        if read_line == "" {
            break
        }
    }
    ff.Close()
}

когда я использую ReadString, как я могу сохранить каждую строку в срезе (или есть лучший способ сохранить их, чтобы я мог ими манипулировать)? Затем я бы использовал пакет сортировки примерно так:

sorted := sort.Strings(lines) 

затем, чтобы записать в файл, я использую что-то похожее на следующее, хотя я не включил его, потому что я еще не получил «сортировку» для работы:

io.WriteString(ff, (lines + "\n"))

Заранее спасибо за любые предложения!


person rick    schedule 14.09.2011    source источник


Ответы (4)


Например,

package main

import (
    "bufio"
    "fmt"
    "os"
    "sort"
)

func readLines(file string) (lines []string, err os.Error) {
    f, err := os.Open(file)
    if err != nil {
        return nil, err
    }
    defer f.Close()
    r := bufio.NewReader(f)
    for {
        const delim = '\n'
        line, err := r.ReadString(delim)
        if err == nil || len(line) > 0 {
            if err != nil {
                line += string(delim)
            }
            lines = append(lines, line)
        }
        if err != nil {
            if err == os.EOF {
                break
            }
            return nil, err
        }
    }
    return lines, nil
}

func writeLines(file string, lines []string) (err os.Error) {
    f, err := os.Create(file)
    if err != nil {
        return err
    }
    defer f.Close()
    w := bufio.NewWriter(f)
    defer w.Flush()
    for _, line := range lines {
        _, err := w.WriteString(line)
        if err != nil {
            return err
        }
    }
    return nil
}

func main() {
    file := `lines.txt`
    lines, err := readLines(file)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
    sort.Strings(lines)
    err = writeLines(file, lines)
    if err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}
person peterSO    schedule 15.09.2011

Это довольно простой способ сделать это.

import (
    "bytes"
    "io/ioutil"
    "sort"
)

// allow [][]byte to implement the sort.Interface interface
type lexicographically [][]byte

// bytes.Compare compares the byte slices lexicographically (alphabetically)
func (l lexicographically) Less(i, j int) bool { return bytes.Compare(l[i], l[j]) < 0 }
func (l lexicographically) Len() int           { return len(l) }
func (l lexicographically) Swap(i, j int)      { l[i], l[j] = l[j], l[i] }

func SortFile(name string) error {
    content, err := ioutil.ReadFile(name)
    if err != nil {
        return err
    }

    lines := bytes.Split(content, []byte{'\n'})
    sort.Sort(lexicographically(lines))

    content = bytes.Join(lines, []byte{'\n'})
    return ioutil.WriteFile(name, content, 0644)
}
person eric chiang    schedule 25.09.2014
comment
Вы не проверяете scanner.Err. Вам не нужно повторно открывать файл, вы можете просто вызвать file.Truncate(0) (игнорируя тот факт, что неиспользование второго файла опасно тем, что он потеряет данные, если программа остановится). И вы не проверяете ошибку от file.Close() (может быть важно при записи файла). - person Dave C; 26.05.2015

поскольку вы собираетесь сортировать строки, вам нужно прочитать весь файл. вы можете либо проглотить файл с помощью io/ioutil.ReadAll, либо вы можете просто написать небольшую функцию пролистывания. когда у вас есть строки файла, их сортировку можно выполнить с помощью вызова sort.Strings. я добавлю, возможно, слишком многословную версию, которая, надеюсь, иллюстрирует, как это можно сделать. я также рекомендую прочитать это отличное объяснение того, как работает пакет сортировки go: пакет сортировки Go

package main

import (
    "os"
    "bufio"
    "fmt"
    "sort"
)

// slurp file into slice of lines/strings
func slurp(f string) (lines []string, e os.Error) {

    var fd *os.File
    var line string
    var bufRd *bufio.Reader
    var keepReading bool = true

    fd, e = os.Open(f)

    if e != nil {
        return nil, e
    }

    defer fd.Close()

    bufRd = bufio.NewReader(fd)

    for keepReading {
        line, e = bufRd.ReadString('\n')
        switch e {
        case nil:
            lines = append(lines, line)
        case os.EOF:
            lines = append(lines, line)
            keepReading = false
        default:
            return lines, e
        }
    }

    return lines, nil
}

// test stuff out..
func main() {

    if len(os.Args) > 1 {

        lines, e := slurp(os.Args[1])

        if e != nil {
            fmt.Fprintf(os.Stderr,"%s\n", e)
            os.Exit(1)
        }

        fmt.Println("\n----- unsorted -----\n")

        for _, line := range lines {
            fmt.Printf("%s", line)
        }

        fmt.Println("\n----- sorted -----\n")

        sort.Strings(lines)

        for _, line := range lines {
            fmt.Printf("%s", line)
        }
    }
}

обратите внимание, что сортировка выполняется на месте, поэтому она ничего не возвращает

person bjarneh    schedule 15.09.2011

Просто интересно, насколько удобно использовать Unix sort для этой цели. Я знаю, что этот код не может работать во многих сценариях развертывания, но я считаю, что стоит упомянуть его как вариант:

package main

import (
    "os"
    "os/exec"
)

func main() {
    file := "file.txt"

    command := []string{"sort", file, "-o", file}

    cmd := exec.Command(command[0], command[1:]...)
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr

    if err := cmd.Run(); err != nil {
        panic(err)
    }
}

Мысли?

person Fernando Á.    schedule 26.05.2015