我进行了搜索,但令人惊讶的是找不到答案。
我有一个很长的
NSString
,我想缩短。我希望最大长度约为 20 个字符。我在某处读到最好的解决方案是使用substringWithRange
。这是截断字符串的最佳方法吗?
NSRange stringRange = {0,20};
NSString *myString = @"This is a string, it's a very long string, it's a very long string indeed";
NSString *shortString = [myString substringWithRange:stringRange];
看起来有点微妙(如果字符串短于最大长度就会崩溃)。我也不确定它是否是 Unicode 安全的。有更好的方法吗?有人有一个很好的类别吗?
实际上,关于“Unicode 安全”的部分是完全正确的,因为许多字符在 unicode 中组合,而建议的答案没有考虑到。
例如,如果您想输入 é。一种方法是输入“e”(0x65)+组合重音符号“́”(0x301)。现在,如果您像这样输入“café”并截断 4 个字符,您将得到“cafe”。这可能会在某些地方引起问题。
如果你不关心这个,其他答案也可以。否则,请执行以下操作:
// define the range you're interested in
NSRange stringRange = {0, MIN([myString length], 20)};
// adjust the range to include dependent chars
stringRange = [myString rangeOfComposedCharacterSequencesForRange:stringRange];
// Now you can create the short string
NSString *shortString = [myString substringWithRange:stringRange];
请注意,这样您的范围可能会比您的初始范围长度更长。在上面的咖啡馆示例中,您的范围将扩展到长度 5,即使您仍然有 4 个“字形”。如果您绝对需要的长度小于您指定的长度,则需要检查这一点。
let trimToCharacter = 20
let shortString = String(myString.prefix(trimToCharacter))
快乐编码。
由于这个答案实际上并不在此列表中,所以最简单、最明智的一句话:
NSString *myString = @"This is a string, it's a very long string, it's a very long string indeed";
myString = (myString.length > 20) ? [myString substringToIndex:20] : myString;
更短的解决方案是:
NSString *shortString = ([myString length]>MINLENGTH ? [myString substringToIndex:MINLENGTH] : myString);
看起来有点精致(如果字符串短于最大长度就会崩溃)
那为什么不修复那部分呢?
NSRange stringRange = {0, MIN([myString length], 20)};
可以使用三元运算:
NSString *shortString = (stringRange.length <= [myString length]) ? myString : [myString substringWithRange:stringRange];
或者更好地控制最终结果:
if (stringRange.length > [myString length])
// throw exception, ignore error, or set shortString to myString
else
shortString = [myString substringWithRange:stringRange];
扩展以在不同位置(头部、尾部或中间)截断。
Swift 4.2 及更高版本
extension String {
enum TruncationPosition {
case head
case middle
case tail
}
func truncated(limit: Int, position: TruncationPosition = .tail, leader: String = "...") -> String {
guard self.count >= limit else { return self }
switch position {
case .head:
return leader + self.suffix(limit)
case .middle:
let halfCount = (limit - leader.count).quotientAndRemainder(dividingBy: 2)
let headCharactersCount = halfCount.quotient + halfCount.remainder
let tailCharactersCount = halfCount.quotient
return String(self.prefix(headCharactersCount)) + leader + String(self.suffix(tailCharactersCount))
case .tail:
return self.prefix(limit) + leader
}
}
}
最简单又漂亮的解决方案(文本末尾有3个点):
NSString *newText = [text length] > intTextLimit ?
[[text substringToIndex:intTextLimit] stringByAppendingString:@"…"] :
text;
//Short the string if string more than 45 chars
if([self.tableCellNames[indexPath.section] length] > 40) {
// define the range you're interested in
NSRange stringRange = {0, MIN([self.tableCellNames[indexPath.section] length], 40)};
// adjust the range to include dependent chars
stringRange = [self.tableCellNames[indexPath.section]
rangeOfComposedCharacterSequencesForRange:stringRange];
// Now you can create the short string
NSString *shortStringTitle = [self.tableCellNames[indexPath.section] substringWithRange:stringRange];
shortStringTitle = [shortStringTitle stringByAppendingString:@"..."];
titleLabel.text = shortStringTitle;
} else {
titleLabel.text = self.tableCellNames[indexPath.section];
}
//VKJ
所有 NSString 操作都是 Unicode 安全的,因为 NSString 内部本质上是一个 unichar 数组。即使字符串采用不同的编码,它在显示时也会转换为您指定的编码。
如果您想从最终使用中截断:
[fileName substringToIndex:anyNumber];
如果你想从头开始截断:
[fileName substringFromIndex:anyNumber];
使用
NSLineBreakMode
定义截断模式,您可以截断字符串,如下所示:
"a really long text to truncate with the given modes"
,,,
.truncated(.byClipping, to: 20)) // a really long text t
.truncated(.byWordWrapping, to: 20)) // a really long...
.truncated(.byTruncatingTail, to: 20)) // a really long tex...
.truncated(.byTruncatingMiddle, to: 20)) // a really...ven modes
.truncated(.byTruncatingHead, to: 20)) // ...h the given modes
这是背后的扩展:
extension String {
func truncated(_ mode: NSLineBreakMode = .byTruncatingTail, to maxLength: Int, truncatingSign: Self = "...") -> Self {
guard self.count > maxLength else { return self }
let visibleCount = maxLength - truncatingSign.count
switch mode {
case .byWordWrapping:
let mutatedSelf = self.prefix(visibleCount)
let nLastChar = self.index(after: mutatedSelf.endIndex)
guard self[nLastChar].isWhitespace else { return mutatedSelf + truncatingSign }
var lastWord: String = ""
mutatedSelf.enumerateSubstrings(in: startIndex..., options: [.byWords, .reverse]) { (substring , _, _, stop) in
guard let substring = substring else { return }
lastWord = substring
stop = true
}
if let range = mutatedSelf.range(of: lastWord, options: .backwards, range: nil, locale: nil) {
return mutatedSelf
.replacingCharacters(in: range, with: "")
.replacingOccurrences(of: "\\s+$", with: "", options: .regularExpression)
+ truncatingSign
}
return mutatedSelf + truncatingSign
case .byCharWrapping: return self.prefix(visibleCount) + truncatingSign
case .byClipping: return self.prefix(maxLength) + ""
case .byTruncatingHead: return truncatingSign + self.suffix(visibleCount)
case .byTruncatingTail: return self.prefix(visibleCount) + truncatingSign
case .byTruncatingMiddle:
let middle = Double(visibleCount)/2
return self.prefix(Int(middle.rounded(.down))) + ""
+ truncatingSign
+ self.suffix(Int(middle.rounded(.up))) + ""
@unknown default: return self /* Future unknown case */
}
}
}