Connecting

Go 언어 파일 입출력 / 정규 표현식 / json 본문

Go 언어

Go 언어 파일 입출력 / 정규 표현식 / json

팬도라 2020. 7. 23. 16:12
반응형

파일 입출력

거의 모든 프로그래밍 언어에서 데이터를 읽기/쓰기 작업 기능이 필요합니다. Go 언어 역시 이러한 기능을 제공하며, 다음 예제를 통해서 확인해 보겠습니다.

package main

import (
    f "fmt"
    "io/ioutil"
    "os"
)

func main() {

    CurrentDirectory()
    data, err := ioutil.ReadFile("hello.txt")
    check(err)
    f.Print(string(data))
}

func check(e error) {
    if e != nil {
        f.Println("파일을 읽을 수 없습니다.")
        f.Println("Error Code : ", e)
    }
}

func CurrentDirectory() {
    path, _ := os.Getwd()
    f.Println(path)
}

CurrentDirectory 함수에서는 현재 디렉터리에 위치를 알려주고 check 함수를 통해 파일의 존재 여부에 대한 기본적인 에러처리를 담당합니다. 이러한 조건이 충족되면 ioutil.ReadFile 패키지를 통해서 "hello.txt" 파일을 읽어서 string으로 출력합니다.

다음 예제를 통해서 Go 언어의 OS 패키지에서 제공하는 파일 작성하는 다른 방법을 살펴보겠습니다.

package main

import (
    "bufio"
    f "fmt"
    "os"
)

func main() {

    file, err := os.OpenFile(
        "hello.txt",
        os.O_CREATE|os.O_RDWR|os.O_TRUNC, // 파일이 존재하지 않으면 생성하고, 읽기/쓰기 모드로 파일을 만들고 / 파일이 존재하면 해당 내용을 삭제합니다. 
        os.FileMode(0644)) // 파일 권한 설정

    if err != nil {
        f.Println(err)
        return
    }
    defer file.Close() // main 함수가 끝나기 전에 파일을 닫습니다. 

    w := bufio.NewWriter(file)
    w.WriteString("hello, wisoft")
    w.Flush()

    r := bufio.NewReader(file)
    fi, _ := file.Stat()
    b := make([]byte, fi.Size())

    file.Seek(0, os.SEEK_SET)
    r.Read(b)

    f.Println(string(b))
}

이번에는 bufio 패키지를 사용해서 파일 읽기 / 쓰기를 구현해 보도록 하겠습니다.

package main

import (
    "bufio"
    f "fmt"
    "os"
)

func main() {

    file, err := os.OpenFile(
        "hello2.txt",
        os.O_CREATE|os.O_RDWR|os.O_TRUNC,
        os.FileMode(0644))

    if err != nil {
        f.Println(err)
        return
    }
    defer file.Close()

    r := bufio.NewReader(file)
    w := bufio.NewWriter(file)

    rw := bufio.NewReadWriter(r, w)

    rw.WriteString("Hello, World")
    rw.Flush()

    file.Seek(0, os.SEEK_SET)
    data, _, _ := rw.ReadLine()
    f.Println(string(data))

}

Go 언어에서는 다양한 패키지를 통해서 파일 읽기 / 쓰기를 구현할 수 있습니다. 위에서 작성한 것과 같이 똑같은 일을 처리하지만 다른 패키지를 사용함에 따라서 그 특성이 조금식 달라지게 되는데 어떠한 부분이 달라지는지 확인해 보도록 하겠습니다.

다음 설명은 https://xoit.tistory.com/3의 내용을 차용했음을 알립니다.

  • package "os"
    • os는 low level에서 운영체제 기능에 대한 플랫폼과는 독립적인 인터페이스를 제공하는 패키지이다. Unix와 비슷한 디자인이지만, 에러 처리는 Go와 비슷하다. os.File 타입은 디스크 위에 파일이나 바이트를 스트리밍하는 io.Reader, io.Writer를 구현할 때 사용된다.
  • package "io"
    • io는 I/O primitive(가장 기본 단위)의 기본 인터페이스를 제공하는 패키지이다. 바이트 스트림(io.Reader, io.Writer, etc..)을 처리하는 인터페이스 뿐만 아니라 io.Copy(파일 복사), io.ReadFull(n byte 읽기)와 같은 함수를 제공한다.
  • package "io/util"
    • io/ioutil는 I/O 유틸리티 함수를 구현한 패키지이다. io.ReadFile은 전체 파일을 메모리로 읽어들여 빠른 읽기를 수행할 수 있다. 그래서 큰 파일을 읽을 때는 사용하지 않는 것이 좋다. 정확한 파일 크기의 byte slice([]byte)를 할당하며, 자동으로 파일을 닫는다.
  • package "bufio"
    • bufio는 효율 향상을 위해 입출력을 버퍼링하는 io.Reader, io.Writer를 래핑하는 타입을 제공한다. 행, 단어 단위로 읽기를 수행할때 사용한다. bufio.Scanner는 독립적인 행(line)을 효율적으로 읽는 데 유용한 타입이다. Scanner의 Scan()함수는 미리 정의된 개행문자 혹은 사용자가 정의한 delimeter(Split()사용)를 만날 때까지를 한 줄로(행, line) 판단하여 데이터를 한줄씩 읽는다.

정규표현식

