私はJavaScriptでこれを行うのに慣れています:
var domains = "abcde".substring(0, "abcde".indexOf("cd")) // Returns "ab"
Swiftにはこの機能がありません、同様のことをする方法は?
編集/更新:
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"]
Swift 4.2/4.1/4.0/3.0でテスト済み
String[Range<String.Index>]
subscriptを使用すると、サブ文字列を取得できます。範囲を作成するには開始インデックスと最後のインデックスが必要で、以下のようにできます
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]
を使用することもできます
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
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"
}
この関数は最適化されていませんが、短い文字列を処理します。
Swiftバージョン3では、Stringには次のような関数はありません-
str.index(of: String)
部分文字列にインデックスが必要な場合、その方法の1つは範囲を取得することです。範囲を返す文字列には次の関数があります-
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
注:範囲はオプションです。文字列が見つからない場合は、nilになります。例えば
var str = "play play play"
var range = str.range(of: "Zoo") //Result : nil
range?.lowerBound //Result : nil
range?.upperBound //Result : nil
NSRangeの使用を検討しましたか?
if let range = mainString.range(of: mySubString) {
//...
}
ここには、密接に関連する3つの問題があります。
Cocoa NSStringの世界では、すべての部分文字列検索メソッドが終了しています(Foundation)
Foundation NSRangeはSwift Rangeと一致しません。前者は開始と長さを使用し、後者はエンドポイントを使用します
一般的に、Swift文字はIntではなくString.Index
を使用してインデックス付けされますが、Foundation文字areはIntを使用してインデックス付けされ、それらの間の単純な直接翻訳はありません(FoundationとSwiftは、キャラクターを構成するものについて異なる考えを持っています)
以上を踏まえて、書き方について考えてみましょう。
func substring(of s: String, from:Int, toSubstring s2 : String) -> Substring? {
// ?
}
String Foundationメソッドを使用して、サブストリングs2
をs
で検索する必要があります。結果の範囲は、NSRangeとして(これはFoundationメソッドですが)notとして返されますが、String.Index
のRangeとして(オプションでラップされた場合は、 tは部分文字列を見つけます)。ただし、他の番号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
}
}