Go에서 문자열의 문자 수를 어떻게 얻을 수 있습니까?
예를 들어 문자열 "hello"
이 있으면 메서드가 반환해야합니다 5
. 나는 보았다 len(str)
반환에게 바이트 수 와하지 그래서 문자 수 len("£")
£이 UTF-8 2 바이트로 인코딩되어 있기 때문에 수익률이 대신 일을.
Go에서 문자열의 문자 수를 어떻게 얻을 수 있습니까?
예를 들어 문자열 "hello"
이 있으면 메서드가 반환해야합니다 5
. 나는 보았다 len(str)
반환에게 바이트 수 와하지 그래서 문자 수 len("£")
£이 UTF-8 2 바이트로 인코딩되어 있기 때문에 수익률이 대신 일을.
답변:
RuneCountInString
utf8 패키지에서 시도해 볼 수 있습니다 .
p의 룬 수를 반환합니다.
이 스크립트 에서 볼 수 있듯이 "월드"의 길이는 6 일 수 있지만 (중국어로 쓰면 "世界") 룬 수는 2입니다.
package main
import "fmt"
import "unicode/utf8"
func main() {
fmt.Println("Hello, 世界", len("世界"), utf8.RuneCountInString("世界"))
}
실제로 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\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 "호환성 분해"를 사용합니다.
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 디코드 노력이 배가되는 것을 피하기 위해 룬이 처리 될 때 문자열을 "순회"하는 동안 카운트를 유추하는 것이 좋습니다.
String
의 .length()
방법 중 문자 수를 반환하지 않습니다. 둘 다 코코아의하지 않습니다 NSString
의 -length
방법. 그것들은 단순히 UTF-16 엔티티의 수를 반환합니다. 그러나 실제 코드 포인트 수는 계산에 선형 시간이 걸리기 때문에 거의 사용되지 않습니다.
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)
}
var
함수 외부에서 추출해야합니다 .
문자열 길이를 얻는 방법에는 여러 가지가 있습니다.
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".