정규표현식은 특정한 규칙을 가진 문자열의 집합을 표현하는데 사용하는 형식 언어입니다. 주로 Programming Language나 Text Editor 등 에서 문자열의 검색과 치환을 위한 용도로 쓰이며, 입력한 문자열에서 특정한 조건을 표현할 경우 일반적인 조건문으로는 다소 복잡할 수도 있지만, 정규표현식을 이용하면 매우 간단하게 표현 할 수 있습니다.

하지만 코드가 간단한 만큼 가독성이 떨어져서 표현식을 숙지하지 않으면 이해하기 힘들다는 문제점이 있습니다.

이번 Go 언어 시간에서는 정규표현식에 대한 자세한 설명을 하지는 않을 것이기 때문에, 자주 사용하게 될 정규표현식을 코드 예제를 작성하고 실행하여 살펴보도록 하겠습니다.

package main

import (
    "bytes"
    f "fmt"
    "regexp"
)

func main() {

    match, _ := regexp.MatchString("p([a-z]+)ch", "peach")
    f.Println(match)
    r, _ := regexp.Compile("p([a-z]+)ch")

    f.Println(r.MatchString("peach"))
    f.Println(r.FindString("peach punch"))
    f.Println(r.FindStringIndex("peach punch"))
    f.Println(r.FindStringSubmatch("peach punch"))
    f.Println(r.FindStringSubmatchIndex("peach punch"))
    f.Println(r.FindAllString("peach punch", -1))

    f.Println(r.FindAllStringSubmatchIndex("peach punch pinch", -1))
    f.Println(r.FindAllString("peach punch pinch", 2))
    f.Println(r.Match([]byte("peach")))

    r = regexp.MustCompile("p([a-z]+)ch")
    f.Println(r)

    f.Println(r.ReplaceAllString("a peach", "<fruit>"))

    in := []byte("a peach")
    out := r.ReplaceAllFunc(in, bytes.ToUpper)
    f.Println(string(out))

}

JSON

JavaScript Object Notation (JSON)은 Javascript 객체 문법으로 구조화된 데이터를 표현하기 위한 문자 기반의 표준 포맷입니다. 웹 어플리케이션에서 데이터를 전송할 때 일반적으로 사용합니다. json 문법은 다른 프로그래밍 언어를 해보았다면 어느정도 익숙해져 있다고 가정하고 자세한 설명은 넘어가도록 하겠습니다. Json 자체에 대해 궁금하다면 아래 링크를 확인하시길 바랍니다.

https://ko.wikipedia.org/wiki/JSON

다음 예제를 확인하여 간단하게 json을 만들어 보도록 하겠습니다.

package main

import (
    "encoding/json"
    f "fmt"
)

func main() {

    doc := `{"name": "lucas", "age": "27"}`

    var data map[string]interface{} // JSON 패키지가 디코딩 데이터를 저장할 수 있는 변수를 선언, 임의의 데이터 타입의 문자열 맵을 가짐

    json.Unmarshal([]byte(doc), &data)
    f.Println(data["name"], data["age"])

    f.Println("------------------")
    json2()

}

func json2() {

    data := make(map[string]interface{})

    data["name"] = "K"
    data["age"] = 10

    doc, _ := json.Marshal(data)
    f.Println(string(doc))
}

여기서 Marshal과 Unmarshal이라는 함수를 호출한 것을 확인할 수 있습니다. 소프트웨어는 바이트 단위로 데이터를 인식하는데, 모든 바이트는 해석하는 도구에 따라 달라집니다. 이렇게 변환하고 해석하는 것을 우리는 인코딩 혹은 마샬링이라고 부르게 됩니다. Go 언어에서 encoding 패키지가 이를 담당하게 되고, 실제로는 인터페이스만 정의되어 있고 사용되는 데이터 형태에 따라 서브패키지로 기능이 구성됩니다.

‘마샬링(Marshaling)’ 혹은 ‘인코딩(Encoding)’은 논리적 구조를 로우 바이트로 변환하는 것으로 Go value를 바이트 슬라이스로 변경하는 것을 말합니다.

func Marshal(v interface{}) ([]byte, error) 

이와 반대로 바이트 슬라이스나 문자열을 논리적 자료 구조로 변경하는 것을 언마샬링(Unmashaling)이라고 합니다.

func Unmarshal(data []byte, v interface{}) error 

따라서 첫번째 인자로 바이트 슬라이스를 넘겨주고, 두번째로 결과를 담게될 변수 포인터를 넘겨주는 구조로 작성해야 합니다.

다음 예제를 통해 구조체를 통한 json을 생성해 보도록 하겠습니다.

package main

import (
    "encoding/json"
    f "fmt"
)

type Author struct {
    Name  string
    Email string
}

type Comments struct {
    Id      uint64
    Author  Author
    Content string
}

type Article struct {
    Id         uint64
    Title      string
    Author     Author
    Content    string
    Recommends []string
    Comments   []Comments
}

func main() {

    data := make([]Article, 1) // 값을 저장할 구조체 슬라이스

    data[0].Id = 1
    data[0].Title = "Hello, World"
    data[0].Author.Name = "lucas"
    data[0].Author.Email = "lucas@example.com"
    data[0].Content = "Good"
    data[0].Recommends = []string{"J", "K"}

    data[0].Comments = make([]Comments, 1)
    data[0].Comments[0].Id = 1
    data[0].Comments[0].Author.Name = "Keven"
    data[0].Comments[0].Author.Email = "keven@example.com"
    data[0].Comments[0].Content = "Nice keven"

    doc, _ := json.Marshal(data)
    f.Println(string(doc))

}
Comments