【发布时间】: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 个字节?