如何使用正则表达式拆分字符串

时间:2017-02-27 01:24:16

标签: regex swift string split swift3

我有一个字符串"323 ECO Economics Course 451 ENG English Course 789 Mathematical Topography"我想使用正则表达式[0-9][0-9][0-9][A-Z][A-Z][A-Z]拆分此字符串,以便该函数返回数组:

Array = 
["323 ECO Economics Course ", "451 ENG English Course",  "789 Mathematical Topography"]

我如何使用swift进行此操作?

修改 我的问题与链接的问题不同。我意识到您可以使用myString.components(separatedBy: "splitting string")在swift中拆分字符串。问题是该问题没有解决如何使splitting string成为正则表达式的问题。我尝试使用mystring.components(separatedBy: "[0-9][0-9][0-9][A-Z][A-Z][A-Z]", options: .regularExpression),但这不起作用。

如何使separatedBy:部分成为正则表达式?

3 个答案:

答案 0 :(得分:4)

您可以使用正则表达式"\\b[0-9]{1,}[a-zA-Z ]{1,}"和此answer中的此扩展名来使用literal,caseInsensitive或regularExpression搜索获取字符串的所有范围:

extension String {
    func ranges(of string: String, options: CompareOptions = .literal) -> [Range<Index>] {
        var result: [Range<Index>] = []
        var start = startIndex
        while let range = range(of: string, options: options, range: start..<endIndex) {
            result.append(range)
            start = range.lowerBound < range.upperBound ? range.upperBound : index(range.lowerBound, offsetBy: 1, limitedBy: endIndex) ?? endIndex
        }
        return result
    }
}
let inputString = "323 ECO Economics Course 451 ENG English Course 789 Mathematical Topography"

let courses = inputString.ranges(of: "\\b[0-9]{1,}[a-zA-Z ]{1,}", options: .regularExpression).map{inputString[$0] }

print(courses)   //   ["323 ECO Economics Course ", "451 ENG English Course ", "789 Mathematical Topography"]

答案 1 :(得分:2)

Swift目前还没有原生正则表达式。但Foundation提供了NSRegularExpression

import Foundation

let toSearch = "323 ECO Economics Course 451 ENG English Course 789 MAT Mathematical Topography"

let pattern = "[0-9]{3} [A-Z]{3}"
let regex = try! NSRegularExpression(pattern: pattern, options: [])

// NSRegularExpression works with objective-c NSString, which are utf16 encoded
let matches = regex.matches(in: toSearch, range: NSMakeRange(0, toSearch.utf16.count))

// the combination of zip, dropFirst and map to optional here is a trick
// to be able to map on [(result1, result2), (result2, result3), (result3, nil)]
let results = zip(matches, matches.dropFirst().map { Optional.some($0) } + [nil]).map { current, next -> String in
  let range = current.rangeAt(0)
  let start = String.UTF16Index(range.location)
  // if there's a next, use it's starting location as the ending of our match
  // otherwise, go to the end of the searched string
  let end = next.map { $0.rangeAt(0) }.map { String.UTF16Index($0.location) } ?? String.UTF16Index(toSearch.utf16.count)

  return String(toSearch.utf16[start..<end])!
}

dump(results)

运行它将输出

▿ 3 elements
  - "323 ECO Economics Course "
  - "451 ENG English Course "
  - "789 MAT Mathematical Topography"

答案 2 :(得分:0)

我需要这样的东西,应该更像 JS String.prototype.split(pat: RegExp) 或 Rust 的 String.splitn(pat: Pattern<'a>) 但使用 Regex。我结束了这个

extension NSRegularExpression {
    convenience init(_ pattern: String) {...}
    
    
    /// An array of substring of the given string, separated by this regular expression, restricted to returning at most n items.
    /// If n substrings are returned, the last substring (the nth substring) will contain the remainder of the string.
    /// - Parameter str: String to be matched
    /// - Parameter n: If `n` is specified and n != -1, it will be split into n elements else split into all occurences of this pattern
    func splitn(_ str: String, _ n: Int = -1) -> [String] {
        let range = NSRange(location: 0, length: str.utf8.count)
        let matches = self.matches(in: str, range: range);
        
        var result = [String]()
        if n != -1 && n < 2 { return [str] }
        
        if let first = matches.first?.range {
            if first.location == 0 { result.append("") }
            if first.location != 0 {
                let _range = NSRange(location: 0, length: first.location)
                result.append(String(str[Range(_range, in: str)!]))
            }
        }
        
        for (cur, next) in zip(matches, matches[1...]) {
            let loc = cur.range.location + cur.range.length
            if n != -1 && result.count + 1 == n {
                let _range = NSRange(location: loc, length: str.utf8.count - loc)
                result.append(String(str[Range(_range, in: str)!]))
                return result
                
            }
            let len = next.range.location - loc
            let _range = NSRange(location: loc, length: len)
            result.append(String(str[Range(_range, in: str)!]))
        }
        
        if let last = matches.last?.range, !(n != -1 && result.count >= n) {
            let lastIndex = last.length + last.location
            if lastIndex == str.utf8.count { result.append("") }
            if lastIndex < str.utf8.count {
                let _range = NSRange(location: lastIndex, length: str.utf8.count - lastIndex)
                result.append(String(str[Range(_range, in: str)!]))
            }
        }
        
        return result;
    }
    
}

通过以下测试

func testRegexSplit() {
        XCTAssertEqual(NSRegularExpression("\\s*[.]\\s+").splitn("My . Love"), ["My", "Love"])
        XCTAssertEqual(NSRegularExpression("\\s*[.]\\s+").splitn("My . Love . "), ["My", "Love", ""])
        XCTAssertEqual(NSRegularExpression("\\s*[.]\\s+").splitn(" . My . Love"), ["", "My", "Love"])
        XCTAssertEqual(NSRegularExpression("\\s*[.]\\s+").splitn(" . My . Love . "), ["", "My", "Love", ""])
        XCTAssertEqual(NSRegularExpression("xX").splitn("xXMyxXxXLovexX"), ["", "My", "", "Love", ""])
    }



func testRegexSplitWithN() {
        XCTAssertEqual(NSRegularExpression("xX").splitn("xXMyxXxXLovexX", 1), ["xXMyxXxXLovexX"])
        XCTAssertEqual(NSRegularExpression("xX").splitn("xXMyxXxXLovexX", -1), ["", "My", "", "Love", ""])
        XCTAssertEqual(NSRegularExpression("xX").splitn("xXMyxXxXLovexX", 2), ["", "MyxXxXLovexX"])
        XCTAssertEqual(NSRegularExpression("xX").splitn("xXMyxXxXLovexX", 3), ["", "My", "xXLovexX"])
        XCTAssertEqual(NSRegularExpression("xX").splitn("xXMyxXxXLovexX", 4), ["", "My", "", "LovexX"])
    }