在Swift String中查找字符索引

是时候承认失败了…

在Objective-C中,我可以使用像这样的东西:

NSString* str = @"abcdefghi"; [str rangeOfString:@"c"].location; // 2 

在Swift中,我看到类似的东西:

 var str = "abcdefghi" str.rangeOfString("c").startIndex 

…但是这只是给了我一个String.Index ,我可以用它来回原始string,但不提取的位置。

FWIW, String.Index有一个叫做_position的私人ivar,它有正确的值。 我只是不知道它是如何暴露的。

我知道我可以很容易地添加这个string自己。 我对这个新的API中缺less的东西感到好奇。

你不是唯一一个找不到解决scheme的人。

String不会实现RandomAccessIndexType 。 可能是因为它们启用不同字节长度的字符。 这就是为什么我们必须使用string.characters.count (Swift 1.x中的countcountElements )来获取字符数。 这也适用于职位。 _position可能是原始数组字节的索引,他们不想公开这个数组。 String.Index是为了防止我们访问字符中间的字节。

这意味着您得到的任何索引都必须从String.startIndexString.endIndexString.Index实现BidirectionalIndexType )创build。 任何其他指数都可以使用successorpredecessor方法来创build。

现在来帮助我们索引,有一组方法(Swift 1.x中的函数):

Swift 3.0

 let text = "abc" let index2 = text.index(text.startIndex, offsetBy: 2) //will call succ 2 times let lastChar: Character = text[index2] //now we can index! let characterIndex2 = text.characters.index(text.characters.startIndex, offsetBy: 2) let lastChar2 = text.characters[characterIndex2] //will do the same as above let range: Range<String.Index> = text.range(of: "b")! let index: Int = text.distance(from: text.startIndex, to: range.lowerBound) 

Swift 2.x

 let text = "abc" let index2 = text.startIndex.advancedBy(2) //will call succ 2 times let lastChar: Character = text[index2] //now we can index! let lastChar2 = text.characters[index2] //will do the same as above let range: Range<String.Index> = text.rangeOfString("b")! let index: Int = text.startIndex.distanceTo(range.startIndex) //will call successor/predecessor several times until the indices match 

Swift 1.x

 let text = "abc" let index2 = advance(text.startIndex, 2) //will call succ 2 times let lastChar: Character = text[index2] //now we can index! let range = text.rangeOfString("b") let index: Int = distance(text.startIndex, range.startIndex) //will call succ/pred several times 

