将CamelCase字符串分隔为Swift中以空格分隔的单词

Bri*_*old 15 string macos camelcasing ios swift

我想将一个CamelCase字符串分隔成一个新字符串中以空格分隔的单词.这是我到目前为止:

var camelCaps: String {
    guard self.count > 0 else { return self }
    var newString: String = ""

    let uppercase = CharacterSet.uppercaseLetters
    let first = self.unicodeScalars.first!
    newString.append(Character(first))
    for scalar in self.unicodeScalars.dropFirst() {
        if uppercase.contains(scalar) {
            newString.append(" ")
        }
        let character = Character(scalar)
        newString.append(character)
    }

    return newString
}

let aCamelCaps = "aCamelCaps"
let camelCapped = aCamelCaps.camelCaps // Produce: "a Camel Caps"

let anotherCamelCaps = "ÄnotherCamelCaps"
let anotherCamelCapped = anotherCamelCaps.camelCaps // "Änother Camel Caps"
Run Code Online (Sandbox Code Playgroud)

我倾向于怀疑这可能不是转换为以空格分隔的单词的最有效方式,如果我称之为紧密循环,或1000次.在Swift中有更有效的方法吗?

[编辑1:]我需要的解决方案对于Unicode标量应保持通用,而不是特定于罗马ASCII"A..Z".

[编辑2:]解决方案也应该跳过第一个字母,即不要在第一个字母前添加一个空格.

[编辑3:]更新了Swift 4语法,并添加了大写字母的缓存,从而提高了非常长的字符串和紧密循环的性能.

Aug*_*P A 11

这里是另一种为Swift 2.x做同样事情的方法

extension String {

