go에서 표준출력 및 입력을 하기 위해선 fmt라는 패키지를 사용해야 합니다. 기능에 따라 여러 print이 존재하여 처음 접하면 어느 것을 사용할 지 햇갈립니다. 또한 데이터를 출력 및 받고자 할 때 데이터 타입에 따라 사용해야 하는 포맷이 다르기 때문에 확인을 하고 사용해야 합니다.

fmt.Fprint (OS)

print 앞에 F가 붙으면 파일 입출력에 해당합니다. fprint는 파일에 쓰는 용도입니다.

func Fprint, Fprintf, Fprintln

문법

/* Fprint */
// Fprint가 여러번 호출되도 한 줄로 이어서 파일에 쓰임
fmt.Fprint(파일, 파일에쓰고자 하는 데이터)
 
/* Fprintln */
// Fprintln 여러번 호출될 때마다 마지막 줄에 줄넘김 (CRLF)가 추가됨
fmt.Fprintln(파일, 파일에쓰고자 하는 데이터)
 
/* Fprintf */
// Fprintf가 호출될 때마다 사용자가 추가한 구분자로 구분
fmt.Fprintf(파일, 구분자,파일에쓰고자 하는 데이터)

예제

package main
 
import (
   "os"
   "fmt"
)
 
func main (
 
	file1, _ := os.Create("test.txt")
	defer file1.Close()
	sep := "|"
 
	for _, row := range rows { 
		fmt.Fprint(file1, row)
		fmt.Fprintln(file1, row)
		fmt.Fprintf(file1, sep, row)
	}
)

fmt.Print (표준출력)

일반적으로 많이 사용하는 print에 대한 출력입니다.

func Print, Printf, Println

문법

/* Print */
// Print가 여러번 호출되도 한 줄로 이어서 출력
fmt.Print(출력하고자 하는 데이터)
 
/* Println */
// Println가 여러번 호출되면 다음 줄에 출력
fmt.Println(출력하고자 하는 데이터)
 
/* Printf */
// Printf가 호출될 때마다 사용자가 추가한 포맷에 해당하여 입력된 변수값을 byte로 int타입으로 출력
fmt.Printf(포맷, 출력하고자 하는 데이터)

예제

package main
import (
	"fmt"
)
func main() {
	tmp := "test"
	fmt.Println("1")
	fmt.Println("2")
	fmt.Print("3")
	fmt.Print("4", tmp, "\n")
	fmt.Println("5"+" 34343"+" 1")
	fmt.Printf("%s",tmp)
}
 
// 결과
1
2
34test
5 34343 1
test

fmt.Sprint

사용법은 fmt.Print와 같지만 해당 문법을 사용하면 표준출력을 하진 않습니다. 해당 문법을 사용하는 경우는 출력되는 값을 string 타입으로 return할 때 사용됩니다.

문법

/* Sprint */
// Sprint 리턴할 때 맨 뒤에 \n 없이 리턴되도록 
fmt.Sprint(출력(리턴)하고자 하는 데이터)
 
/* Sprintln */
// Sprintln 리턴할 때 맨 뒤에 \n을 붙여서 리턴되도록
fmt.Sprintln(출력(리턴)하고자 하는 데이터)
 
/* Sprintf */
// Sprintf가 호출될 때마다 사용자가 추가한 포맷에 해당하여 입력된 변수값을 string 타입으로 출력
fmt.Printf(포맷, 출력하고자 하는 데이터)

예제

package main
import (
	"fmt"
)

func test() string {
	tmp := "test()"
	tmp1 := 123
	return fmt.Sprintf("%s, %d", tmp, tmp1)
}

func main() {
	tmp := test()
	fmt.Println(tmp)
}
 
// 결과
test(), 123

Sprint와 Print의 차이점

먼저 리턴되는 타입이 다르다고 볼 수 있습니다. Sprint는 string 타입으로 리턴되며 Print나 Fprint의 경우 바이트를 int 타입으로 리턴합니다.

package main


import (
	"fmt"
	"reflect"
)


func test() string {
	tmp := "test()"
	tmp1 := 123
	return fmt.Sprintf("%s, %d", tmp, tmp1)
}


func main() {
	tmp := test()
	tmp2,_ := fmt.Printf("%s","test(), 123")
	
	fmt.Println(tmp2) 
	fmt.Println(reflect.TypeOf(tmp))
	fmt.Println(reflect.TypeOf(tmp2))
}
 
