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

How to use Swift NSRegularExpression to get uppercased letter?

我有这样的字符串:

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


From "te_st" --> Found (regex: "_.") ------替换为下一个字符 (+ 大写 ("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: ("".uppercased()))

您可以使用带有 .regularExpression 选项的字符串 range(of:, options:, range:) 方法来匹配 "_[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)   // "anOTHErStrinG\n"

这是一个使用 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: "")

没有将匹配项转换为大写的常规语法。您发布的代码试图将字符串 </code> 转换为大写,这当然只是 <code>。它不会尝试在运行时转换由 </code> 匹配项表示的值。</p> <p>这是使用正则表达式查找后跟小写字母的 <code>_ 的另一种方法。这些被枚举并替换为大写字母。

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())

这输出:

teSt
HeLLo
anOTHErStrinG

问题是它将字符串“$1”转换为大写(意料之中的是,它没有变化,只是“$1”)并使用“$1”作为模板。如果要使用正则表达式,则必须自己枚举匹配项。

另一种方法是将字符串拆分为 _ 个字符,并将每个子字符串的第一个字符(第一个字符除外)大写,然后使用 reduce:

将其重新组合在一起
let input = "te_st"

let output = input.components(separatedBy: "_").enumerated().reduce("") { [=10=] + (.0 == 0 ? .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()
        }
}

导致:

teSt

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

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: "")
}
print(toCamelCase("te_st")) //-> teSt
print(toCamelCase("_he_l_lo")) //-> HeLLo
print(toCamelCase("an_o_t_h_er_strin_g")) //-> anOTHErStrinG