Регулярное выражение matchInString в Swift 2

toCamel() Функция в этом расширении String должна удалить _ за которым следует символ в середине строки, и замените два символа на верхний регистр.

public extension String {
    public func rangeFromNSRange(aRange: NSRange) -> Range<String.Index> {
        let s = advance(self.startIndex, aRange.location)
        let e = advance(self.startIndex, aRange.location + aRange.length)
        return s..<e
    }
    public var ns : NSString {return self as NSString}
    public subscript (aRange: NSRange) -> String? {
        get {return self.substringWithRange(self.rangeFromNSRange(aRange))}
    }

    public var cdr: String {return isEmpty ? "" : String(characters.dropFirst())}

    public func toCamel() throws -> String {
        var s = self
        let regex = try NSRegularExpression(pattern: "_.", options: [])
        let matches = regex.matchesInString(s, options:[], range:NSMakeRange(0, s.ns.length)).reverse()
        for match in matches {
            print("match = \(s[match.range]!)")
            let matchRange = s.rangeFromNSRange(match.range)
            let uc = s[match.range]!.uppercaseString.cdr
            s.replaceRange(matchRange, with: uc)
        }
        if s.hasPrefix("_") {s = s.cdr}
        return s
    }
}

Дело в том, что matchesInString не возвращает совпадения, где _уже является частью предыдущего матча. И, таким образом, это дает следующие результаты:

try "hello_world".toCamel()    //"helloWorld"

try "hello__world".toCamel()   //"hello_world"

try "hello___world".toCamel()  //"hello_World"

Как обойти это?

3 ответа

Решение

Измените свой шаблон на _+(.), Это соответствует одному или нескольким подчеркиваниям, за которыми следует другой символ. Круглые скобки помещают этого персонажа в группу захвата. Вы можете вернуть захваченного персонажа с помощью rangeAtIndex

public func toCamel() throws -> String {
    var s = self
    let regex = try NSRegularExpression(pattern: "_+(.)", options: [])
    let matches = regex.matchesInString(s, options:[], range:NSMakeRange(0, s.ns.length)).reverse()
    for match in matches {
        print("match = \(s[match.range]!)")
        let matchRange = s.rangeFromNSRange(match.range) // the whole match range
        let replaceRange = match.rangeAtIndex(1)         // range of the capture group
        let uc = s[replaceRange]!.uppercaseString
        s.replaceRange(matchRange, with: uc)
    }
    if s.hasPrefix("_") {s = s.cdr}
    return s
}

Примеры:

"hello_world".toCamel()              // helloWorld
"hello___world".toCamel()            // helloWorld
"hello_beautiful____world".toCamel() // helloBeautifulWolrd

Обновление для Swift 2.1.1

public extension String {

    public func rangeFromNSRange(aRange: NSRange) -> Range<String.Index> {
        let s = self.startIndex.advancedBy(aRange.location)
        let e = self.startIndex.advancedBy(aRange.location + aRange.length)
        return s..<e
    }
    public var ns : NSString {return self as NSString}
    public subscript (aRange: NSRange) -> String? {
        get {return self.substringWithRange(self.rangeFromNSRange(aRange))}
    }

    public var cdr: String {return isEmpty ? "" : String(characters.dropFirst())}

    public func toCamel() throws -> String {
        var s = self
        let regex = try NSRegularExpression(pattern: "_+(.)", options: [])
        let matches = regex.matchesInString(s, options:[], range:NSMakeRange(0, s.ns.length)).reverse()
        for match in matches {
            print("match = \(s[match.range]!)")
            let matchRange = s.rangeFromNSRange(match.range) // the whole match range
            let replaceRange = match.rangeAtIndex(1)         // range of the capture group
            let uc = s[replaceRange]!.uppercaseString
            s.replaceRange(matchRange, with: uc)
        }
        if s.hasPrefix("_") {s = s.cdr}
        return s
    }
}

В вашем коде есть две проблемы.

Во-первых, регулярное выражение, соответствующее вашему требованию, должно быть _+[^_]{1},

Второй в вашем cdr компьютерная собственность, String(characters.dropFirst()) исключить только первое _ персонаж. Вы можете использовать String(characters.suffix(1))

Отредактированный код:

public extension String {
    public func rangeFromNSRange(aRange: NSRange) -> Range<String.Index> {
        let s = advance(self.startIndex, aRange.location)
        let e = advance(self.startIndex, aRange.location + aRange.length)
        return s..<e
    }
    public var ns : NSString {return self as NSString}
    public subscript (aRange: NSRange) -> String? {
        get {return self.substringWithRange(self.rangeFromNSRange(aRange))}
    }

    public var cdr: String {return isEmpty ? "" : String(characters.suffix(1))}

    public func toCamel() throws -> String {
        var s = self
        let regex = try NSRegularExpression(pattern: "_+[^_]{1}", options: [])
        let matches = regex.matchesInString(s, options:[], range:NSMakeRange(0, s.ns.length)).reverse()
        for match in matches {
            print("match = \(s[match.range]!)")
            let matchRange = s.rangeFromNSRange(match.range)
            let uc = s[match.range]!.uppercaseString.cdr
            s.replaceRange(matchRange, with: uc)
        }
        if s.hasPrefix("_") {s = s.cdr}
        return s
    }
}

Я проверил это на детской площадке.

Пытаться NSRegularExpression(pattern: "_+.", options: [])


Вы должны также прочитать Ссылку класса NSRegularExpression.

+ Совпадение 1 или более раз. Матч столько раз, сколько возможно.

Другие вопросы по тегам