2020年8月3日月曜日

文字列処理 time.afterの怪 構造体の外だしの怪

文字列(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/

 

--------------------------------------------------
package main

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)
}
}

------------------------------------------------------
Goの構造体をpackageに外出ししてハマッたこと