// 결과
test(), 12311
string
int

Sprintf의 리턴으로 받은 값은 string타입이고 Printf의 리턴으로 받은 값은 int입니다. 또한 특이한 점은 Printf의 값을 받아 다시 표준출력으로 실행하면 입력받은 값과 해당 값에 대한 길이를 같이 보여줍니다.


다음으로 차이점은 표준출력이 되냐 안되냐의 차이입니다. Print는 표준출력으로 가능하기 때문에 보이지만 Sprint의 경우 표준출력이 불가능합니다.

package main
import (
	"fmt"
)
func main() {
	fmt.Sprintln("test")
}


// 결과없음

Sprint는 반드시 string으로 리턴 되어야 하는 값에서만 사용하도록 해야 합니다.

printf 포맷

printf의 경우 받는 인자값에 해당하는 포맷을 사용해야 합니다. 아래 예시와 결과를 각 표시했으니 필요한 부분을 검색해 사용하면 됩니다.

package main
import (
"fmt"
)
type point struct {
x,y int
}
func main() {
p :=point{1,2}
fmt.Printf("%v", p)
// {1 2}
fmt.Printf("%+v\n", p)
// {1 2}{x:1 y:2}
fmt.Printf("%#v\n", p)
// main.point{x:1, y:2}
fmt.Printf("%T\n", p)
// main.point
fmt.Printf("%t\n", true)
// true
fmt.Printf("%d\n", 123)
// 123
fmt.Printf("%b\n", 14)
// 1110
fmt.Printf("%c\n", 33)
// !
fmt.Printf("%x\n", 456)
// 1c8
fmt.Printf("%f\n", 78.9)
// 78.900000
fmt.Printf("%e\n", 123400000.0)
// 1.234000e+08
fmt.Printf("%E\n", 123400000.0)
// 1.234000E+08
fmt.Printf("%s\n", "\"string\"")
// "string"
fmt.Printf("%q\n", "\"string\"")
// "\"string\""
fmt.Printf("%x\n", "hex this")
// 6865782074686973
fmt.Printf("%p\n", &p)
// 0x1040e0f8
fmt.Printf("%d\n",&p)
// &{1 2}
}


'언어 > GO' 카테고리의 다른 글

[GO] Database/sql  (0) 2016.11.26
[GO] Defer  (0) 2016.11.25
[GO] fmt print  (0) 2016.11.11
[GO] 기본 디렉터리 설정  (0) 2016.10.26
[GO] GO언어란?  (1) 2016.10.26
[GO] 한글을 포함한 문자열 길이 자르기  (0) 2016.10.12

설치는 https://golang.org/dl/에서 받으시면 됩니다. 

기본 디렉토리

Go 언어는 모든 부분이 패키지로 구성되어 있고, 인터넷에 있는 소스 코드를 받아와서 바로 사용하기 때문에 기준 디렉터리가 필요합니다.

다음과 같이 company 디렉터리를 예로 들면 company아래에 bin, pkg, src 디렉터리가 들어있습니다.

  • bin: 소스 파일(패키지)를 컴파일하여 실행 파일(바이너리)이 생성되는 디렉터리입니다.
  • pkg: 패키지를 컴파일하여 라이브러리 파일이 생성되는 디렉터리입니다. pkg 디렉터리 아래에는 <운영체제>_<아키텍쳐> 형식으로 디렉터리가 생성됩니다. 64비트 리눅스라면 linux_amd64 디렉터리 아래에 라이브러리 파일이 생성됩니다.
  • src: 내가 작성한 소스 파일과 인터넷에서 자동으로 받아온 소스 파일이 저장되는 디렉터리입니다.

[경로]
┖[프로젝트명]
      ┠bin
      ┠pkg
      ┖src
           ┖hello
                 ┖hello.go 

환경변수

리눅스 및 mac OS

$ vi ~/.bash_profile 
#추가
export GOROOT=/usr/local/go
export PATH=$PATH:/usr/local/go/bin
export GOPATH=/[경로]/[프로젝트명]

$ source ~/.bash_profile

$ go env --결과확인
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/[경로]/[프로젝트명]"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GO15VENDOREXPERIMENT=""
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fno-common"
CXX="clang++"
CGO_ENABLED="1"


