如何使用 Swift NSRegularExpression 获取大写字母



我有一个这样的字符串:

"te_st"并且喜欢将all underscores followed by a character替换为此字符的大写版本。


"te_st" --> 找到 (正则表达式: "_." ( --------替换为下一个字符 (+大写 ( "s" -> "S" (--------> "teSt"


"te_st" --->到"teSt"
"_he_l_lo" --->到"HeLLo"
"an_o_t_h_er_strin_g" --->到"anOTHErStrinG"

。但我不能像这个小截图那样使用 Swift 的NSRegularExpression真正让它工作:

var result = "te_st" // result should be teSt
result = try! NSRegularExpression(pattern: "_*").stringByReplacingMatches(in: result, range: NSRange(0..<result.count), withTemplate: ("$1".uppercased()))

没有常规语法将匹配项转换为大写。您发布的代码正在尝试将字符串$1转换为大写,这当然只是$1。它不会尝试在运行时转换$1匹配项表示的值。

这是另一种使用正则表达式查找后跟小写字母的_的方法。这些被枚举并替换为大写字母。

extension String {
    func toCamelCase() -> String {
        let expr = try! NSRegularExpression(pattern: "_([a-z])")
        var res = self
        for match in expr.matches(in: self, range: NSRange(0..<res.count)).reversed() {
            let range = Range(match.range, in: self)!
            let letterRange = Range(match.range(at: 1), in: self)!
            res.replaceSubrange(range, with: self[letterRange].uppercased())
        }
        return res
    }
}
print("te_st".toCamelCase())
print("_he_l_lo".toCamelCase())
print("an_o_t_h_er_strin_g".toCamelCase())

这输出:

测试
你好
anOtheRStrinG

下面是一个使用 NSRegularExpression 的实现。我使用组匹配来获取 _ 之后的字符并将其大写并替换字符串。

func capitalizeLetterAfterUnderscore(string: String) -> String {
  var capitalizedString = string
  guard let regularExpression = try?  NSRegularExpression(pattern: "_(.)") else {
                                                            return capitalizedString
  }
  let matches = regularExpression.matches(in: string,
                                          options: .reportCompletion,
                                          range: NSMakeRange(0, string.count))
  for match in matches {
      let groupRange = match.range(at: 1)
      let index = groupRange.location
      let characterIndex = string.index(string.startIndex,
                                          offsetBy: index)
      let range = characterIndex ... characterIndex
      let capitalizedCharacter = String(capitalizedString[characterIndex]).capitalized
      capitalizedString = capitalizedString.replacingCharacters(in: range,
                                                                with: capitalizedCharacter)
  }
  capitalizedString = capitalizedString.replacingOccurrences(of: "_", with: "")
  return capitalizedString
}

capitalizeLetterAfterUnderscore(string: "an_o_t_h_er_strin_g") // anOTHErStrinG

这是另一个不使用正则表达式的。我扩展了也可以重用的方法。

extension String {
  func indexes(of character: String) -> [Index] {
    precondition(character.count == 1, "character should be single letter string")
    return enumerated().reduce([]) { (partial, component) in
      let currentIndex = index(startIndex,
                               offsetBy: component.offset)
      return String(self[currentIndex]) == character
                                  ? partial + [currentIndex]
                                  : partial
    }
  }
  func capitalizeLetter(after indexes: [Index]) -> String {
    var modifiedString = self
    for currentIndex in indexes {
      guard let letterIndex = index(currentIndex,
                                           offsetBy: 1,
                                           limitedBy: endIndex)
        else { continue }
      let range = letterIndex ... letterIndex
      modifiedString = modifiedString.replacingCharacters(in: range,
                                                          with: self[range].capitalized)
    }
    return modifiedString
  }
}
let string = "an_o_t_h_er_strin_g"
let newString = string.capitalizeLetter(after: string.indexes(of: "_"))
                      .replacingOccurrences(of: "_",with: "")

您可以将字符串range(of:, options:, range:)方法与.regularExpression选项一起使用,以匹配"_[a-z]"的出现次数,并将迭代以相反顺序找到的范围的子范围替换为范围下限大写后的索引处的字符:

let string = "an_o_t_h_er_strin_g"
let regex = "_[a-z]"
var start = string.startIndex
var ranges:[Range<String.Index>] = []
while let range = string.range(of: regex, options: .regularExpression, range: start..<string.endIndex) {
    start = range.upperBound
    ranges.append(range)
}
var finalString = string
for range in ranges.reversed() {
    finalString.replaceSubrange(range, with: String(string[string.index(after: range.lowerBound)]).uppercased())
}
print(finalString)   // "anOTHErStrinGn"
<</div> div class="one_answers">

问题是它将字符串"$1"转换为大写(不出所料,这只是"$1"(,并使用"$1"作为模板。如果要使用正则表达式,则必须自己枚举匹配项。

另一种方法是将字符串拆分为_个字符,并将每个子字符串的第一个字符(第一个除外(大写,并使用 reduce 将其重新连接在一起:

let input = "te_st"
let output = input.components(separatedBy: "_").enumerated().reduce("") { $0 + ($1.0 == 0 ? $1.1 : $1.1.uppercasedFirst()) }

或者,如果您的目标不是编写像大多数正则表达式那样神秘的代码,我们可以使其更清晰:

let output = input
    .components(separatedBy: "_")
    .enumerated()
    .reduce("") { result, current in
        if current.offset == 0 {
            return current.element      // because you don’t want the first component capitalized
        } else {
            return result + current.element.uppercasedFirst()
        }
}

结果是:

测试

请注意,使用此扩展名将第一个字符大写:

extension String {
    func uppercasedFirst(with locale: Locale? = nil) -> String {
        guard count > 0 else { return self }
        return String(self[startIndex]).uppercased(with: locale) + self[index(after: startIndex)...]
    }
}
如果你想

NSRegularExpression进行动态转换,你可以NSRegularExpression子类并覆盖replacementString(for:in:offset:template:)

class ToCamelRegularExpression: NSRegularExpression {
    override func replacementString(for result: NSTextCheckingResult, in string: String, offset: Int, template templ: String) -> String {
        if let range = Range(result.range(at: 1), in: string) {
            return string[range].uppercased()
        } else {
            return super.replacementString(for: result, in: string, offset: 0, template: templ)
        }
    }
}
func toCamelCase(_ input: String) -> String { //Make this a String extension if you prefer...
    let regex = try! ToCamelRegularExpression(pattern: "_(.)")
    return regex.stringByReplacingMatches(in: input, options: [], range: NSRange(0..<input.utf16.count), withTemplate: "$1")
}
print(toCamelCase("te_st")) //-> teSt
print(toCamelCase("_he_l_lo")) //-> HeLLo
print(toCamelCase("an_o_t_h_er_strin_g")) //-> anOTHErStrinG

最新更新