swift的字符串操作自从3开始就变的麻烦,文章Swift 3.0 String中列举了一下swift3的String的用法。 3之后String引入了String.Index类型,编辑操作就变得麻烦了,要操作String就必须通过相关的String.Index,不能直接通过Int下标来操作了。 于是就通过extension给String添加方法让其操作也能像数组一样直接用下标操作 ## 首先 将`String`实现`NameSpace`协议,然后通过`string.dvt`访问扩展的功能方法,减少和其它的三方冲突概率。`NameSpace`协议详见[通过swift的协议实现C++的命名空间效果](http://blog.tcoding.cn/iOS/swiftspace.html) ```swift extension String: NameSpace { } public extension BaseWrapper where DT == String { // ··· } ``` ### 获取一个`Range` 不少字符串操作要先得到一个`Range`对象,所以先实现快捷获取`Range`的方法 ``` swift /// 获取从`start`开始到`end`结束的`Range` /// /// 如果`start`或`end`不在范围内直接返回nil /// /// - Parameters: /// - start: 开始的位置 /// - end: 结束的位置 /// - Returns: 返回结果 func range(_ start: Int, to end: Int) -> Range? { guard start >= 0, end >= start, end < self.base.count else { return nil } return Range(NSRange(location: start, length: end - start + 1), in: self.base) } ``` 用法: ``` swift let string = "0123456789" let range = string.dvt.range(start, to: end) // [start ... end] ``` > 注:所有的扩展方法都做了防止越界处理,当传入的参数非法的时候会返回一个空字符串或`nil` ### 通过下标获取字符串的子串 `subscript`方法可以通过`[]`来调用 ``` swift /// 获取从`start`开始到`end`结束的字符串 /// /// 首先判断`start`和`end`的合法性,`end >= 0 && end >= start && start < self.base.count`; /// 然后做越界处理得到新的`newStart`和`newEnd`,通过`newEnd - newStart + 1`来获取目标字符串长度; /// 利用`NSRange`初始化一个`Range`; /// 然后通过`Range`拿到目标字符串 /// /// - Parameters: /// - start: 开始的位置 /// - end: 结束的位置 /// - Returns: 返回结果 subscript(_ start: Int, to end: Int) -> String { guard end >= 0, end >= start, start < self.base.count else { return "" } let newStart = max(0, start) let newEnd = min(end, self.base.count - 1) guard newEnd >= newStart, let range = self.range(newStart, to: newEnd), !range.isEmpty else { return "" } return "\(self.base[range])" } /// 获取从`start`开始到`end`字符串出现的位置的字符串 /// /// 首先利用components获取新的字符串; /// 然后新字符串长度减去`start`得到目标字符串长度; /// 通过`suffix(length)`获取目标字符串 /// /// - Parameters: /// - start: 开始的位置 /// - end: 结束的位置 /// - Returns: 返回结果 subscript(_ start: Int, to end: String) -> String { let string = self.base.components(separatedBy: end).first ?? self.base let length = max(0, string.count - start) if length == 0 { return "" } return "\(string.suffix(length))" } /// 获取从`start`开始的`count`长度的字符串 /// /// 如果`start`不在范围内或者`count <= 0`,返回一个空字符串 /// 否则计算`end`的位置调用`subscript(_:to:)`获取 /// /// - Parameters: /// - start: 开始的位置 /// - count: 长度 /// - Returns: 返回结果 subscript(_ start: Int, length count: Int) -> String { guard count > 0, start < self.base.count else { return "" } let newStart = max(0, start) return self[newStart, to: newStart + count - 1] } /// 获取`index`位置的字符串 /// /// 利用`index(_:offsetBy:)`获取到指定位置的`String.Index`,然后直接通过下标获取结果 /// 如果`index`不在范围内,返回一个空字符串 /// /// - Parameters: /// - index: 指定的位置 /// - Returns: 返回结果 /// - Complexity: O(*n*) subscript(_ index: Int) -> String { guard index >= 0, index < self.base.count else { return "" } return "\(self.base[String.Index(utf16Offset: index, in: self.base)])" } ``` ### 插入和替换 ``` swift /// 将字符串插入到`index` /// /// 从`index`位置将原字符串分割为`left`和`right`,然后拼接成新的字符串。 /// 如果`index`不在范围内,会插入到最前面(`index < 0`)或最后面(`index >= count`) /// /// - Parameters: /// - newElement: 需要插入的字符串结果 /// - index: 需要插入的位置,已经做了越界处理 /// - Returns: 返回结果 /// - Complexity: O(*n*),主要是确定index的位置需要的时间 @discardableResult func insert(_ newElement: String, at index: Int) -> String { let newIndex = min(max(0, index), self.base.count) let left = self.base.prefix(newIndex) let right = self.base.suffix(from: self.base.index(self.base.startIndex, offsetBy: newIndex)) return left + newElement + right } /// 字符串替换 /// - Parameters: /// - start: 需要替换的字符串起始位置 /// - end: 需要替换的字符串结束位置 /// - replacement: 替换字符串 /// - Returns: 替换后的字符串 func replace(_ start: Int, to end: Int, with replacement: String) -> String { var res = self.base if let range = self.range(start, to: end) { res.replaceSubrange(range, with: replacement) } return res } ``` ## 单元测试代码: ``` swift func testString() throws { let str = "0123456789" XCTAssertEqual(str.dvt.insert("abc", at: -3), "abc0123456789") XCTAssertEqual(str.dvt.insert("abc", at: 3), "012abc3456789") XCTAssertEqual(str.dvt.insert("abc", at: 13), "0123456789abc") XCTAssertEqual(str.dvt[-1], "") XCTAssertEqual(str.dvt[1], "1") XCTAssertEqual(str.dvt[100], "") XCTAssertEqual(str.dvt[-1, length: -10], "") XCTAssertEqual(str.dvt[-1, length: 0], "") XCTAssertEqual(str.dvt[-1, length: 3], "012") XCTAssertEqual(str.dvt[-1, length: 12], "0123456789") XCTAssertEqual(str.dvt[1, length: -10], "") XCTAssertEqual(str.dvt[1, length: 0], "") XCTAssertEqual(str.dvt[1, length: 3], "123") XCTAssertEqual(str.dvt[1, length: 12], "123456789") XCTAssertEqual(str.dvt[10, length: -10], "") XCTAssertEqual(str.dvt[10, length: 0], "") XCTAssertEqual(str.dvt[10, length: 3], "") XCTAssertEqual(str.dvt[10, length: 12], "") XCTAssertEqual(str.dvt[-1, to: -2], "") XCTAssertEqual(str.dvt[-1, to: 0], "0") XCTAssertEqual(str.dvt[-1, to: 1], "01") XCTAssertEqual(str.dvt[-1, to: 2], "012") XCTAssertEqual(str.dvt[-1, to: 12], "0123456789") XCTAssertEqual(str.dvt[1, to: -2], "") XCTAssertEqual(str.dvt[1, to: 0], "") XCTAssertEqual(str.dvt[1, to: 1], "1") XCTAssertEqual(str.dvt[1, to: 2], "12") XCTAssertEqual(str.dvt[1, to: 12], "123456789") XCTAssertEqual(str.dvt[98, to: -2], "") XCTAssertEqual(str.dvt[8, to: 0], "") XCTAssertEqual(str.dvt[8, to: 1], "") XCTAssertEqual(str.dvt[8, to: 9], "89") XCTAssertEqual(str.dvt[8, to: 12], "89") XCTAssertEqual(str.dvt[9, to: -2], "") XCTAssertEqual(str.dvt[9, to: 0], "") XCTAssertEqual(str.dvt[9, to: 1], "") XCTAssertEqual(str.dvt[9, to: 9], "9") XCTAssertEqual(str.dvt[9, to: 12], "9") XCTAssertEqual(str.dvt[10, to: -2], "") XCTAssertEqual(str.dvt[10, to: 0], "") XCTAssertEqual(str.dvt[10, to: 1], "") XCTAssertEqual(str.dvt[10, to: 10], "") XCTAssertEqual(str.dvt[10, to: 12], "") XCTAssertEqual("18112341234".dvt.replace(3, to: 6, with: "****"), "181****1234") XCTAssertEqual(str.dvt.replace(0, to: 1, with: "**"), "**23456789") XCTAssertEqual("123456789".dvt[2, to: "4"], "3") } ``` ## 最后 该扩展包含在`DVTFoundation`库里面,使用`pod 'DVTFoundation'`导入; 也可以访问`DVTFoundation`的[GitHub](https://github.com/darvintang/DVTFoundation)仓库查看。
没有评论