또는 
$ vi ~/.bashrc 
#추가
export GOROOT=/usr/local/go
export PATH=$PATH:/usr/local/go/bin
export GOPATH=/home/[경로]/[프로젝트명]

$ source ~/.bashrc

이렇게 설정된 뒤에는 인터넷에서 소스 코드를 받아와서 저장하거나, 패키지를 컴파일하여 라이브러리 파일이 생성될 때 GOPATH에 설정된 경로를 기준으로 합니다. 또 다른 프로젝트를 시작할 때는 GOPATH를 해당 프로젝트 경로로 바꿔주면 됩니다.

'언어 > GO' 카테고리의 다른 글

[GO] Defer  (0) 2016.11.25
[GO] fmt print  (0) 2016.11.11
[GO] 기본 디렉터리 설정  (0) 2016.10.26
[GO] GO언어란?  (1) 2016.10.26
[GO] 한글을 포함한 문자열 길이 자르기  (0) 2016.10.12
[GO] 전각문자(double byte)를 반각문자(single byte)로 변경  (0) 2016.10.12

Go 언어는 빠른 성능, 안정성, 편의성, 쉬운 프로그래밍을 목표로 개발되었으며 범용 프로그래밍 언어입니다.

내용


구글이 2009년에 만든 프로그래밍 언어입니다. 이름이 이름인 만큼 검색이 불편해 보통 golang이라고 많이 이야기합니다. Go 언어의 사용자들은 고퍼(Gopher)라고 부르며, 고퍼들을 위한 연례행사인 고퍼콘(Gophercon)이 세계 각국에서 매년 열리고 있습니다. 전반적으로 C에서 영향을 많이 받았으며, 컨테이너 기반 가상화 도구인 Docker를 개발하는데에 사용된 언어입니다. 또한 Go 언어를 이용해서 안드로이드와 iOS 앱을 제작할 수 있게 적용 중입니다. 또한 Python이 그렇듯 웹 개발에도 쓰이고 있고 특히 서버 사이드 언어로서 좋은 평가를 받고 있습니다. 또한, Go가 자체적으로 지원하는 라이브러리만으로도 간결하게 웹사이트를 만들어낼 수 있습니다.


Go 언어의 특징은 컴파일 언어이지만 컴파일러의 컴파일 속도가 매우 빨라 인터프리터 언어처럼 쓸 수 있다는 점에 있습니다. 이는 언어의 문법 구조를 개선함으로써 달성하였습니다. 컴파일러가 소스 코드를 해석하는 pass 수를 줄여서 달성한 것으로 보입니다. 접근하기 어렵지 않고, 코드 역시 간결하면서도 컴파일 언어답게 높은 성능을 낼 수 있다는 점이 호평을 받습니다. 간결하게 코드를 작성할 수 있으면서도 풍부한 라이브러리 등의 덕택에 막강한 기능을 쉽게 구현할 수 있다는 것은 큰 장점이다.

그러나 컴파일 언어의 태생적인 한계를 극복한 것은 아니라서 대형 모듈을 이것저것 붙이면 컴파일에 필요한 시간이 있기에 Python 등의 인터프리터 언어보다는 기동할 때에 확실히 반응이 늦습니다. 물론 컴파일 언어 중에서는 매우 빠른 편이지만 아무래도 인터프리터 언어의 즉응성까지 바라는 건 무리지만 물론 컴파일 언어인만큼 실행 시 퍼포먼스는 확실합니다.


또한 Go는 GoRoutine이라는 비동기 메커니즘을 제공합니다. 이 비동기 메커니즘은 Erlang에서 영향을 받은 것으로 각각의 고루틴은 병렬로 동작하며 메시지 채널을 통해 값을 주고받습니다. 고루틴을 사용하면 이벤트 처리, 병렬 프로그래밍 등이 간단해집니다. 단 병렬화된 고루틴의 동기화 문제는 프로그래머가 챙겨 줘야 하며 동기화를 무시할 경우 프로그램이 비정상 종료할 수도 있습니다. 예를 들어 부모 루틴이 자식 루틴보다 먼저 끝나버리면 자식 루틴은 OS에 의해 메모리에서 강제로 사출되어 버립니다. 단 동기화 방법은 기존 멀티스레드 응용프로그램에 비해 매우 간단한 편이고 단순히 고루틴으로부터 반환값을 받는 줄을 메인 스레드에 추가하면 됩니다.

