문제

Go에서 문자열의 문자 수를 어떻게 얻을 수 있나요?

예를 들어 문자열이 있는 경우 "hello" 메서드가 반환되어야 합니다. 5.나는 것을보고 len(str) 바이트 수를 반환합니다. 그리고는 아니다 문자 수는 그래서 len("£") £는 UTF-8의 2바이트로 인코딩되므로 1 대신 2를 반환합니다.

도움이 되었습니까?

해결책

당신은 시도 할 수 있습니다 RuneCountInString utf8 패키지에서.

p의 룬 수를 반환합니다.

즉, 에 설명된 대로 이 스크립트:"World"의 길이는 6일 수 있습니다(중국어로 작성된 경우:"세계"), 룬 개수는 2입니다.

package main

import "fmt"
import "unicode/utf8"

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

프로젠 추가하다 댓글에서:

실제로 당신은 할 수 있습니다 len() 단지 타입 캐스팅으로 룬을 오버할 수 있습니다.
len([]rune("世界")) 인쇄할 것이다 2.Go 1.3에서는 최소한입니다.


그리고 CL 108985 (Go 1.11의 경우 2018년 5월), len([]rune(string)) 이제 최적화되었습니다.(수정 문제 24923)

컴파일러가 감지합니다. len([]rune(string)) 패턴을 자동으로 작성하고 for r := range s 호출로 바꿉니다.

문자열에서 룬을 계산하는 새로운 런타임 함수를 추가합니다.패턴을 감지하도록 컴파일러를 수정합니다. 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)

스테판 슈타이거 블로그 게시물 "을 가리킨다.Go의 텍스트 정규화"

캐릭터란 무엇입니까?

에서 언급했듯이 문자열 블로그 게시물, 캐릭터는 여러 룬에 걸쳐 있을 수 있습니다..
예를 들어, 'e'와 '◌́◌́'(급격 "\u0301")은 결합하여 'é'("e\u0301" NFD에서). 이 두 룬을 합치면 하나의 캐릭터가 됩니다.

문자의 정의는 응용 프로그램에 따라 다를 수 있습니다.
을 위한 표준화 우리는 그것을 다음과 같이 정의할 것입니다:

  • 스타터로 시작하는 일련의 룬,
  • 다른 룬과 거꾸로 수정되거나 결합되지 않는 룬,
  • 그 뒤에는 시작하지 않는 빈 시퀀스, 즉 해당하는 룬(일반적으로 악센트)이 뒤따릅니다.

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

해당 패키지와 그 패키지를 사용하여 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 "호환성 분해"


올리버'에스 답변 ~를 가리키다 유니코드 텍스트 분할 특정 중요한 텍스트 요소 사이의 기본 경계를 안정적으로 결정하는 유일한 방법입니다.사용자가 인지하는 문자, 단어, 문장.

이를 위해서는 다음과 같은 외부 라이브러리가 필요합니다. 리보/유니세그, 이는 유니코드 텍스트 분할.

그게 실제로 중요할 겁니다."문자소 무리", 여러 코드 포인트가 사용자가 인식하는 하나의 문자로 결합될 수 있습니다.

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

세 개의 룬(유니코드 코드 포인트)이 있더라도 문자소는 두 개입니다.

다른 팁

문자열을 [] rune로 변환하여 패키지가없는 패키지없이 룬을 숫자로 가져 오는 방법이 있습니다.

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와 equals character"는 귀하의 작업에 대해 괜찮습니다 (일반적으로 그것은 아닙니다). Vonc의 답변은 당신에게 완벽합니다.그렇지 않으면 유니 코드 문자열의 룬 문자 수가 흥미로운 값이있는 상황이 거의 없어야합니다.그리고 이러한 상황에서도 가능하면 UTF-8 디코드 노력이 두 배를 두 배로 피하기 위해 룬 문자열을 처리하는 동안 문자열을 처리하는 동안 카운트를 추론하는 것이 좋습니다.

그래픽 클러스터를 고려해야 할 필요가있는 경우 regexp 또는 유니 코드 모듈을 사용하십시오.그래픽 클러스터의 길이가 무제한이므로 코드 포인트 수 (룬) 수 (룬) 수 또는 바이트가 validaiton에 대해 필요합니다.매우 긴 시퀀스를 제거하려면 시퀀스가 스트림 안전 텍스트 형식을 준수하는지 확인하십시오. .

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

나는 지금까지 제공되는 답변이 당신이 기대할 때와 같은 문자의 수를 제공한다는 것을 지적해야합니다. 특히 이모티콘 (그러나 태국어, 한국어 또는 아랍어와 같은 일부 언어)을 다루는 것처럼 보입니다. 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".
.

정규화는 실제로 문자 계수와 동일하지 않으며 많은 문자를 하나의 코드 포인트로 정규화 할 수 없습니다.

Masakielastic의 답변 닫기가 있지만 수정 자만 (무지개 플래그가 포함 된 수정 자 포함) 코드 포인트) :

fmt.Println(GraphemeCountInString("🏳️‍🌈🇩🇪"))  // Outputs "5".
fmt.Println(GraphemeCountInString2("🏳️‍🌈🇩🇪")) // Outputs "5".
.

유니 코드 문자열을 (사용자 인식) 문자로 분할하는 올바른 방법, 즉 그래픽 클러스터는 유니 코드 표준 별관 # 29 . 규칙은 3.1.1 섹션을 찾을 수 있습니다. github.com/rivo/uniseg 패키지는 이러한 규칙을 구현하므로 올바른 문자 수를 결정할 수 있습니다. 문자열 :

fmt.Println(uniseg.GraphemeClusterCount("🏳️‍🌈🇩🇪")) // Outputs "2".
.

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

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)

}

.

나는 정상화를 조금 더 빨리 수행하려고 시도했다.

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

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top