Pregunta

¿Cómo puedo obtener el número de caracteres de una cadena en Ir?

Por ejemplo, si tengo una cadena "hello" el método debe devolver 5.Vi que len(str) devuelve el número de bytes y no el número de caracteres para len("£") devuelve 2 en lugar de 1, debido a £ se codifica con dos bytes en el formato UTF-8.

¿Fue útil?

Solución

Usted puede tratar de RuneCountInString a partir de la utf8 paquete.

devuelve el número de runas en p

que, como se ilustra en la esta secuencia de comandos:la longitud de "Mundo" puede ser 6 (cuando se escriben en Chino:"世界"), pero su runa recuento es de 2:

package main

import "fmt"
import "unicode/utf8"

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

Phrozen agrega en los comentarios:

En realidad se puede hacer len() sobre las runas por un sólo tipo de fundición.
len([]rune("世界")) imprimir 2.En acequias en Ir 1.3.


Y con CL 108985 (Mayo de 2018, para Ir 1.11), len([]rune(string)) está ahora optimizado.(Corrige tema 24923)

El compilador detecta len([]rune(string)) patrón de forma automática, y lo reemplaza con para i := rango s de la llamada.

Se añade un nuevo tiempo de ejecución de la función para contar las runas en una cadena.Modifica el compilador para detectar el patrón de len([]rune(string)) y lo reemplaza con la nueva runa de conteo de tiempo de ejecución de la función.

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)

Stefan Steiger puntos a los post del blog "El texto de la normalización en Ir"

¿Qué es un personaje?

Como se mencionó en la cadenas blog, los personajes pueden abarcar varias runas.
Por ejemplo, un 'e'y '◌◌' (aguda "\u0301") se pueden combinar para formar 'é' ("e\u0301"en el NFD). Juntos, estos dos runas tienen un carácter.

La definición de un carácter puede variar dependiendo de la aplicación.
Para la normalización vamos a definirlo como:

  • una secuencia de runas que se inicia con un motor de arranque,
  • una runa que no modificar o combinar hacia atrás con cualquier otra runa,
  • seguido por posiblemente secuencia vacía de no empezar, es decir, las runas que hacer (normalmente los acentos).

El algoritmo de normalización de los procesos de un carácter en el momento.

El uso de este paquete y su Iter tipo de, el número real de "carácter" sería:

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

Aquí, se utiliza la Normalización de Unicode formulario NFKD "Compatibilidad de Descomposición"


Oliver's respuesta puntos a TEXTO UNICODE SEGMENTACIÓN como la única forma confiable de determinar defecto límites entre ciertos elementos de texto:percibida por el usuario de los caracteres, palabras y oraciones.

Por eso, usted necesita una biblioteca externa como rivo/uniseg, que hace Texto Unicode Segmentación.

Que realmente contar "grafema clúster", donde varios puntos de código que pueden ser combinadas en un percibida por el usuario del personaje.

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

Dos grafemas, a pesar de que hay tres runas (puntos de código Unicode).

Otros consejos

Hay una manera de obtener el número de runas sin ningún tipo de paquetes mediante la conversión de string a []runa como 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)))

}

recuento de bytes 30 16

recuento de las runas 16 16

Depende mucho de tu definición de lo que es un "personaje" es.Si "runa es igual a un personaje" está bien para su tarea (generalmente no lo es), entonces la respuesta por VonC es perfecto para usted.De lo contrario, debe ser probablemente señalar, que hay algunas situaciones donde el número de runas en una cadena Unicode es un valor interesante.E incluso en esas situaciones es mejor, si es posible, para inferir el conde mientras que "atravesar" la cadena como las runas son procesados para evitar la duplicación de la UTF-8 decodificar esfuerzo.

Si usted necesita tomar grafema clusters en cuenta, el uso de regexp o unicode módulo.Contando el número de puntos de código(runas) o bytes que también es necesario para validaiton ya que la duración de este clúster es ilimitado.Si desea eliminar extremadamente largas secuencias, comprobar si las secuencias se ajustan a flujo seguro de formato de texto.

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

Debo señalar que ninguna de las respuestas ofrecidas hasta ahora dará el número de caracteres como era de esperar, especialmente cuando usted está tratando con emojis (pero también algunos idiomas como el Tailandés, coreano, o árabe). VonC sugerencias de salida será el siguiente:

fmt.Println(utf8.RuneCountInString("🏳️‍🌈🇩🇪")) // Outputs "6".
fmt.Println(len([]rune("🏳️‍🌈🇩🇪"))) // Outputs "6".

Eso es debido a que estos métodos sólo el recuento de puntos de código Unicode.Hay muchos personajes que puede estar compuesta de varios puntos de código.

Mismo para el uso de la La normalización de paquete:

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

La normalización no es realmente el mismo, ya que el recuento de caracteres y a muchos de los personajes no puede ser normalizada en un único punto de código equivalente.

masakielastic la respuesta viene de cerca, pero que sólo se encarga de modificadores (la bandera del arco iris contiene un modificador que es lo que no se contabilizan como su propio punto de código):

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

La forma correcta de dividir cadenas Unicode en (percibida por el usuario) caracteres, es decir,grafema clusters, se define en el Estándar Unicode Anexo #29.Las reglas se pueden encontrar en La sección 3.1.1.El github.com/rivo/uniseg paquete implementa estas reglas para que usted pueda determinar el número correcto de caracteres en una cadena:

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

Hay varias maneras de obtener una longitud de cadena:

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)

}

Traté de hacer para hacer que se normalice un poco más rápido:

    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
    }
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top