고루틴은 멀티스레드 메커니즘이지만 자체적인 스케줄러에 의해 관리되는 경량 스레드이며 OS에서 관리하는 경량스레드보다 더 경량입니다. 따라서 고루틴은 CPU 코어수와 무관하게 수백 수천만 고루틴을 작성해도 성능에 문제가 생기지 않습니다.
다만 컴파일 언어이므로 바이너리만 배포할 경우 해당 타깃 머신에 맞춰서 컴파일해야 합니다. 바이트코드를 생성하는 방식이 아니므로 다중 플랫폼을 지원하려면 소스코드 통째로 배포해야 합니다.

특징


 

GO언어의 특징은 다음과 같습니다.


  • 정적 타입, 강 타입
  • 컴파일 언어
  • 가비지 컬렉션
  • 병행성(Concurrency)
  • 멀티코어 환경 지원
  • 모듈화 및 패키지 시스템
  • 빠른 컴파일 속도

Go 언어의 문법은 C 언어를 기반으로 하고 있으며, C++의 복잡한 문법 대신 간단하고 간결한 문법을 추구하고 있습니다.

정적 타입과 동적 타입


프로그래밍 언어에서 타입은 우리말로 자료형을 뜻합니다. 그리고 자료형에는 보통 정수, 실수, 불, 문자열, 객체 등이 있습니다. 여기서 자료형을 컴파일할 때(Compile-time) 결정하면 정적 타입이고, 실행할 때(Run-time) 결정하면 동적 타입이라 합니다. 요즘 흔히 사용하는 Python, Ruby, JavaScript 등의 스크립트 언어는 동적 타입(Dynamic Typing) 언어입니다. 

# python
 
val = 10 val2 = 'test' # 타입이 없음


반면 C, C++, Java, C# 등의 언어는 정적 타입(Static Typing) 언어입니다. 이 언어들은 값의 형태에 따라 각각 자료형을 가지며 자료형에 맞지 않는 값을 대입하면 컴파일할 때 에러가 발생합니다. 

int val = 10; char val2 = 'a';

약 타입과 강 타입

약 타입(Weakly-typed)은 값의 타입을 바꿀 수 있습니다. 여기서 값의 타입 바꾸기를 형 변환이라고 합니다. 즉 약 타입 언어는 자료형이 달라도 컴파일 또는 실행 시점에 정해진 규칙에 따라 암시적 형 변환(Implicit conversion)을 해주는 방식입니다. 다음과 같이 C 언어는 약 타입 언어이므로 자료형이 달라도 암시적 형 변환을 합니다.

int a = 1; float b =1.3; float c = a + b; // int인 a가 float으로 변경


강 타입(Strongly-typed)은 값 자체가 타입이며, 타입을 바꿀 수 없습니다. 즉 컴파일 또는 실행할 때 자료형이 다르면 에러를 발생시킵니다. 다음과 같이 Go 언어는 강 타입 언어이므로 암시적 형 변환을 하지 않습니다. 또한, 컴파일할 때 타입을 결정하므로 정적 타입 언어입니다.


var a int = 1 var b float32 = 1.3 var c float32 = a + b // int형인 a가 float형으로 변환되지 않아 컴파일 에러발생

가비지 컬렉션

Go 언어는 가비지 컬렉션(Garbage Collection, GC)을 제공합니다. C, C++는 메모리를 할당하면 반드시 해제를 해주어야 합니다. 이렇게 되다 보니 C, C++ 프로그래밍은 로직 작성보다 메모리 관리에 더 많은 노력과 시간을 소모하고 있어서 생산성이 많이 떨어집니다. 이후 메모리를 알아서 관리해주는 가비지 컬렉션 기술이 나왔고, 이 기술을 사용한 Java와 C#이 등장했습니다. 마찬가지로 Python, Ruby, JavaScript 등의 스크립트 언어들도 각 언어의 가상 머신에서 가비지 컬렉션 기술을 사용하고 있습니다.

아래의 그림과 같이 Go 언어는 메모리를 관리해주는 가비지 컬렉터(Garbage Collector)가 실행 파일안에 내장되어 있습니다. 가상 머신 위에서 실행되는 언어들처럼 가상 머신이 메모리 관리를 해주는 것과 차이가 있습니다. 즉 Go 언어는 C, C++ 실행 파일 방식의 간결함과 가상 머신의 가비지 컬렉션 기능을 함께 가지고 있습니다.


