Go를 사용하여 파일을 읽고 쓰는 방법?


284

Go를 스스로 배우려고 노력했지만 일반 파일을 읽고 쓰는 데 어려움을 겪었습니다.

나는까지 얻을 수 inFile, _ := os.Open(INFILE, 0, 0)있지만 실제로는 파일의 내용을 얻는 것이 의미가 없습니다. 읽기 함수는 []byte매개 변수로 사용 하기 때문 입니다.

func (file *File) Read(b []byte) (n int, err Error)

답변:


476

Go에서 파일을 읽고 쓰는 모든 방법의 Go 1 호환 목록을 만들어 봅시다.

파일 API가 최근 변경되었으며 대부분의 다른 답변은 Go 1에서 작동하지 않기 때문에 bufio중요한 IMHO가 누락되었습니다 .

다음 예제에서는 파일을 읽고 대상 파일에 기록하여 파일을 복사합니다.

기본으로 시작

package main

import (
    "io"
    "os"
)

func main() {
    // open input file
    fi, err := os.Open("input.txt")
    if err != nil {
        panic(err)
    }
    // close fi on exit and check for its returned error
    defer func() {
        if err := fi.Close(); err != nil {
            panic(err)
        }
    }()

    // open output file
    fo, err := os.Create("output.txt")
    if err != nil {
        panic(err)
    }
    // close fo on exit and check for its returned error
    defer func() {
        if err := fo.Close(); err != nil {
            panic(err)
        }
    }()

    // make a buffer to keep chunks that are read
    buf := make([]byte, 1024)
    for {
        // read a chunk
        n, err := fi.Read(buf)
        if err != nil && err != io.EOF {
            panic(err)
        }
        if n == 0 {
            break
        }

        // write a chunk
        if _, err := fo.Write(buf[:n]); err != nil {
            panic(err)
        }
    }
}

여기 내가 사용 os.Open하고 os.Create있는 편리한 래퍼 주위 os.OpenFile. 우리는 일반적으로 OpenFile직접 전화 할 필요가 없습니다 .

EOF 취급 통지. 각 호출 Read을 채우려 고 시도하고 파일 끝에 도달하면 오류로 buf반환합니다 io.EOF. 이 경우 buf여전히 데이터를 보유합니다. 결과적인 호출 Read은 읽은 바이트 수로 0 을 리턴 io.EOF하며 오류 와 동일 합니다. 다른 오류가 있으면 공황 상태가됩니다.

사용 bufio

package main

import (
    "bufio"
    "io"
    "os"
)

func main() {
    // open input file
    fi, err := os.Open("input.txt")
    if err != nil {
        panic(err)
    }
    // close fi on exit and check for its returned error
    defer func() {
        if err := fi.Close(); err != nil {
            panic(err)
        }
    }()
    // make a read buffer
    r := bufio.NewReader(fi)

    // open output file
    fo, err := os.Create("output.txt")
    if err != nil {
        panic(err)
    }
    // close fo on exit and check for its returned error
    defer func() {
        if err := fo.Close(); err != nil {
            panic(err)
        }
    }()
    // make a write buffer
    w := bufio.NewWriter(fo)

    // make a buffer to keep chunks that are read
    buf := make([]byte, 1024)
    for {
        // read a chunk
        n, err := r.Read(buf)
        if err != nil && err != io.EOF {
            panic(err)
        }
        if n == 0 {
            break
        }

        // write a chunk
        if _, err := w.Write(buf[:n]); err != nil {
            panic(err)
        }
    }

    if err = w.Flush(); err != nil {
        panic(err)
    }
}

bufio데이터와 관련이 많지 않기 때문에 여기서는 버퍼 역할 만합니다. 대부분의 다른 상황 (특히 텍스트 파일) bufio은 장면 뒤의 버퍼링을 처리하는 동안 쉽고 유연하게 읽고 쓸 수 있는 멋진 API 를 제공함으로써 매우 유용 합니다.

사용 ioutil

package main

import (
    "io/ioutil"
)

func main() {
    // read the whole file at once
    b, err := ioutil.ReadFile("input.txt")
    if err != nil {
        panic(err)
    }

    // write the whole body at once
    err = ioutil.WriteFile("output.txt", b, 0644)
    if err != nil {
        panic(err)
    }
}

파이처럼 쉬워요! 그러나 큰 파일을 다루지 않는 것이 확실한 경우에만 사용하십시오.


