문자열의 문자 수를 얻는 방법은 무엇입니까?
-
12-12-2019 - |
문제
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
}
.