Go 언어는 메모리 관리에 신경 쓰지 않고, 로직 작성에 집중할 수 있습니다. 따라서 스크립트 언어(가상 머신 위에서 실행되는 언어)처럼 생산성이 높고 C, C++처럼 빠른 성능이 장점입니다.

병행성


Go 언어는 언어 차원에서 병행성(Concurrency, 동시성)을 제공합니다. 먼저 병행성과 병렬성(Parallelism)의 차이를 알아보겠습니다.


  • 병행성: 동시 처리의 논리적인 개념입니다. 단일 코어에서 스레드를 여러 개 생성하면 겉으로 보기에는 동시에 실행되는 것처럼 보이지만 실제로는 스레드 여러 개가 시간을 쪼개어 순차적(시분할)으로 실행됩니다. 물론 논리적인 개념이기 때문에 단일 코어에서 처리되든 멀티코어에서 처리되든 병행성은 만족합니다.
  • 병렬성: 동시 처리의 물리적인 개념입니다. 작업을 여러 CPU 코어에 나눠서 동시에 처리하는 상태를 뜻합니다.


Go 언어는 go 키워드를 통해 함수 여러 개를 동시에 실행할 수 있습니다. 이렇게 실행된 함수를 고루틴(Goroutine)이라고 하는데 스레드와는 차이점이 있습니다. 스레드는 운영체제의 커널에서 제공하는 리소스이기 때문에 많이 생성할수록 부담이 커집니다. 그림 1-5와 같이 Go 언어는 적정량의 스레드를 생성해서 고루틴을 처리합니다. 또한, 최대 프로세서(코어) 개수 설정에 따라 멀티코어도 지원됩니다(현재 프로세서의 개수를 구할 수 있으며, 일부 프로세서만 사용할 수도 있습니다).

아래 그림과 같이 Go 언어에서는 채널을 이용하여 고루틴끼리 통신을 할 수 있습니다. 즉 간단하게 채널을 통해 데이터를 공유하고 실행 순서를 제어합니다. 지금까지 해오던 멀티스레드 프로그래밍의 동기화 객체와 비슷합니다. Go 언어의 개발자인 롭 파이크는 1980년 대부터 병행성을 지원하는 프로그래밍 언어를 연구해 왔으며 CSP를 따르는 Newsqueak 언어를 개발했고, Go 언어에 채널 개념을 도입했습니다.


모듈화 및 패키지


예전과는 다르게 지금은 소프트웨어의 규모가 매우 크고 복잡해져서 코드의 재사용성이 더욱 중요해졌습니다. 최근에 나온 프로그래밍 언어는 객체지향 개념이 도입되었고, 패키지 시스템까지 지원해주고 있어서 중복 개발이 많이 줄었습니다.


Python pip, Ruby gem, Node.js npm, Java Maven 등의 패키지 시스템은 인터넷의 저장소에서 패키지를 받아오고, 의존성까지 자동으로 관리해줍니다. 마찬가지로 Go 언어는 언어 자체에서 모듈화를 제공하며 인터넷에 있는 소스 코드를 바로 가져와서 사용할 수 있습니다. 그리고 다양한 패키지 관리 도구로 패키지간 의존성을 쉽게 관리할 수 있습니다.


import 키워드로 저장소 주소만 지정한 뒤 go getgo install 명령을 사용하면 Git, Mercurial, Subversion, Bazaar로 GitHub, BitBucket, Launchpad, IBM DevOps Services에서 자동으로 소스 코드를 가져옵니다.

컴파일 속도

C, C++은 컴파일할 때 처리해야 되는 헤더 파일이 많아서 컴파일 속도가 매우 느립니다. 게다가 헤더 파일 간의 의존관계가 매우 복잡하여 헤더 파일이 조금만 수정되어도 컴파일을 다시 하는 문제가 있습니다. 특히나 요즘은 프로그램의 규모가 더 커졌기 때문에 컴파일 시간이 더 길어지고 있습니다.

반면에 Go 언어는 C, C++과는 달리 헤더 파일이 없고, 소스 코드를 패키지화하여 변경된 부분만 컴파일하므로 컴파일 속도가 빠릅니다. 또한, 문법적으로도 복잡한 요소를 최대한 줄여 컴파일 속도에 유리하게 설계했습니다.

