문자열 배열로 텍스트 파일 읽기 (및 쓰기)


100

문자열 배열 안팎으로 텍스트 파일을 읽고 쓰는 기능은 상당히 일반적인 요구 사항이라고 생각합니다. 또한 처음에 데이터베이스에 액세스 할 필요가없는 언어로 시작할 때 매우 유용합니다. Golang에 존재합니까?
예 :

func ReadLines(sFileName string, iMinLines int) ([]string, bool) {

func WriteLines(saBuff[]string, sFilename string) (bool) { 

중복보다는 기존의 것을 사용하고 싶습니다.


2
사용 bufio.Scanner는 파일에서 행을 읽어 볼 수 stackoverflow.com/a/16615559/1136018golang.org/pkg/bufio
잭 Valmadre

답변:


124

Go1.1 릴리스 부터 파일에서 줄을 쉽게 읽을 수 있는 bufio.Scanner API가 있습니다. Scanner로 재 작성한 위의 다음 예제를 고려하십시오.

package main

import (
    "bufio"
    "fmt"
    "log"
    "os"
)

// readLines reads a whole file into memory
// and returns a slice of its lines.
func readLines(path string) ([]string, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()

    var lines []string
    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        lines = append(lines, scanner.Text())
    }
    return lines, scanner.Err()
}

// writeLines writes the lines to the given file.
func writeLines(lines []string, path string) error {
    file, err := os.Create(path)
    if err != nil {
        return err
    }
    defer file.Close()

    w := bufio.NewWriter(file)
    for _, line := range lines {
        fmt.Fprintln(w, line)
    }
    return w.Flush()
}

func main() {
    lines, err := readLines("foo.in.txt")
    if err != nil {
        log.Fatalf("readLines: %s", err)
    }
    for i, line := range lines {
        fmt.Println(i, line)
    }

    if err := writeLines(lines, "foo.out.txt"); err != nil {
        log.Fatalf("writeLines: %s", err)
    }
}

124

파일이 너무 크지 않으면 다음 ioutil.ReadFilestrings.Split같은 및 함수를 사용하여 수행 할 수 있습니다 .

content, err := ioutil.ReadFile(filename)
if err != nil {
    //Do something
}
lines := strings.Split(string(content), "\n")

ioutilstrings 패키지 에 대한 설명서를 읽을 수 있습니다 .


5
전체 파일을 메모리로 읽어들이므로 파일이 크면 문제가 될 수 있습니다.
jergason 2013 년

22
@Jergason, 그래서 그는 "파일이 너무 크지 않은 경우 ..."로 대답을 시작했습니다.
laurent

9
ioutil은 다음으로 가져올 수 있습니다."io/ioutil"
Pramod

7
참고 strings.Split 은 일반 POSIX 텍스트 파일을 구문 분석 할 때 한 줄 (빈 문자열)을 추가합니다.
bain

1
참고로 Windows에서는 \r. 따라서 \r모든 요소에 추가 할 수 있습니다 .
matfax

32

첫 번째 답변을 업데이트 할 수 없습니다.
어쨌든 Go1 출시 후 몇 가지 주요 변경 사항이 있으므로 아래와 같이 업데이트했습니다.

package main

import (
    "os"
    "bufio"
    "bytes"
    "io"
    "fmt"
    "strings"
)

// Read a whole file into the memory and store it as array of lines
func readLines(path string) (lines []string, err error) {
    var (
        file *os.File
        part []byte
        prefix bool
    )
    if file, err = os.Open(path); err != nil {
        return
    }
    defer file.Close()

    reader := bufio.NewReader(file)
    buffer := bytes.NewBuffer(make([]byte, 0))
    for {
        if part, prefix, err = reader.ReadLine(); err != nil {
            break
        }
        buffer.Write(part)
        if !prefix {
            lines = append(lines, buffer.String())
            buffer.Reset()
        }
    }
    if err == io.EOF {
        err = nil
    }
    return
}

func writeLines(lines []string, path string) (err error) {
    var (
        file *os.File
    )

    if file, err = os.Create(path); err != nil {
        return
    }
    defer file.Close()

    //writer := bufio.NewWriter(file)
    for _,item := range lines {
        //fmt.Println(item)
        _, err := file.WriteString(strings.TrimSpace(item) + "\n"); 
        //file.Write([]byte(item)); 
        if err != nil {
            //fmt.Println("debug")
            fmt.Println(err)
            break
        }
    }
    /*content := strings.Join(lines, "\n")
    _, err = writer.WriteString(content)*/
    return
}

func main() {
    lines, err := readLines("foo.txt")
    if err != nil {
        fmt.Println("Error: %s\n", err)
        return
    }
    for _, line := range lines {
        fmt.Println(line)
    }
    //array := []string{"7.0", "8.5", "9.1"}
    err = writeLines(lines, "foo2.txt")
    fmt.Println(err)
}

18

이를 위해 bufio 패키지 와 함께 os.File ( io.Reader 인터페이스 를 구현 함)을 사용할 수 있습니다 . 그러나 이러한 패키지는 파일 크기에 관계없이 고정 메모리 사용량을 염두에두고 빌드되며 매우 빠릅니다.

불행히도 이것은 전체 파일을 메모리로 읽는 것을 조금 더 복잡하게 만듭니다. 줄 제한을 초과하는 경우 bytes.Buffer 를 사용 하여 줄의 일부를 연결할 수 있습니다 . 어쨌든 프로젝트에서 직접 라인 리더를 사용하는 것이 좋습니다 (특히 텍스트 파일의 크기를 모르는 경우!). 그러나 파일이 작 으면 다음 예제로 충분할 수 있습니다.

package main

import (
    "os"
    "bufio"
    "bytes"
    "fmt"
)

// Read a whole file into the memory and store it as array of lines
func readLines(path string) (lines []string, err os.Error) {
    var (
        file *os.File
        part []byte
        prefix bool
    )
    if file, err = os.Open(path); err != nil {
        return
    }
    reader := bufio.NewReader(file)
    buffer := bytes.NewBuffer(make([]byte, 1024))
    for {
        if part, prefix, err = reader.ReadLine(); err != nil {
            break
        }
        buffer.Write(part)
        if !prefix {
            lines = append(lines, buffer.String())
            buffer.Reset()
        }
    }
    if err == os.EOF {
        err = nil
    }
    return
}

func main() {
    lines, err := readLines("foo.txt")
    if err != nil {
        fmt.Println("Error: %s\n", err)
        return
    }
    for _, line := range lines {
        fmt.Println(line)
    }
}

또 다른 대안은 io.ioutil.ReadAll 을 사용 하여 한 번에 전체 파일을 읽고 나중에 한 줄씩 자르는 것입니다. 파일에 줄을 다시 쓰는 방법에 대한 명시적인 예제를 제공하지는 않지만 기본적으로 os.Create()예제에있는 것과 유사한 루프 가 뒤 따릅니다 (참조 main()).


그 정보에 감사드립니다. 나는 그것이 매우 유용하다고 생각하기 때문에 전체 작업을 수행하기 위해 기존 패키지를 사용하는 데 더 관심이있었습니다. 예를 들어, 처음에 데이터베이스를 사용하지 않고 데이터 지속성으로 Go를 사용하고 싶습니다. 일부 언어에는 이것을 믿습니다. 예. 저는 Ruby가 (메모리에서) 문자열의 배열을 읽는 Readlines를 가지고 있다고 생각합니다. 저는 특히 Ruby 팬이 아닙니다. 내 생각 엔 별거 아니에요. 복제가 싫지만, 그걸 원하는 건 나뿐 일 수도 있습니다. 어쨌든, 나는 그것을 할 패키지를 작성했으며 아마도 github에 넣을 것입니다. 이러한 파일은 일반적으로 매우 작습니다.
brianoh

단순히 모든 종류의 go 구조 (예 : 문자열, 정수, 맵 또는 더 복잡한 구조의 배열)를 유지하려면를 사용하면 gob.Encode()됩니다. 결과는 줄 바꿈으로 구분 된 텍스트 파일 대신 이진 파일입니다. 이 파일은 모든 종류의 데이터를 포함 할 수 있으며 효율적으로 파싱 할 수 있으며 결과 파일은 더 작아지며 이러한 줄 바꿈 및 동적 할당을 처리 할 필요가 없습니다. 따라서 나중에 Go에서 사용하기 위해 무언가를 유지하려는 경우 더 적합 할 것입니다.
tux21b 2011

내가 원하는 것은 모든 줄 (필드)을 변경할 수 있도록 텍스트 줄 배열입니다. 이 파일은 매우 작습니다. 변경이 이루어지면 가변 ​​길이 문자열이 결국 다시 기록됩니다. 내가하고 싶은 일에 매우 유연하고 빠릅니다. 줄 (필드)을 구분하려면 줄 바꿈이 필요합니다. 아마도 더 나은 방법이 있을지 모르지만 이것은 현재 내 목적에 맞는 것처럼 보입니다. 나중에 제안한 내용을 살펴보고 변경해 보겠습니다.
brianoh

2
r58 (2011 년 7 월)부터 인코딩 / 라인 패키지가 제거되었습니다. "이제 기능이 bufio에 있습니다."
kristianp 2011 년

4
func readToDisplayUsingFile1(f *os.File){
    defer f.Close()
    reader := bufio.NewReader(f)
    contents, _ := ioutil.ReadAll(reader)
    lines := strings.Split(string(contents), '\n')
}

또는

func readToDisplayUsingFile1(f *os.File){
    defer f.Close()
    slice := make([]string,0)

    reader := bufio.NewReader(f)

    for{

    str, err := reader.ReadString('\n')
    if err == io.EOF{
        break
    }

        slice = append(slice, str)
    }

1
모두가 "현대적"이라고 계속해서 Go라고 말하려고할수록 35 년 된 베어 최소 라이브러리 바인딩 코드처럼 보입니다. : \ 단순히 라인 기반 텍스트 파일을 읽는 것이 너무 엉망이라는 사실은 Go가 더 일반적인 목적으로 갈 길이 멀다는 사실을 강화할뿐입니다. 다른 언어와 플랫폼에서 여전히 매우 효율적으로 처리되는 텍스트, 줄 기반 데이터가 많이 있습니다. $ .02
ChrisH
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.