【问题标题】:Fit Swift string in database VARCHAR(255)在数据库 VARCHAR(255) 中拟合 Swift 字符串
【发布时间】:2016-07-11 10:19:08
【问题描述】:

我正在尝试从 Swift 字符串中获取最多 255 个 UTF8 代码单元的有效子字符串(想法是能够将其存储在数据库 VARCHAR(255) 字段中)。

获取子字符串的标准方法是这样的:

let string: String = "Hello world!"
let startIndex = string.startIndex
let endIndex = string.startIndex.advancedBy(255, limit: string.endIndex)
let databaseSubstring1 = string[startIndex..<endIndex]

但显然这会给我一个 255 个字符的字符串,在 UTF8 表示中可能需要超过 255 个字节。

对于 UTF8,我可以这样写:

let utf8StartIndex = string.utf8.startIndex
let utf8EndIndex = utf8StartIndex.advancedBy(255, limit: string.utf8.endIndex)
let databaseSubstringUTF8View = name.utf8[utf8StartIndex..<utf8EndIndex]
let databaseSubstring2 = String(databaseSubstringUTF8View)

但我冒着最后有半个字符的风险,这意味着我的 UTF8View 不是有效的 UTF8 序列。 正如预期的那样,databaseSubstring2 是一个可选字符串,因为初始化程序可能会失败(它被定义为public init?(_ utf8: String.UTF8View))。

所以我需要某种方法在最后去除无效的 UTF8 代码点,或者 - 如果可能的话 - 一种内置的方法来做我在这里尝试做的事情。

编辑

原来数据库理解字符,所以我不应该尝试计算 UTF8 代码单元,而是数据库将在我的字符串中计算多少个字符(这可能取决于数据库)。

根据@OOPer,MySQL 将字符计为 UTF-16 代码单元。我想出了以下实现:

private func databaseStringForString(string: String, maxLength: Int = 255) -> String
{
    // Start by clipping to 255 characters
    let startIndex = string.startIndex
    let endIndex = startIndex.advancedBy(maxLength, limit: string.endIndex)
    var string = string[startIndex..<endIndex]

    // Remove characters from the end one by one until we have less than
    // the maximum number of UTF-16 code units
    while (string.utf16.count > maxLength) {
        let startIndex = string.startIndex
        let endIndex = string.endIndex.advancedBy(-1, limit: startIndex)
        string = string[startIndex..<endIndex]
    }
    return string
}

这个想法是计算 UTF-16 代码单元,但从末尾删除字符(这就是 Swift 认为的字符是什么)。

编辑 2

仍然根据@OOPer,Posgresql 将字符计为 unicode 标量,所以这应该可以工作:

private func databaseStringForString(string: String, maxLength: Int = 255) -> String
{
    // Start by clipping to 255 characters
    let startIndex = string.startIndex
    let endIndex = startIndex.advancedBy(maxLength, limit: string.endIndex)
    var string = string[startIndex..<endIndex]

    // Remove characters from the end one by one until we have less than
    // the maximum number of Unicode Scalars
    while (string.unicodeScalars.count > maxLength) {
        let startIndex = string.startIndex
        let endIndex = string.endIndex.advancedBy(-1, limit: startIndex)
        string = string[startIndex..<endIndex]
    }
    return string
}

【问题讨论】:

  • 我很困惑...您希望结果是 255 个字符,还是 255 个字节
  • 我希望将结果存储在 VARCHAR(255) 中,因此最多 255 个字节。但我想要一个有效的 UTF8 序列,所以它可能必须更少。
  • 您使用的是什么数据库? MySQL 中的VARCHAR(255) 表示列的编码中有255 个字符,可能超过255 个字节。
  • 是的,我知道。我的数据库使用 UTF8。
  • 然后我又更加困惑了。如果您的数据库允许您存储 255 个字符(可能远远超过 255 个字节),那么您为什么要将字符串限制为 255 个字节?

标签: swift string utf-8 swift2


【解决方案1】:

正如我在评论中所写,您可能需要您的databaseStringForString(_:maxLength:) 来截断您的字符串以匹配您的 DBMS 的长度限制。带有 utf8 的 PostgreSQL,带有 utf8mb4 的 MySQL。

我会编写与您的 EDIT 2 相同的功能:

func databaseStringForString(string: String, maxUnicodeScalarLength: Int = 255) -> String {
    let start = string.startIndex
    for index in start..<string.endIndex {
        if string[start..<index.successor()].unicodeScalars.count > maxUnicodeScalarLength {
            return string[start..<index]
        }
    }
    return string
}

这可能效率较低,但会短一些。

let s = "abc\u{1D122}\u{1F1EF}\u{1F1F5}" //->"abc???"

let dbus = databaseStringForString(s, maxUnicodeScalarLength: 5) //->"abc?"(=="abc\u{1D122}")

因此,使用带有 utf8(=utf8mb3) 的 MySQL 的人需要这样的东西:

func databaseStringForString(string: String, maxUTF16Length: Int = 255) -> String {
    let start = string.startIndex
    for index in start..<string.endIndex {
        if string[start..<index.successor()].utf16.count > maxUTF16Length {
            return string[start..<index]
        }
    }
    return string
}

let dbu16 = databaseStringForString(s, maxUTF16Length: 4) //->"abc"

【讨论】:

猜你喜欢
  • 2021-03-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-24
  • 1970-01-01
  • 2017-09-05
相关资源
最近更新 更多