文字列(string型) 文字列は
"" ダブルクオートで囲む
package main
func main() {
s := "hello world"
println(s) // hello world
}
エスケープシーケンス
\" ダブルクオート
\n 改行(line feed)
\r 復帰(carriage return)
\t 水平タブ
\v 垂直タブ
文字列は "" で囲むので " を文字列に含めることはできません。
なので \ (バックスラッシュ) に続けて
" を記述することでそれを使うことができます。
その他にも \n で改行、 \t で水平タブなどを表現します。
package main
func main() {
var s string
// "" を出力する
s = "\"hello\""
println(s)
// "hello"
// 改行
s = "hello\nworld"
println(s)
// hello
// world
// 水平タブ
s = "\thello"
println(s)
//
hello
}
文字数の取得
package main
func main() {
s := "hello world"
length := len(s)
println(length) // 11 }
文字列から一文字取得 文字列(string型)は、バイトスライス([]byte)としてスライスと同様にインデックス番号(添え字)で文字(byte)が取得できる
package main
func main() {
s := "hello world"
b := s[0]
println(string(b)) // h }
文字列の切り取り これもスライスと同じで [start:end] で抜き出すことができる。
package main
func main() {
s := "hello world"
b := s[0:5]
println(string(b)) // hello }
連結演算子(文字列の連結) + を使って文字列を連結できる
package main
func main() {
a := "Hello" + "World"
println(a) // HelloWorld
// 変数でも同じ
b := "Hello"
c := "World"
d := b + c
println(d) // HelloWorld
// 片方が変数でも同じ
e := "Hello"
f := e + "World"
println(f) // HelloWorld
g := "Hello" // += で g = g + "World" と同じ意味になる
g += "World"
println(g) // HelloWorld }
ヒアドキュメント "" の代わりに `` (バッククォート)で囲むことで、そのまま複数行の文字列を書くことができます。 エスケープシーケンスはそのまま表示されるので、以下のように \n\r\t\v がそのまま出力されます。
package main
func main() {
s := `foo\n\r\t\v
bar
baz`
println(s)
// foo\n\r\t\v
//
bar
// baz
}
シングルバイト文字(byte)とマルチバイト文字(rune)
byte (uint8 の別名)。8ビット = 1バイト ジングルバイト文字
e-words: シングルバイト文字
rune (int32 の別名)。1バイト以上なのでマルチバイト文字(日本語など)
e-words: マルチバイト文字
package main
import
"fmt"
func main() {
s := "こんにちは"
// 5文字なのに 15 になる
fmt.Println(len(s)) // 15
// マルチバイト文字は正しく出力されない
fmt.Println(string(s[0])) // ã
// []byteにすると1バイトごとにスライスになる。
// 1バイトごとのスライスを数えるので上の len(s) は 15 になるし、
//
s[0] での抜き出しも正しく出力されない
fmt.Println([]byte(s))
// [227 129 147 227 130 147 227 129 171 227
129 161 227 129 175]
// []runeにすると文字ごとにスライスになる
fmt.Println([]rune(s))
// [12371 12435 12395 12385 12399] }
マルチバイトの文字数取得 unicode/utf8パッケージ
package main
import
"unicode/utf8"
func main() {
s := "こんにちは"
// []rune にしてから長さを数える
r := []rune(s)
println(len(r)) // 5
// runeの長さを数える関数を使う
println(utf8.RuneCountInString(s)) // 5
}
マルチバイトの文字を取得 一度 []rune に変換する
package main
func main() {
s := "hello こんにちは"
r := []rune(s)
println(string(r[6])) // こ
println(string(r[7])) // ん
println(string(r[8])) // に
println(string(r[9])) // ち
println(string(r[10])) // は
}
マルチバイト文字の切り取り
package main
import
"strings"
func main() {
s := "hello こんにちは"
// 一度 []rune にして抜き出す
r := []rune(s)
println(string(r[6:len(s)])) // こんにちは
// strings パッケージの Index 関数を使えば開始位置がわかる
target := "こんにちは"
if i := strings.Index(s, target); i != -1 {
println(s[i : i+len(target)]) // こんにちは
} }
文字列のループ for 文は、[]byte をインデックス番号で取得するので byte/uint8 になる。なので、これもマルチバイト文字は正確に扱うことができない。
package main
import (
"reflect"
)
func main() {
s := "hello"
for i := 0; i < len(s); i++ {
kind := reflect.TypeOf(s[i]).String()
println(kind) // uint8
}
}
対して、for...range のは、変数に rune/int32 がセットされる。 []rune としてループする
package main
import (
"reflect"
)
func main() {
s := "hello"
for _, v := range s {
kind := reflect.TypeOf(v).String()
println(kind) // int32
}
}
シングルバイト文字とマルチバイト文字の判別
package main
import (
"fmt"
"unicode/utf8"
)
func main() {
s := "hello こんにちは 12345"
for _, v := range s {
if v < utf8.RuneSelf {
fmt.Println("Singlebyte
char:", string(v))
} else {
fmt.Println("Multibyte char
:", string(v))
}
}
}
文字列に関する便利な標準パッケージ
stringsパッケージ
https://golang.org/pkg/strings/
fmtパッケージ
https://golang.org/pkg/fmt/
strconvパッケージ
https://golang.org/pkg/strconv/
textパッケージ
https://golang.org/pkg/text/
import (
"bufio"
"fmt"
"log"
"net"
"time"
)
type client struct {
who string
ch chan<- string // an outgoing message channel
}
const limitTimeout = 30 * time.Second
// const limitTimeout = 10 * time.Second
var (
entering = make(chan client)
leaving = make(chan client)
messages = make(chan string) // all incoming client messages
)
func broadcaster() {
clients := make(map[client]bool) // all connected clients
for {
select {
case msg := <-messages:
// Broadcast incoming message to all
// clients' outgoing message channels.
for cli := range clients {
cli.ch <- msg
}
case cli := <-entering:
if len(clients) > 0 {
cli.ch <- "active users: "
for other := range clients {
cli.ch <- "\t" + other.who
}
}
clients[cli] = true
case cli := <-leaving:
delete(clients, cli)
close(cli.ch)
}
}
}
func handleConn(conn net.Conn) {
ch := make(chan string) // outgoing client messages
go clientWriter(conn, ch)
who := conn.RemoteAddr().String()
ch <- "You are " + who
messages <- who + " has arrived"
entering <- client{who, ch}
timer := time.AfterFunc(limitTimeout, func() {
conn.Close()
})
// ここで終わるのでleaving,messagesは実施されないと思いきや
// conn.Close()にたどり着く前の操作を終えてくれる!
input := bufio.NewScanner(conn)
for input.Scan() {
messages <- who + ": " + input.Text()
timer.Reset(limitTimeout)
}
// NOTE: ignoring potential errors from input.Err()
leaving <- client{who, ch}
messages <- who + " has left"
conn.Close()
}
func clientWriter(conn net.Conn, ch <-chan string) {
for msg := range ch {
fmt.Fprintln(conn, msg) // NOTE: ignoring network errors
}
}
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
go broadcaster()
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
continue
}
go handleConn(conn)
}
}