    func camelCaseToWords() -> String {

        return unicodeScalars.reduce("") {

            if NSCharacterSet.uppercaseLetterCharacterSet().characterIsMember(uint_least16_t($1.value)) {
                return ($0 + " " + String($1))
            }
            else {

                return ($0 + String($1))
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Swift 3.x

extension String {

    func camelCaseToWords() -> String {

        return unicodeScalars.reduce("") {

            if CharacterSet.uppercaseLetters.contains($1) {

                return ($0 + " " + String($1))
            }
            else {

                return $0 + String($1)
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

可能对某人有帮助:)


OOP*_*Per 8

就我在旧MacBook上测试而言,您的代码似乎对短字符串足够有效:

import Foundation

extension String {

    var camelCaps: String {
        var newString: String = ""

        let upperCase = CharacterSet.uppercaseLetters
        for scalar in self.unicodeScalars {
            if upperCase.contains(scalar) {
                newString.append(" ")
            }
            let character = Character(scalar)
            newString.append(character)
        }

        return newString
    }

    var camelCaps2: String {
        var newString: String = ""

        let upperCase = CharacterSet.uppercaseLetters
        var range = self.startIndex..<self.endIndex
        while let foundRange = self.rangeOfCharacter(from: upperCase,range: range) {
            newString += self.substring(with: range.lowerBound..<foundRange.lowerBound)
            newString += " "
            newString += self.substring(with: foundRange)

            range = foundRange.upperBound..<self.endIndex
        }
        newString += self.substring(with: range)

        return newString
    }

    var camelCaps3: String {
        struct My {
            static let regex = try! NSRegularExpression(pattern: "[A-Z]")
        }
        return My.regex.stringByReplacingMatches(in: self, range: NSRange(0..<self.utf16.count), withTemplate: " $0")
    }
}
let aCamelCaps = "aCamelCaps"

assert(aCamelCaps.camelCaps == aCamelCaps.camelCaps2)
assert(aCamelCaps.camelCaps == aCamelCaps.camelCaps3)

let t0 = Date().timeIntervalSinceReferenceDate

for _ in 0..<1_000_000 {
    let aCamelCaps = "aCamelCaps"

    let camelCapped = aCamelCaps.camelCaps
}

let t1 = Date().timeIntervalSinceReferenceDate
print(t1-t0) //->4.78703999519348

for _ in 0..<1_000_000 {
    let aCamelCaps = "aCamelCaps"

    let camelCapped = aCamelCaps.camelCaps2
}

let t2 = Date().timeIntervalSinceReferenceDate
print(t2-t1) //->10.5831440091133

for _ in 0..<1_000_000 {
    let aCamelCaps = "aCamelCaps"

    let camelCapped = aCamelCaps.camelCaps3
}

let t3 = Date().timeIntervalSinceReferenceDate
print(t3-t2) //->14.2085000276566
Run Code Online (Sandbox Code Playgroud)

(不要尝试在Playground中测试上面的代码.这些数字来自作为CommandLine应用程序执行的单个试验.)


小洋粉*_*小洋粉 8

extension String {
    func titlecased() -> String {
        return self
            .replacingOccurrences(of: "([a-z])([A-Z](?=[A-Z])[a-z]*)", with: "$1 $2", options: .regularExpression)
            .replacingOccurrences(of: "([A-Z])([A-Z][a-z])", with: "$1 $2", options: .regularExpression)
            .replacingOccurrences(of: "([a-z])([A-Z][a-z])", with: "$1 $2", options: .regularExpression)
            .replacingOccurrences(of: "([a-z])([A-Z][a-z])", with: "$1 $2", options: .regularExpression)
    }
}
Run Code Online (Sandbox Code Playgroud)

 "ThisStringHasNoSpacesButItDoesHaveCapitals"
 "IAmNotAGoat"
 "LOLThatsHilarious!"
 "ThisIsASMSMessage"
Run Code Online (Sandbox Code Playgroud)

出去

"This String Has No Spaces But It Does Have Capitals" 
"I Am Not A Goat" 
"LOL Thats Hilarious!" 
"This Is ASMS Message" // (Difficult tohandle single letter words when they are next to acronyms.)
Run Code Online (Sandbox Code Playgroud)

在此输入链接描述


mat*_*odv 6

我可能会迟到,但我想对奥古斯丁PA的答案或Leo Dabus的评论分享一些改进。
基本上,如果我们使用upper camel case符号(例如“ DuckDuckGo”),该代码将无法正常工作,因为它将在字符串的开头添加一个空格。
为了解决这个问题,这是使用Swift 3.x进行的代码的稍微修改,并且与大小写兼容:

extension String {

    func camelCaseToWords() -> String {
        return unicodeScalars.reduce("") {
            if CharacterSet.uppercaseLetters.contains($1) {
                if $0.characters.count > 0 {
                    return ($0 + " " + String($1))
                }
            }
            return $0 + String($1)
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Mic*_*ann 5

我可以用更少的代码行(并且没有字符集)来完成此扩展,但是,是的,如果您想在大写字母前面插入空格,您基本上必须枚举每个字符串。

\n
extension String {\n    var differentCamelCaps: String {\n        var newString: String = ""\n        for eachCharacter in self {\n            if "A"..."Z" ~= eachCharacter {\n                newString.append(" ")\n            }\n            newString.append(eachCharacter)\n        }\n        return newString\n    }\n}\n\nprint("\xc3\x84notherCamelCaps".differentCamelCaps) // \xc3\x84nother Camel Caps\n
Run Code Online (Sandbox Code Playgroud)\n

  • `如果 "A"..."Z" ~= 每个字符 {` (2认同)

Ami*_*aiB 5

一线解决方案

我同意@aircraft,正则表达式可以在一个LOC中解决此问题!

// Swift 5 (and probably 4?)
extension String {
    func titleCase() -> String {
        return self
            .replacingOccurrences(of: "([A-Z])",
                                  with: " $1",
                                  options: .regularExpression,
                                  range: range(of: self))
            .trimmingCharacters(in: .whitespacesAndNewlines)
            .capitalized // If input is in llamaCase
    }
}
Run Code Online (Sandbox Code Playgroud)

此JS答案的道具。

PS我有一个要点为snake_case ? CamelCase 在这里

PPS我为New Swift(当前为5.1)更新了此内容,然后看到了@busta的回答,并换了我startIndex..<endIndexrange(of: self)。归功于你们应得的!

  • 谢谢,我很欣赏这个答案,也许它对某些人有用。然而,我的目标不是实现一行代码,而是找到一个更高效、更可维护的实现。regex 解决方案比上面提供的其他解决方案运行得慢,最快的解决方案需要更多的代码行。 (3认同)

bus*_*117 5

一个更好的完整快速解决方案...基于AmitaiB的答案

extension String {
    func titlecased() -> String {
        return self.replacingOccurrences(of: "([A-Z])", with: " $1", options: .regularExpression, range: self.range(of: self))
            .trimmingCharacters(in: .whitespacesAndNewlines)
            .capitalized
    }
}
Run Code Online (Sandbox Code Playgroud)