为了账号安全,请及时绑定邮箱和手机立即绑定

在 golang 中正确拆分符文

在 golang 中正确拆分符文

Go
慕村225694 2022-07-04 16:12:50
我想知道是否有一种简单的方法,例如处理代码点/符文的众所周知的函数,从符文片的中间取出一块而不会弄乱它,或者是否都需要自己编码才能下来等于或小于最大字节数。具体来说,我要做的是将字符串传递给函数,将其转换为符文,以便我可以尊重代码点,如果切片长于某些最大字节,则从符文中心移除足够的符文以获得字节到必要的。如果字符串只是单字节字符并且处理如下:这是简单的数学运算:func shortenStringIDToMaxLength(in string, maxLen int) string {    if len(in) > maxLen {        excess := len(in) - maxLen        start := maxLen/2 - excess/2        return in[:start] + in[start+excess:]    }    return in}但是在一个可变字符宽度的字节字符串中,它要么需要更多的编码循环,要么会有很好的函数来简化它。有没有人有关于如何最好地用符文处理这种事情的代码示例?这里的想法是字符串将进入的 DB 字段具有固定的最大字节长度,而不是代码点,因此需要一些从符文到最大字节的算法。从字符串中间取字符的原因只是这个特定程序的需要。谢谢!编辑:一旦我发现范围运算符尊重字符串上的符文,这变得很容易只使用我发现的字符串,因为下面的答案很好。在这种情况下,我不必担心字符串是格式良好的 UTF 格式,但如果我这样做了,我现在知道 UTF 模块,谢谢!这就是我最终得到的结果:package mainimport (    "fmt")func ShortenStringIDToMaxLength(in string, maxLen int) string {    if maxLen < 1 {        // Panic/log whatever is your error system of choice.    }    bytes := len(in)    if bytes > maxLen {        excess := bytes - maxLen        lPos := bytes/2 - excess/2        lastPos := 0        for pos, _ := range in {            if pos > lPos {                lPos = lastPos                break            }            lastPos = pos        }        rPos := lPos + excess        for pos, _ := range in[lPos:] {            if pos >= excess {                rPos = pos                break            }        }        return in[:lPos] + in[lPos+rPos:]    }    return in}func main() {    out := ShortenStringIDToMaxLength(`123456789 123456789`, 5)    fmt.Println(out, len(out))}https://play.golang.org/p/YLGlj_17A-j
查看完整描述

2 回答

?
胡子哥哥

TA贡献1825条经验 获得超6个赞

这是您的算法的改编版,它从前缀的开头和后缀的结尾删除不完整的符文:


func TrimLastIncompleteRune(s string) string {

    l := len(s)


    for i := 1; i <= l; i++ {

        suff := s[l-i : l]

        // repeatedly try to decode a rune from the last bytes in string

        r, cnt := utf8.DecodeRuneInString(suff)

        if r == utf8.RuneError {

            continue

        }


        // if success : return the substring which contains

        // this succesfully decoded rune

        lgth := l - i + cnt

        return s[:lgth]

    }


    return ""

}


func TrimFirstIncompleteRune(s string) string {

    // repeatedly try to decode a rune from the beginning

    for i := 0; i < len(s); i++ {

        if r, _ := utf8.DecodeRuneInString(s[i:]); r != utf8.RuneError {

            // if success : return

            return s[i:]

        }

    }

    return ""

}


func shortenStringIDToMaxLength(in string, maxLen int) string {

    if len(in) > maxLen {

        firstHalf := maxLen / 2

        secondHalf := len(in) - (maxLen - firstHalf)


        prefix := TrimLastIncompleteRune(in[:firstHalf])

        suffix := TrimFirstIncompleteRune(in[secondHalf:])


        return prefix + suffix

    }

    return in

}

play.golang.org 上的链接


此算法仅尝试从选定的前缀和后缀中删除更多字节。


例如,如果事实证明您需要从后缀中删除 3 个字节才能获得有效的符文,则它不会尝试查看是否可以在前缀中添加 3 个字节,以获得更接近maxLen字节的最终结果。


查看完整回答
反对 回复 2022-07-04
?
冉冉说

TA贡献1877条经验 获得超1个赞

您可以使用简单的算术来查找start和end使得字符串s[:start]+s[end:]短于您的字节限制。但是您需要确保start和end都是任何 utf-8 序列的第一个字节,以保持序列有效。


UTF-8 的特性是任何给定字节都是序列的第一个字节,只要它的前两位不是 10。


所以你可以写这样的代码(操场: https: //play.golang.org/p/xk_Yo_1wTYc)


package main


import (

    "fmt"

)


func truncString(s string, maxLen int) string {

    if len(s) <= maxLen {

        return s

    }

    start := (maxLen + 1) / 2

    for start > 0 && s[start]>>6 == 0b10 {

        start--

    }

    end := len(s) - (maxLen - start)

    for end < len(s) && s[end]>>6 == 0b10 {

        end++

    }

    return s[:start] + s[end:]

}


func main() {

    fmt.Println(truncString("this is a test", 5))

    fmt.Println(truncString("日本語", 7))

}

无论输入字符串有多长(假设它是有效的 utf-8),这段代码都有一个理想的属性,它需要 O(maxLen) 时间。


查看完整回答
反对 回复 2022-07-04
  • 2 回答
  • 0 关注
  • 113 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信