55
이 질문을 우연히 발견 한 사람은 2009 년 에이 라이브러리가 소개되기 전에 요청 되었으므로이 대답을 가이드로 사용하십시오!
Seth Hoenig 2:17에

1
golang.org/pkg/os/#File.Write 에 따르면 Write가 모든 바이트를 쓰지 않은 경우 오류를 반환합니다. 따라서 첫 번째 예 ( panic("error in writing")) 의 추가 검사 는 필요하지 않습니다.
ayke

15
이 예제는 fo.Close ()의 오류 리턴을 확인하지 않습니다. Linux 매뉴얼 페이지에서 close (2) : close ()의 반환 값을 확인하지 않는 것이 일반적이지만 심각한 프로그래밍 오류입니다. 이전 write (2) 작업의 오류가 최종 close ()에 먼저보고 될 수 있습니다. 파일을 닫을 때 반환 값을 확인하지 않으면 데이터가 자동으로 손실 될 수 있습니다. 이것은 NFS 및 디스크 할당량에서 특히 관찰 할 수 있습니다.
Nick Craig-Wood

12
그렇다면 "큰"파일은 무엇입니까? 1KB? 1MB? 1GB? 아니면 "큰"기계의 하드웨어에 따라 달라 집니까?
425nesp December

3
@ 425nesp 파일 전체를 메모리로 읽어들이므로 실행중인 시스템에서 사용 가능한 메모리 양에 따라 다릅니다.
Mostafa

49

이것은 좋은 버전입니다.

package main

import (
  "io/ioutil"; 
  )


func main() {
  contents,_ := ioutil.ReadFile("plikTekstowy.txt")
  println(string(contents))
  ioutil.WriteFile("filename", contents, 0644)
}

8
전체 파일을 메모리에 저장합니다. 파일이 클 수 있으므로 항상 원하는 것은 아닙니다.
user7610

9
또한 0x777가짜입니다. 어쨌든 더 비슷 0644하거나 0755(16 진수가 아닌 8 진수) 여야합니다 .
cnst

@cnst는 0x777에서 0644로 변경
Trenton

31

사용 io.Copy

package main

import (
    "io"
    "log"
    "os"
)

func main () {
    // open files r and w
    r, err := os.Open("input.txt")
    if err != nil {
        panic(err)
    }
    defer r.Close()

    w, err := os.Create("output.txt")
    if err != nil {
        panic(err)
    }
    defer w.Close()

    // do the actual work
    n, err := io.Copy(w, r)
    if err != nil {
        panic(err)
    }
    log.Printf("Copied %v bytes\n", n)
}

당신은 바퀴를 개혁 기분하지 않는 경우, io.Copy그리고 io.CopyN잘 서비스를 제공 할 수 있습니다. io.Copy 함수 의 소스확인 하면 Go 라이브러리에 패키지 된 Mostafa 솔루션 (실제로는 '기본'솔루션) 중 하나 일뿐입니다. 그러나 그는 자신보다 훨씬 큰 버퍼를 사용하고 있습니다.


5
언급 한 것은 가치 - 파일의 내용이 디스크에 기록 된 것을 확인하기 위해, 당신은 사용할 필요가 w.Sync()애프터io.Copy(w, r)
셰이 Tsadok

또한 기존 파일에 io.Copy()쓰면 공급하는 데이터 만 쓰므로 기존 파일에 더 많은 내용이 있으면 제거되지 않으므로 파일이 손상 될 수 있습니다.
Invidian

1
@Invidian 대상 파일을 여는 방법에 따라 다릅니다. 당신이 경우 w, err := os.Create("output.txt")"명명 된 파일을 만들거나 자릅니다 만듭니다. 파일이 이미 존재하는 경우가립니다."때문에, 당신은 무엇을 설명, 발생하지 않습니다 golang.org/pkg/os/#Create .
user7610

파일을 읽기 전에 한 번에 전체 파일을 읽을 필요없이 휠을 다시 발명하지 않으므로 정답이되어야합니다.
Eli Davis

11

최신 Go 버전을 사용하면 파일을 쉽게 읽고 쓸 수 있습니다. 파일에서 읽으려면 :

package main

import (
    "fmt"
    "io/ioutil"
)

func main() {
    data, err := ioutil.ReadFile("text.txt")
    if err != nil {
        return
    }
    fmt.Println(string(data))
}

파일에 쓰려면 :

package main

import "os"

