[string] 문자열에서 문자 수를 얻는 방법은 무엇입니까?

Go에서 문자열의 문자 수를 어떻게 얻을 수 있습니까?

예를 들어 문자열 "hello"이 있으면 메서드가 반환해야합니다 5. 나는 보았다 len(str)반환에게 바이트 수 와하지 그래서 문자 수 len("£")£이 UTF-8 2 바이트로 인코딩되어 있기 때문에 수익률이 대신 일을.



답변

RuneCountInStringutf8 패키지에서 시도해 볼 수 있습니다 .

p의 룬 수를 반환합니다.

이 스크립트 에서 볼 수 있듯이 “월드”의 길이는 6 일 수 있지만 (중국어로 쓰면 “世界”) 룬 수는 2입니다.

package main

import "fmt"
import "unicode/utf8"

func main() {
    fmt.Println("Hello, 世界", len("世界"), utf8.RuneCountInString("世界"))
}

Phrozen 은 주석에 다음을 추가합니다 .

실제로 len()타입 캐스팅만으로 룬을 처리 할 수 있습니다 .
len([]rune("世界"))인쇄 2합니다. 바둑에서 도약 1.3.


그리고 CL 108985 (2018 년 5 월, Go 1.11)로 len([]rune(string))최적화되었습니다. (Fixes 이슈 24923) )

컴파일러는 len([]rune(string))패턴을 자동으로 감지 하여 r : = range 호출로 대체합니다.

문자열에서 룬을 계산하기 위해 새로운 런타임 함수를 추가합니다. 패턴을 감지하도록 컴파일러를 수정하고 len([]rune(string))
새로운 룬 계산 런타임 함수로 대체합니다.

RuneCount/lenruneslice/ASCII                  27.8ns ± 2%  14.5ns ± 3%  -47.70%  (p=0.000 n=10+10)
RuneCount/lenruneslice/Japanese                126ns ± 2%    60ns ± 2%  -52.03%  (p=0.000 n=10+10)
RuneCount/lenruneslice/MixedLength             104ns ± 2%    50ns ± 1%  -51.71%  (p=0.000 n=10+9)

스테판 스타 이거는 블로그 게시물 “을 가리키는 이동의 텍스트 정상화

캐릭터는 무엇입니까?

로가에 언급 된 문자열 블로그 게시물 , 문자는 여러 룬에 걸쳐있을 수 있습니다 .
예를 들어, ‘ e‘및 ‘◌́◌́'(급성 “\ u0301”)은 결합하여 ‘é'( e\u0301NFD에서 ” “) 를 형성 할 수 있습니다 . 이 두 룬은 하나의 캐릭터 입니다.

문자의 정의는 응용 프로그램에 따라 다를 수 있습니다. 정규화
위해 다음 과 같이 정의합니다.

  • 스타터로 시작하는 일련의 룬
  • 다른 룬과 수정하거나 뒤로 결합하지 않는 룬
  • 빈 스타터가 아닌 런, 즉 룬 문자 (일반적으로 악센트)가 이어질 수 있습니다.

정규화 알고리즘은 한 번에 한 문자 만 처리합니다.

해당 패키지와 해당 Iter유형 을 사용하면 실제 “문자”수는 다음과 같습니다.

package main

import "fmt"
import "golang.org/x/text/unicode/norm"

func main() {
    var ia norm.Iter
    ia.InitString(norm.NFKD, "école")
    nc := 0
    for !ia.Done() {
        nc = nc + 1
        ia.Next()
    }
    fmt.Printf("Number of chars: %d\n", nc)
}

여기서는 유니 코드 정규화 양식 NFKD “호환성 분해”를 사용합니다.


Oliver답변UNICODE TEXT SEGMENTATION 을 특정 중요한 텍스트 요소 (사용자 인식 문자, 단어 및 문장) 사이의 기본 경계를 안정적으로 결정하는 유일한 방법으로 지적합니다.

이를 위해서는 rivo / uniseg 와 같은 외부 라이브러리가 필요합니다.이 라이브러리 는 Unicode Text Segmentation 입니다.

실제로는 ” grapheme cluster “로 계산됩니다 . 여기서 여러 코드 포인트가 하나의 사용자 인식 문자로 결합 될 수 있습니다.

package uniseg

import (
    "fmt"

    "github.com/rivo/uniseg"
)

func main() {
    gr := uniseg.NewGraphemes("??!")
    for gr.Next() {
        fmt.Printf("%x ", gr.Runes())
    }
    // Output: [1f44d 1f3fc] [21]
}

3 개의 룬 (유니 코드 코드 포인트)이 있지만 2 개의 그래 핀입니다.

당신은 “다른 예를 볼 수 있습니다 을 반대로 GO 문자열을 조작하는 방법을?

??‍?은 하나의 그래프이지만 유니 코드에서 코드 포인트 변환기 까지 4 개의 룬입니다.


답변

다음과 같이 문자열을 [] rune으로 변환하여 패키지없이 룬 수를 얻는 방법이 있습니다 len([]rune(YOUR_STRING)).

package main

import "fmt"