使用String.Index是很麻烦的,但是使用一个包装来整数索引(请参阅https://stackoverflow.com/a/25152652/669586 )是危险的,因为它隐藏了真正索引的低效率。

请注意,Swift索引实现的问题是, 为一个string创build的索引/范围不能可靠地用于不同的string ,例如:

Swift 2.x

 let text: String = "abc" let text2: String = "🎾🏇🏈" let range = text.rangeOfString("b")! //can randomly return a bad substring or throw an exception let substring: String = text2[range] //the correct solution let intIndex: Int = text.startIndex.distanceTo(range.startIndex) let startIndex2 = text2.startIndex.advancedBy(intIndex) let range2 = startIndex2...startIndex2 let substring: String = text2[range2] 

Swift 1.x

 let text: String = "abc" let text2: String = "🎾🏇🏈" let range = text.rangeOfString("b") //can randomly return nil or a bad substring let substring: String = text2[range] //the correct solution let intIndex: Int = distance(text.startIndex, range.startIndex) let startIndex2 = advance(text2.startIndex, intIndex) let range2 = startIndex2...startIndex2 let substring: String = text2[range2] 

Swift 3.0使得这一点更加冗长:

 let string = "Hello.World" let needle: Character = "." if let idx = string.characters.index(of: needle) { let pos = string.characters.distance(from: string.startIndex, to: idx) print("Found \(needle) at position \(pos)") } else { print("Not found") } 

延期:

 extension String { public func index(of char: Character) -> Int? { if let idx = characters.index(of: char) { return characters.distance(from: startIndex, to: idx) } return nil } } 

Swift 2.0中,这变得更容易了:

 let string = "Hello.World" let needle: Character = "." if let idx = string.characters.indexOf(needle) { let pos = string.startIndex.distanceTo(idx) print("Found \(needle) at position \(pos)") } else { print("Not found") } 

延期:

 extension String { public func indexOfCharacter(char: Character) -> Int? { if let idx = self.characters.indexOf(char) { return self.startIndex.distanceTo(idx) } return nil } } 

Swift 1.x实现:

对于纯Swift解决scheme,可以使用:

 let string = "Hello.World" let needle: Character = "." if let idx = find(string, needle) { let pos = distance(string.startIndex, idx) println("Found \(needle) at position \(pos)") } else { println("Not found") } 

作为String的扩展:

 extension String { public func indexOfCharacter(char: Character) -> Int? { if let idx = find(self, char) { return distance(self.startIndex, idx) } return nil } } 
 extension String { // MARK: - sub String func substringToIndex(index:Int) -> String { return self.substringToIndex(advance(self.startIndex, index)) } func substringFromIndex(index:Int) -> String { return self.substringFromIndex(advance(self.startIndex, index)) } func substringWithRange(range:Range<Int>) -> String { let start = advance(self.startIndex, range.startIndex) let end = advance(self.startIndex, range.endIndex) return self.substringWithRange(start..<end) } subscript(index:Int) -> Character{ return self[advance(self.startIndex, index)] } subscript(range:Range<Int>) -> String { let start = advance(self.startIndex, range.startIndex) let end = advance(self.startIndex, range.endIndex) return self[start..<end] } // MARK: - replace func replaceCharactersInRange(range:Range<Int>, withString: String!) -> String { var result:NSMutableString = NSMutableString(string: self) result.replaceCharactersInRange(NSRange(range), withString: withString) return result } } 

我发现这个解决scheme为swift2:

 var str = "abcdefghi" let indexForCharacterInString = str.characters.indexOf("c") //returns 2 

我不确定如何从String.Index中提取位置,但是如果您愿意回退一些Objective-C框架,则可以桥接到objective-c并按照以前的方式进行操作。

 "abcdefghi".bridgeToObjectiveC().rangeOfString("c").location 

似乎有些NSString方法还没有(或者不会)被移植到String。 包含也想到。

这是一个干净的string扩展,回答了这个问题:

Swift 3:

 extension String { var length:Int { return self.characters.count } func indexOf(target: String) -> Int? { let range = (self as NSString).range(of: target) guard range.toRange() != nil else { return nil } return range.location } func lastIndexOf(target: String) -> Int? { let range = (self as NSString).range(of: target, options: NSString.CompareOptions.backwards) guard range.toRange() != nil else { return nil } return self.length - range.location - 1 } func contains(s: String) -> Bool { return (self.range(of: s) != nil) ? true : false } } 

Swift 2.2:

 extension String { var length:Int { return self.characters.count } func indexOf(target: String) -> Int? { let range = (self as NSString).rangeOfString(target) guard range.toRange() != nil else { return nil } return range.location } func lastIndexOf(target: String) -> Int? { let range = (self as NSString).rangeOfString(target, options: NSStringCompareOptions.BackwardsSearch) guard range.toRange() != nil else { return nil } return self.length - range.location - 1 } func contains(s: String) -> Bool { return (self.rangeOfString(s) != nil) ? true : false } } 

如果你想使用熟悉的NSString,你可以明确声明:

 var someString: NSString = "abcdefghi" var someRange: NSRange = someString.rangeOfString("c") 

我不知道如何在Swift中做到这一点。

我知道这是旧的,一个答案已被接受,但你可以findstring的索引在几行代码中使用:

 var str : String = "abcdefghi" let characterToFind: Character = "c" let characterIndex = find(str, characterToFind) //returns 2 

关于Swiftstring的其他一些很好的信息,这里是Swift中的string

这对我有用,

 var loc = "abcdefghi".rangeOfString("c").location NSLog("%d", loc); 

这也工作了,

 var myRange: NSRange = "abcdefghi".rangeOfString("c") var loc = myRange.location NSLog("%d", loc); 

如果你仔细想想,你实际上并不需要确切的Int版本的位置。 范围,甚至String.Index是足够的,如果需要再次获取子string:

 let myString = "hello" let rangeOfE = myString.rangeOfString("e") if let rangeOfE = rangeOfE { myString.substringWithRange(rangeOfE) // e myString[rangeOfE] // e // if you do want to create your own range // you can keep the index as a String.Index type let index = rangeOfE.startIndex myString.substringWithRange(Range<String.Index>(start: index, end: advance(index, 1))) // e // if you really really need the // Int version of the index: let numericIndex = distance(index, advance(index, 1)) // 1 (type Int) } 

最简单的方法是:

Swift 3

  var textViewString:String = "HelloWorld2016" guard let index = textViewString.characters.index(of: "W") else { return } let mentionPosition = textViewString.distance(from: index, to: textViewString.endIndex) print(mentionPosition) 

如果你想知道string中一个字符位置是一个int值,使用:

 let loc = newString.range(of: ".").location 

string是NSString的桥梁types,所以添加

 import Cocoa 

到你的swift文件并使用所有的“旧”方法。

与Objective-C中的NSString相比,Swift中的variablestypesstring包含不同的函数。 正如苏丹所提到的那样,

Swiftstring不实现RandomAccessIndex

你可以做的是将你的Stringtypes的variables向下转换为NSString(这在Swift中是有效的)。 这将使您能够访问NSString中的函数。

 var str = "abcdefghi" as NSString str.rangeOfString("c").locationx // returns 2 

就思维而言,这可能被称为“反转”。 你发现世界是圆的,而不是平坦的。 “你不需要知道angular色的指数就可以做到。” 而作为一个C程序员,我发现这很难接受! 你的行“let index = letters.characters.indexOf(”c“)!” 本身就够了。 例如,要删除C你可以使用…(游乐场贴)

  var letters = "abcdefg" //let index = letters.rangeOfString("c")!.startIndex //is the same as let index = letters.characters.indexOf("c")! range = letters.characters.indexOf("c")!...letters.characters.indexOf("c")! letters.removeRange(range) letters 

然而,如果你想要一个索引,你需要返回一个实际的INDEX而不是一个Int作为一个Int值,这将需要额外的步骤来实际使用。 这些扩展返回一个索引,一个特定字符的数量,以及这个操作系统可插入代码将演示的范围。

 extension String { public func firstIndexOfCharacter(aCharacter: Character) -> String.CharacterView.Index? { for index in self.characters.indices { if self[index] == aCharacter { return index } } return nil } public func returnCountOfThisCharacterInString(aCharacter: Character) -> Int? { var count = 0 for letters in self.characters{ if aCharacter == letters{ count++ } } return count } public func rangeToCharacterFromStart(aCharacter: Character) -> Range<Index>? { for index in self.characters.indices { if self[index] == aCharacter { let range = self.startIndex...index return range } } return nil } } var MyLittleString = "MyVery:important String" var theIndex = MyLittleString.firstIndexOfCharacter(":") var countOfColons = MyLittleString.returnCountOfThisCharacterInString(":") var theCharacterAtIndex:Character = MyLittleString[theIndex!] var theRange = MyLittleString.rangeToCharacterFromStart(":") MyLittleString.removeRange(theRange!) 

如果你正在寻找简单的方法来获得字符或string结帐索引这个库http://www.dollarswift.org/#indexof-char-character-int

您可以使用另一个string或正则expression式从一个string中获取indexOf

使用Swift 2获取string中的子串索引:

 let text = "abc" if let range = text.rangeOfString("b") { var index: Int = text.startIndex.distanceTo(range.startIndex) ... } 

在迅速2.0

 var stringMe="Something In this.World" var needle="." if let idx = stringMe.characters.indexOf(needle) { let pos=stringMe.substringFromIndex(idx) print("Found \(needle) at position \(pos)") } else { print("Not found") } 
 let mystring:String = "indeep"; let findCharacter:Character = "d"; if (mystring.characters.contains(findCharacter)) { let position = mystring.characters.indexOf(findCharacter); NSLog("Position of c is \(mystring.startIndex.distanceTo(position!))") } else { NSLog("Position of c is not found"); } 

我玩跟随

 extension String { func allCharactes() -> [Character] { var result: [Character] = [] for c in self.characters { result.append(c) } return } } 

直到我明白所提供的现在它只是字符数组

 let c = Array(str.characters) 

如果你只需要一个字符的索引,那么最简单,快速的解决scheme(正如Pascal已经指出的那样)是:

 let index = string.characters.index(of: ".") let intIndex = string.distance(from: string.startIndex, to: index) 

关于将String.Index转换为Int ,这个扩展适用于我:

 public extension Int { /// Creates an `Int` from a given index in a given string /// /// - Parameters: /// - index: The index to convert to an `Int` /// - string: The string from which `index` came init(_ index: String.Index, in string: String) { self.init(string.distance(from: string.startIndex, to: index)) } } 

与此问题相关的示例用法:

 var testString = "abcdefg" Int(testString.range(of: "c")!.lowerBound, in: testString) // 2 testString = "🇨🇦🇺🇸🇩🇪👩‍👩‍👧‍👦\u{1112}\u{1161}\u{11AB}" Int(testString.range(of: "🇨🇦🇺🇸🇩🇪")!.lowerBound, in: testString) // 0 Int(testString.range(of: "👩‍👩‍👧‍👦")!.lowerBound, in: testString) // 1 Int(testString.range(of: "한")!.lowerBound, in: testString) // 5 

重要:

正如你所看到的,它将扩展的字形聚类分组并join与String.Index不同的字符。 当然,这就是为什么我们有String.Index 。 你应该记住,这种方法认为群是单数字,这是接近正确的。 如果你的目标是通过Unicode代码分割一个string,这不是你的解决scheme。

Swift 2.0中 ,下面的函数在给定的字符之前返回一个子string。

 func substring(before sub: String) -> String { if let range = self.rangeOfString(sub), let index: Int = self.startIndex.distanceTo(range.startIndex) { return sub_range(0, index) } return "" } 

Swift 3

 extension String { func substring(from:String) -> String { let searchingString = from let rangeOfSearchingString = self.range(of: searchingString)! let indexOfSearchingString: Int = self.distance(from: self.startIndex, to: rangeOfSearchingString.upperBound ) let trimmedString = self.substring(start: indexOfSearchingString , end: self.count) return trimmedString } }