我曾经在JavaScript中这样做过:
var domains = "abcde".substring(0, "abcde".indexOf("cd")) // Returns "ab"
斯威夫特没有这个功能,如何做类似的事情呢?
答案 0 :(得分:87)
编辑/更新:
Xcode 10.2.x•Swift 5或更高版本
extension StringProtocol { // for Swift 4.x syntax you will needed also to constrain the collection Index to String Index - `extension StringProtocol where Index == String.Index`
func index(of string: Self, options: String.CompareOptions = []) -> Index? {
return range(of: string, options: options)?.lowerBound
}
func endIndex(of string: Self, options: String.CompareOptions = []) -> Index? {
return range(of: string, options: options)?.upperBound
}
func indexes(of string: Self, options: String.CompareOptions = []) -> [Index] {
var result: [Index] = []
var startIndex = self.startIndex
while startIndex < endIndex,
let range = self[startIndex...].range(of: string, options: options) {
result.append(range.lowerBound)
startIndex = range.lowerBound < range.upperBound ? range.upperBound :
index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
}
return result
}
func ranges(of string: Self, options: String.CompareOptions = []) -> [Range<Index>] {
var result: [Range<Index>] = []
var startIndex = self.startIndex
while startIndex < endIndex,
let range = self[startIndex...].range(of: string, options: options) {
result.append(range)
startIndex = range.lowerBound < range.upperBound ? range.upperBound :
index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
}
return result
}
}
<强>用法:强>
let str = "abcde"
if let index = str.index(of: "cd") {
let substring = str[..<index] // ab
let string = String(substring)
print(string) // "ab\n"
}
let str = "Hello, playground, playground, playground"
str.index(of: "play") // 7
str.endIndex(of: "play") // 11
str.indexes(of: "play") // [7, 19, 31]
str.ranges(of: "play") // [{lowerBound 7, upperBound 11}, {lowerBound 19, upperBound 23}, {lowerBound 31, upperBound 35}]
不区分大小写的样本
let query = "Play"
let ranges = str.ranges(of: query, options: .caseInsensitive)
let matches = ranges.map { str[$0] } //
print(matches) // ["play", "play", "play"]
正则表达式样本
let query = "play"
let escapedQuery = NSRegularExpression.escapedPattern(for: query)
let pattern = "\\b\(escapedQuery)\\w+" // matches any word that starts with "play" prefix
let ranges = str.ranges(of: pattern, options: .regularExpression)
let matches = ranges.map { str[$0] }
print(matches) // ["playground", "playground", "playground"]
答案 1 :(得分:34)
测试Swift 4.2 / 4.1 / 4.0 / 3.0
使用 String[Range<String.Index>]
下标,您可以获得子字符串。您需要开始索引和最后一个索引来创建范围,您可以按照下面的方式执行
let str = "abcde"
if let range = str.range(of: "cd") {
let substring = str[..<range.lowerBound] // or str[str.startIndex..<range.lowerBound]
print(substring) // Prints ab
}
else {
print("String not present")
}
如果未定义此运算符..<
的起始索引,则采用起始索引。您也可以使用str[str.startIndex..<range.lowerBound]
代替str[..<range.lowerBound]
答案 2 :(得分:5)
let str = "abcdefghabcd"
if let index = str.index(of: "b") {
print(index) // Index(_compoundOffset: 4, _cache: Swift.String.Index._Cache.character(1))
}
let str : String = "ilike"
for i in 0...str.count {
let index = str.index(str.startIndex, offsetBy: i) // String.Index
let prefix = str[..<index] // String.SubSequence
let suffix = str[index...] // String.SubSequence
print("prefix \(prefix), suffix : \(suffix)")
}
prefix , suffix : ilike
prefix i, suffix : like
prefix il, suffix : ike
prefix ili, suffix : ke
prefix ilik, suffix : e
prefix ilike, suffix :
let substring1 = string[startIndex...endIndex] // including endIndex
let subString2 = string[startIndex..<endIndex] // excluding endIndex
答案 3 :(得分:4)
可以在Swift中执行此操作,但需要更多行,这是一个函数indexOf()
执行预期的操作:
func indexOf(source: String, substring: String) -> Int? {
let maxIndex = source.characters.count - substring.characters.count
for index in 0...maxIndex {
let rangeSubstring = source.startIndex.advancedBy(index)..<source.startIndex.advancedBy(index + substring.characters.count)
if source.substringWithRange(rangeSubstring) == substring {
return index
}
}
return nil
}
var str = "abcde"
if let indexOfCD = indexOf(str, substring: "cd") {
let distance = str.startIndex.advancedBy(indexOfCD)
print(str.substringToIndex(distance)) // Returns "ab"
}
此功能未进行优化,但它可以完成短字符串的工作。
答案 4 :(得分:2)
在Swift版本3中,String没有像 -
这样的函数str.index(of: String)
如果子字符串需要索引,则获取范围的方法之一。我们在返回范围的字符串中有以下函数 -
str.range(of: <String>)
str.rangeOfCharacter(from: <CharacterSet>)
str.range(of: <String>, options: <String.CompareOptions>, range: <Range<String.Index>?>, locale: <Locale?>)
例如,在str
中查找第一次出现的游戏的索引var str = "play play play"
var range = str.range(of: "play")
range?.lowerBound //Result : 0
range?.upperBound //Result : 4
注意:范围是可选的。如果它无法找到String,它将使它为零。例如
var str = "play play play"
var range = str.range(of: "zoo") //Result : nil
range?.lowerBound //Result : nil
range?.upperBound //Result : nil
答案 5 :(得分:2)
快速5
查找子字符串的索引
let str = "abcdecd"
if let range: Range<String.Index> = str.range(of: "cd") {
let index: Int = str.distance(from: str.startIndex, to: range.lowerBound)
print("index: ", index) //index: 2
}
else {
print("substring not found")
}
查找字符索引
let str = "abcdecd"
if let firstIndex = str.firstIndex(of: "c") {
let index = str.distance(from: str.startIndex, to: firstIndex)
print("index: ", index) //index: 2
}
else {
print("symbol not found")
}
答案 6 :(得分:1)
您是否考虑过使用NSRange?
if let range = mainString.range(of: mySubString) {
//...
}
答案 7 :(得分:1)
Leo Dabus的答案很好。这是我基于他使用compactMap
来避免Index out of range
错误的答案。
extension StringProtocol {
func ranges(of targetString: Self, options: String.CompareOptions = [], locale: Locale? = nil) -> [Range<String.Index>] {
let result: [Range<String.Index>] = self.indices.compactMap { startIndex in
let targetStringEndIndex = index(startIndex, offsetBy: targetString.count, limitedBy: endIndex) ?? endIndex
return range(of: targetString, options: options, range: startIndex..<targetStringEndIndex, locale: locale)
}
return result
}
}
// Usage
let str = "Hello, playground, playground, playground"
let ranges = str.ranges(of: "play")
ranges.forEach {
print("[\($0.lowerBound.utf16Offset(in: str)), \($0.upperBound.utf16Offset(in: str))]")
}
// result - [7, 11], [19, 23], [31, 35]
答案 8 :(得分:0)
这里有三个密切相关的问题:
所有子字符串查找方法都在Cocoa NSString世界中完成(基础)
基金会NSRange与Swift Range不匹配;前者使用起点和长度,后者使用终点
一般情况下,使用String.Index
索引Swift字符,而不使用Int,但使用Int对基础字符 进行索引,并且它们之间没有简单的直接转换(因为Foundation)和斯威夫特对角色的构成有不同的看法。
考虑到这一切,让我们考虑如何写作:
func substring(of s: String, from:Int, toSubstring s2 : String) -> Substring? {
// ?
}
必须使用String Foundation方法在s2
中搜索子字符串s
。结果范围返回给我们,不作为NSRange(即使这是一个Foundation方法),但是作为String.Index
的范围(包含在Optional中,以防我们没有&# 39;根本找不到子串。但是,另一个数字from
是一个Int。因此,我们不能形成任何涉及它们的范围。
但我们不必!我们所要做的就是使用一个String.Index
的方法切掉原始字符串的 end ,然后使用原始字符串的 start 切掉采用Int的方法。幸运的是,存在这样的方法!像这样:
func substring(of s: String, from:Int, toSubstring s2 : String) -> Substring? {
guard let r = s.range(of:s2) else {return nil}
var s = s.prefix(upTo:r.lowerBound)
s = s.dropFirst(from)
return s
}
或者,如果您希望能够将此方法直接应用于字符串,就像这样...
let output = "abcde".substring(from:0, toSubstring:"cd")
...然后将其作为String的扩展名:
extension String {
func substring(from:Int, toSubstring s2 : String) -> Substring? {
guard let r = self.range(of:s2) else {return nil}
var s = self.prefix(upTo:r.lowerBound)
s = s.dropFirst(from)
return s
}
}
答案 9 :(得分:0)
快捷键5
extension String {
enum SearchDirection {
case first, last
}
func characterIndex(of character: Character, direction: String.SearchDirection) -> Int? {
let fn = direction == .first ? firstIndex : lastIndex
if let stringIndex: String.Index = fn(character) {
let index: Int = distance(from: startIndex, to: stringIndex)
return index
} else {
return nil
}
}
}
测试:
func testFirstIndex() {
let res = ".".characterIndex(of: ".", direction: .first)
XCTAssert(res == 0)
}
func testFirstIndex1() {
let res = "12345678900.".characterIndex(of: "0", direction: .first)
XCTAssert(res == 9)
}
func testFirstIndex2() {
let res = ".".characterIndex(of: ".", direction: .last)
XCTAssert(res == 0)
}
func testFirstIndex3() {
let res = "12345678900.".characterIndex(of: "0", direction: .last)
XCTAssert(res == 10)
}
答案 10 :(得分:0)
快捷键5
let alphabat = "abcdefghijklmnopqrstuvwxyz"
var index: Int = 0
if let range: Range<String.Index> = alphabat.range(of: "c") {
index = alphabat.distance(from: alphabat.startIndex, to: range.lowerBound)
print("index: ", index) //index: 2
}