string - 在 golang 中正确拆分 rune

标签 string go utf-8 slice rune

我想知道是否有一种简单的方法,例如处理代码点/ rune 的众所周知的函数,从 rune 片的中间取出一 block 而不会弄乱它,或者是否都需要自己编码才能下来等于或小于最大字节数。
具体来说,我要做的是将字符串传递给函数,将其转换为 rune ,以便我可以尊重代码点,如果 slice 长于某些最大字节,则从 rune 中心移除足够的 rune 以获得字节到必要的。
如果字符串只是单字节字符并且处理如下:这是简单的数学运算:

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
}
但是在一个可变字符宽度的字节字符串中,它要么需要更多的编码循环,要么会有很好的函数来简化它。有没有人有关于如何最好地用 rune 处理这种事情的代码示例?
这里的想法是字符串将进入的 DB 字段具有固定的最大字节长度,而不是代码点,因此需要一些从 rune 到最大字节的算法。从字符串中间取字符的原因只是这个特定程序的需要。
谢谢!
编辑:
一旦我发现范围运算符尊重字符串上的 rune ,这变得很容易只使用我发现的字符串,因为下面的答案很好。在这种情况下,我不必担心字符串是格式良好的 UTF 格式,但如果我这样做了,我现在知道 UTF 模块,谢谢!
这就是我最终得到的结果:
package main

import (
    "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

最佳答案

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

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
}
link on play.golang.org

此算法仅尝试从选定的前缀和后缀中删除更多字节。
例如,如果您需要从后缀中删除 3 个字节以获得有效的 rune ,它不会尝试查看是否可以在前缀中添加 3 个字节,以使最终结果更接近 maxLen。字节。

关于string - 在 golang 中正确拆分 rune ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64082390/

相关文章:

string - 删除文件中的半唯一字符串

java utf-8编码字符串中奇数个字符的字节变化

swift - 查找 Swift String 中所有 Substring 实例的有效方法

德尔菲字符串: Pull a last name from a full name

java - 模板处理——查找字符串中的变量引用

go - Go 中的接收器(方法)放在哪里?

Git2go : How to handle simple merge conflicts

go - slice 文字和make slice之间在行为上有区别吗?

ios - Swift 3 : how to convert a UTF8 data stream (1, 每个字符 2,3 或 4 个字节)到字符串?

MySQL 列从 UTF-8 迁移到 ASCII : Foreign Key and Unique Key constraints