언어의 문법도 생산성에 큰 영향을 미치지만 컴파일 속도도 매우 중요합니다. 따라서 Go 언어는 코드를 간결하게 표현할 수 있으면서 컴파일 속도가 빠르므로 다른 컴파일 언어에 비해 생산성이 훨씬 높습니다.

활용범위

Go 언어는 웹 브라우저, 서버, 데이터베이스 등 규모가 크고 복잡한 애플리케이션을 개발하는데 적합합니다. 이러한 분야는 이제 메모리 관리에 시간을 쏟기 보다는 로직에 집중하는 것이 중요해졌습니다. 따라서 메모리를 일일이 신경 쓰지 않아도 되는 Go 언어로 작성하면 생산성을 높일 수 있습니다. 하지만 아래 그림과 같이 Go 언어는 메모리 관리를 철저히 해야 하는 시스템 라이브러리 개발에는 적합하지 않습니다. 그리고 메모리 및 장치에 직접 접근해야 하는 운영체제와 장치 드라이버도 개발하기 힘듭니다.

요약하자면 Go 언어는 메모리 관리가 다소 느슨해도 되고, 규모가 크고 복잡하며 유지보수가 빈번한 곳에서 편리하게 사용할 수 있습니다. 그리고 다양한 네트워크 라이브러리(패키지)를 제공하므로 인터넷 프로그래밍에 유용합니다.

참고


컴파일 언어인 덕분에, 속도가 느린 스크립트 언어에서 연산 퍼포먼스가 필요한 부분을 Go로 대체해 넣을 수도 있습니다. 예를 들면, Go로 만든 코드를 공유 라이브러리로 컴파일해 Ruby에서 FFI를 이용해 컴파일한 .so 파일을 가져와 사용하는 식이며 PHP에서도 역시 가능합니다. 반대로 Go에서 만든 라이브러리를 스크립트에서 사용할 수 있습니다. PHP를 예로 들면, libphp5.so를 이용해 Go에서 PHP 코드를 불러와 실행하는 행동을 할 수 있습니다. 다만 기본 패키지들 중에서는 성능보다는 편의성에 초점을 맞춘 탓에 극한의 성능을 추구하는 경우라면 사용을 권하기 어려운 것들이 있습니다. 예를 들면 웹 서버 제작시에 쓰이는 net/http나 html/template 등이 그러한데, 이런 경우엔 기본 패키지를 대체하는 별도의 패키지를 이용하면 벤치마크상으로는 심지어 수십 배나 수치가 좋아지는(...) 경우도 있습니다. 예를 들어 html/template의 경우 성능 저하의 주범으로 여겨지는 reflect를 사용해서 템플릿을 생성할 때 (코드 작성자 입장에서는 할 일이 줄어서 편리하지만) 자동으로 모두 이스케이프 처리를 해서 렌더링을 하므로 속도가 느려지게 됩니다. 당연하지만 이런 대체 패키지들을 쓰게 되면 대신 코드 작성은 조금 귀찮아지게 되고, 또한 기본 패키지의 문법이 호환되지 않는 경우도 잦습니다. 다만, 실제 서비스를 구성할 때에는 통상적으로 템플릿 캐싱을 추가할 것이므로 느린 파싱 속도는 사실 문제가 되지 않습니다. net/http의 경우에도 Nginx 못지 않은 성능을 보여주는 대체 패키지가 존재한다. 이런 물건들을 사용한 몇몇 프레임워크들은 유독 벤치마크 그래프가 하늘을 뚫을 기세로 눈에 띕니다(...).


Go 언어의 설계 지향점이 시스템 프로그래밍 언어이었으나, 가비지 컬렉션의 지원이나 제네릭의 부재로 인해 불필요하게 많이 사용되는 박싱/언박싱 등으로 인해 C/C++을 대체할 수 있는 언어는 아니라는 것에 합의가 이루어지고 있는 상황입니다. 실제로 고성능 연산에 사용하기에는 C/C++에 비해 너무 느리며, 저수준 시스템 개발에서는 가비지 컬렉션과 고루틴을 지원하기 위한 무거운 런타임 등으로 인해 사용이 불가능에 가깝습니다. 그런 이유로 대체로 개발 속도와 실행 속도, 병행성 사이의 적정 지점이 필요한 서버 애플리케이션 개발 등에 많이 사용되는 편입니다.

  1. 2018.10.23 11:20

    비밀댓글입니다

한글 단어 길이를 제한을 두어 제한치를 넘어가는 글자를 자르는 함수를 설명하겠습니다.

