【问题标题】:How to use Swift NSRegularExpression to get uppercased letter?如何使用 Swift NSRegularExpression 获取大写字母?
【发布时间】:2018-06-17 18:14:03
【问题描述】:

我有一个这样的字符串:

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

【问题讨论】:

    标签: swift nsregularexpression


    【解决方案1】:

    您可以使用字符串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)   // "anOTHErStrinG\n"
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 工作 - 看起来不错。谢谢桑迪普
      • String.Index 在 String 扩展方法中是多余的。 func indexes(of character: String) -&gt; [Index]
      • 并且Index有一个特定的方法来偏移1个位置,称为index(after:)index(after: currentIndex)
      • @LeoDabus 您看到的任何其他内容都可以改进。请写出来,我可以借鉴:)
      • 如果您使用index(, offsetBy:, limitedBy: )使用字符串endIndex作为限制,您可以删除if indexOfLetterAfter &gt;= endIndex { continue }。它返回一个可选索引
      【解决方案3】:

      没有将匹配转换为大写的常规语法。您发布的代码试图将字符串$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())
      

      这个输出:

      测试
      你好
      另一个字符串

      【讨论】:

      • 但在苹果正则表达式的文档中,“$n”将被结果替换。在这种情况下,$1 将被第一个匹配项及其字符串替换。那么为什么我们不能使用字符串上的方法?
      • 不需要为选项传递一个空数组,因为它已经是两种情况下的默认值
      • @MuhammadZohaibEhsan 因为要求不是简单的替换。替换需要转换为大写。使用简单的正则表达式无法做到这一点。
      • 所以我们不能在模板上使用方法。替换字符串应该用作模板。结果不能被操作?
      • @MuhammadZohaibEhsan 不,没有简单的语法来操作模板结果。这就是 OP 试图通过使用 uppercased 来做的事情。
      【解决方案4】:

      问题在于它将字符串“$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)...]
          }
      }
      

      【讨论】:

        【解决方案5】:

        如果您想使用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
        

        【讨论】:

          猜你喜欢
          • 2017-03-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-09-12
          • 1970-01-01
          相关资源
          最近更新 更多