func main() {
    russian := "Спутник и погром"
    english := "Sputnik & pogrom"

    fmt.Println("count of bytes:",
        len(russian),
        len(english))

    fmt.Println("count of runes:",
        len([]rune(russian)),
        len([]rune(english)))

}

바이트 수 30 16

룬의 수 16 16


답변

“캐릭터”가 무엇인지에 대한 정의에 많이 의존합니다. 작업에 “rune is a character”가 정상이면 (일반적으로 그렇지 않은 경우) VonC의 답변이 귀하에게 적합합니다. 그렇지 않으면 유니 코드 문자열의 룬 수가 흥미로운 값인 상황이 거의 없다는 점에 유의해야합니다. 이러한 상황에서도 UTF-8 디코드 노력이 배가되는 것을 피하기 위해 룬이 처리 될 때 문자열을 “순회”하는 동안 카운트를 유추하는 것이 좋습니다.


답변

grapheme 클러스터를 고려해야 할 경우 regexp 또는 unicode 모듈을 사용하십시오. grapheme 클러스터의 길이가 무제한이기 때문에 유효성 검사에는 코드 포인트 (런) 또는 바이트 수를 계산하는 것도 필요합니다. 매우 긴 시퀀스를 제거하려면 시퀀스가 스트림 안전 텍스트 형식을 따르는 지 확인하십시오 .

package main

import (
    "regexp"
    "unicode"
    "strings"
)

func main() {

    str := "\u0308" + "a\u0308" + "o\u0308" + "u\u0308"
    str2 := "a" + strings.Repeat("\u0308", 1000)

    println(4 == GraphemeCountInString(str))
    println(4 == GraphemeCountInString2(str))

    println(1 == GraphemeCountInString(str2))
    println(1 == GraphemeCountInString2(str2))

    println(true == IsStreamSafeString(str))
    println(false == IsStreamSafeString(str2))
}


func GraphemeCountInString(str string) int {
    re := regexp.MustCompile("\\PM\\pM*|.")
    return len(re.FindAllString(str, -1))
}

func GraphemeCountInString2(str string) int {

    length := 0
    checked := false
    index := 0

    for _, c := range str {

        if !unicode.Is(unicode.M, c) {
            length++

            if checked == false {
                checked = true
            }

        } else if checked == false {
            length++
        }

        index++
    }

    return length
}

func IsStreamSafeString(str string) bool {
    re := regexp.MustCompile("\\PM\\pM{30,}")
    return !re.MatchString(str)
}


답변

문자열 길이를 얻는 방법에는 여러 가지가 있습니다.

package main

import (
    "bytes"
    "fmt"
    "strings"
    "unicode/utf8"
)

func main() {
    b := "这是个测试"
    len1 := len([]rune(b))
    len2 := bytes.Count([]byte(b), nil) -1
    len3 := strings.Count(b, "") - 1
    len4 := utf8.RuneCountInString(b)
    fmt.Println(len1)
    fmt.Println(len2)
    fmt.Println(len3)
    fmt.Println(len4)

}


답변

지금까지 제공된 답변 중 특히 이모티콘을 처리 할 때 예상되는 문자 수 (태국어, 한국어 또는 아랍어와 같은 일부 언어)를 제공하지는 않습니다. VonC의 제안 은 다음을 출력합니다.

fmt.Println(utf8.RuneCountInString("?️‍???")) // Outputs "6".
fmt.Println(len([]rune("?️‍???"))) // Outputs "6".

이러한 메서드는 유니 코드 코드 포인트 만 계산하기 때문입니다. 여러 코드 포인트로 구성 될 수있는 많은 문자가 있습니다.

정규화 패키지 를 사용하는 경우와 동일 합니다 .

var ia norm.Iter
ia.InitString(norm.NFKD, "?️‍???")
nc := 0
for !ia.Done() {
    nc = nc + 1
    ia.Next()
}
fmt.Println(nc) // Outputs "6".

정규화는 실제로 문자를 세는 것과 같지 않으며 많은 문자를 1 코드 포인트로 정규화 할 수 없습니다.

masakielastic의 대답 은 가깝지만 수정자를 처리합니다 (무지개 플래그에는 수정자가 포함되어 자체 코드 포인트로 계산되지 않음).

fmt.Println(GraphemeCountInString("?️‍???"))  // Outputs "5".
fmt.Println(GraphemeCountInString2("?️‍???")) // Outputs "5".

Unicode 문자열을 (사용자 인식) 문자, 즉 grapheme 클러스터로 나누는 올바른 방법은 Unicode Standard Annex # 29에 정의되어 있습니다. 규칙은 3.1.1 절 에서 찾을 수 있습니다 . github.com/rivo/uniseg의 패키지 구현이 규칙 당신은 문자열에있는 문자의 정확한 수를 확인할 수 있습니다 :

fmt.Println(uniseg.GraphemeClusterCount("?️‍???")) // Outputs "2".


답변

정규화를 조금 더 빠르게하려고했습니다.

    en, _ = glyphSmart(data)

    func glyphSmart(text string) (int, int) {
        gc := 0
        dummy := 0
        for ind, _ := range text {
            gc++
            dummy = ind
        }
        dummy = 0
        return gc, dummy
    }