링크 에서 설명한 것과 같이 GO에서 len()함수는 byte기반으로 동작하기 때문에 한글은 3바이트 씩 계산됩니다.

아래와 같이 한글+숫자+영어가 섞여있는 문자열에서 글자 제한을 20자로 두어 그 이상 넘어가면 삭제하는 함수를 설명하겠습니다. 

func main() {
vals := "가나다라마바사1234567890abcd"
}


먼저 한글을 바이트로 변환시키면 다음과 같은 리스트를 볼 수 있습니다.

func main() {
vals := "가나다"
	b := []byte(vals)
fmt.Println(b)
	
}

# [234 176 128 235 130 152 235 139 164]

하나의 숫자 당 1byte로 한글은 3바이트이기 때문에 3개씩 묶어 한 글자를 표현해 줍니다. 만일 자르고자 하는 문자열이 한글이라는 확신이 있으면 직접 함수를 만들어 사용해도 됩니다.

확신이 없다면 아래처럼 사용합니다.


import (
"fmt"
"unicode/utf8"
)
func stringSplit(str string) string {
b := []byte(str)
idx := 0
for i := 0; i < 20; i++ {
_, size := utf8.DecodeRune(b[idx:])
idx += size
}
return str[:idx]
}
func main() {
vals := "가나다라마바사1234567890abcd"
var result string

result = stringSplit(vals)
fmt.Println(result)

}
# 가나다라마바사1234567890abc

먼저 문자열을 바이트로 변환한 다음 utf8.DecodeRune() 라이브러리에 바이트의 첫 글자를 입력합니다. 3개의 데이터를 하나로 묶어 한글 하나를 표현하는데 utf8.DecodeRune()에 가장 맨 처음 보여지는 바이트를 입력하면 함수내에서 연관되는 바이트까지 계산을 하게 됩니다. 그리고 출력값으로 변환된 바이트의 길이와 크기??(까먹음)를 리턴합니다.

이러한 방식으로 원하는 글자수 만큼 한글+숫자+영어 문자열을 계산할 수 있습니다.




//전각문자(특수문자 등)과 같은 기호들을 일반 문자(반각문자)로 변경
func convertHalfwidth(str string) string {
b := []byte(str)
idx := 0
var buffer bytes.Buffer
dataLength := utf8.RuneCountInString(str)

for i := 0; i < dataLength; i++ {
val, size := utf8.DecodeRune(b[idx:])

//문자가 전각문자일 경우 아래만큼 뺴서 실행
//전각문자와 반각문자는 아스키코드로 120만큼 차이나며 16진수로는 80만큼 차이가
if val >= 0xfee0 {
val = val - 0xfee0
//특수기호 공백은 아래와 같이 다른 코드값을 가지므로 하드코딩
} else if val == 0x3000 {
val = 0x20
}
idx += size
buffer.WriteString(string(val))
}

return strings.ToUpper(strings.TrimSpace(buffer.String()))

}


위 기본 코드는 GO뿐만 아니라 전 언어에서 사용이 가능함 (물론 형태는 조금씩 바뀌겠지만)

슬라이스는 배열과 달리 크기가 동적이고 길이가 고정되어 있지 않아 아래의 방식이 가능하지만 배열의 경우 에러가 납니다.

2개 이상의 데이터가 슬라이스에 존재한다고 가정하고 중간 데이터를 삭제를 하면 슬라이스의 중간에 빈 값이 존재하게 됩니다. 이러한 경우에 삭제한 데이터 뒤에 존재하는 데이터들을 앞으로 시프트 하는 방법을 아래 예시를 통해 설명하겠습니다.

예) [32, 29, 78, 16, 81]의 데이터를 가지는 슬라이스에서 78번을 삭제한 다음, [16, 81]을 앞으로 한 칸씩 당겨야 하는 경우

1. [32, 29, 78, 16, 81] - 78 삭제

 2. [32, 29, null, 16, 81] - 78자리에 빈 값존재

3. [32, 29 16, 81] - 이와 같이 삭제된 인덱스 다음 인덱스들의 자리를 한 칸씩 당겨야 함

import "fmt"
func main() {
	items := []int{32, 29, 78, 16, 81}
	items = items[:2 + copy(items[2:], items[2 + 1:])]
    	fmt.Println(items)
}
// [32, 29, 16, 81]


+ Random Posts