func main() {
    file, err := os.Create("text.txt")
    if err != nil {
        return
    }
    defer file.Close()

    file.WriteString("test\nhello")
}

파일의 내용을 덮어 씁니다 (없는 경우 새 파일을 만듭니다).


10

[]byte바이트 배열의 일부 또는 전부의 슬라이스 (하위 문자열과 유사)입니다. 당신이 사용하여 액세스 할 수 조각의 길이 및 용량에 대한 찾습니다 시스템 및 배열 (슬라이스)의 모든 액세스 또는 일부를 더한 필드에 숨겨진 포인터 필드 값의 구조와 조각의 생각 len()cap()기능을 .

바이너리 파일을 읽고 인쇄하는 스타터 키트는 다음과 같습니다. inName시스템의 작은 파일을 참조 하려면 리터럴 값 을 변경 해야합니다.

package main
import (
    "fmt";
    "os";
)
func main()
{
    inName := "file-rw.bin";
    inPerm :=  0666;
    inFile, inErr := os.Open(inName, os.O_RDONLY, inPerm);
    if inErr == nil {
        inBufLen := 16;
        inBuf := make([]byte, inBufLen);
        n, inErr := inFile.Read(inBuf);
        for inErr == nil {
            fmt.Println(n, inBuf[0:n]);
            n, inErr = inFile.Read(inBuf);
        }
    }
    inErr = inFile.Close();
}

9
이동 규칙은 첫 번째 오류를 확인하고, 정상 코드 상주의 외부에게 수 있도록하는 것입니다 if블록
하센

@Jurily : 오류가 발생했을 때 파일이 열려 있으면 어떻게 닫습니까?
peterSO 2009

10
@peterSO : 사용 연기
제임스 Antill

그러나 왜 [256] 바이트가 받아 들여지지 않고 부풀 리면서도 명백하고 어리 석고 장황한 (그러나 분명히 잘못되지는 않았 음) inBuf : = make ([] byte, 256)?
카디프 우주 남자

7

이 시도:

package main

import (
  "io"; 
  )


func main() {
  contents,_ := io.ReadFile("filename");
  println(string(contents));
  io.WriteFile("filename", contents, 0644);
}

1
전체 파일을 한 번에 읽으려는 경우 작동합니다. 파일이 실제로 크거나 일부만 읽으려면 원하는 것이 아닐 수도 있습니다.
Evan Shaw

3
실제로 오류 코드를 확인해야하며 그렇게 무시해서는 안됩니다!
하센

7
이것은 ioutil 패키지로 옮겨졌습니다. ioutil.ReadFile ()입니다.
Christopher

나는 그것이 0644라고 말하기 위해 고쳤습니다
Joakim

1

문서를 보면 그냥 [] byte 유형의 버퍼를 선언하고 읽은 다음 전달하여 많은 문자를 읽고 실제로 읽은 문자 수를 반환해야합니다 (오류).

문서는 말합니다

읽기는 파일에서 len (b) 바이트까지 읽습니다. 읽은 바이트 수와 오류 (있는 경우)를 반환합니다. EOF는 err가 EOF로 설정된 0 카운트로 신호됩니다.

작동하지 않습니까?

편집 : 또한 os 를 사용하는 대신 bufio 패키지에 선언 된 Reader / Writer 인터페이스를 사용해야한다고 생각합니다. 패키지 합니다.


Go에 익숙한 사람들이 이미 익숙한 기능에 대한 문서를 읽을 때 문서를 읽을 때 파문을받는 대신 실제 사람들이 문서를 읽을 때 볼 수있는 내용을 실제로 인정하기 때문에 내 투표가 있습니다.
카디프 우주 남자

1

Read 메서드는 바이트 매개 변수를 사용합니다.이 매개 변수는 읽을 버퍼이기 때문입니다. 일부 서클에서는 일반적인 관용구이며 생각할 때 의미가 있습니다.

이 방법으로 독자가 읽을 바이트 수를 판별하고 실제로 읽은 바이트 수를 확인하고 오류를 적절하게 처리하기 위해 리턴을 검사 할 수 있습니다.

다른 사람들이 그들의 대답에서 지적했듯이 bufio는 아마도 대부분의 파일에서 읽으려는 것일 것입니다.

정말 유용하기 때문에 다른 힌트를 하나 추가하겠습니다. 파일에서 줄을 읽는 것은 ReadLine 메서드가 아니라 ReadBytes 또는 ReadString 메서드를 사용하는 것이 가장 좋습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.