在 Swift 中将 CamelCase 字符串分隔成 space 分隔的单词
Separating CamelCase string into space-separated words in Swift
我想在新字符串中将 CamelCase 字符串分隔成 space 分隔的单词。这是我目前所拥有的:
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"
我怀疑这可能不是转换为 space 分隔词的最有效方法,如果我在紧密循环或 1000 次中调用它的话。在 Swift 中有更有效的方法吗?
[编辑 1:] 我需要的解决方案应该对 Unicode 标量保持通用,而不是特定于罗马 ASCII "A..Z"。
[编辑 2:] 解决方案还应该跳过第一个字母,即不在第一个字母前添加 space。
[编辑 3:]更新了 Swift 4 语法,并添加了大写字母缓存,这提高了非常长的字符串和紧密循环的性能。
我可以用更少的代码行(并且没有 CharacterSet)来完成这个扩展,但是,是的,如果你想在大写字母前面插入空格,你基本上必须枚举每个字符串。
extension String {
var differentCamelCaps: String {
var newString: String = ""
for eachCharacter in self {
if "A"..."Z" ~= eachCharacter {
newString.append(" ")
}
newString.append(eachCharacter)
}
return newString
}
}
print("ÄnotherCamelCaps".differentCamelCaps) // Änother Camel Caps
如果你想让它更高效,你可以使用Regular Expressions
。
extension String {
func replace(regex: NSRegularExpression, with replacer: (_ match:String)->String) -> String {
let str = self as NSString
let ret = str.mutableCopy() as! NSMutableString
let matches = regex.matches(in: str as String, options: [], range: NSMakeRange(0, str.length))
for match in matches.reversed() {
let original = str.substring(with: match.range)
let replacement = replacer(original)
ret.replaceCharacters(in: match.range, with: replacement)
}
return ret as String
}
}
let camelCaps = "aCamelCaps" // there are 3 Capital character
let pattern = "[A-Z]"
let regular = try!NSRegularExpression(pattern: pattern)
let camelCapped:String = camelCaps.replace(regex: regular) { " \([=10=])" }
print("Uppercase characters replaced: \(camelCapped)")
据我在我的旧 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: " [=10=]")
}
}
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
(请勿尝试在 Playground 中测试上面的代码。这些数字取自作为 CommandLine 应用程序执行的单个试验。)
extension String {
func camelCaseToWords() -> String {
return unicodeScalars.dropFirst().reduce(String(prefix(1))) {
return CharacterSet.uppercaseLetters.contains()
? [=10=] + " " + String()
: [=10=] + String()
}
}
}
print("ÄnotherCamelCaps".camelCaseToWords()) // Änother Camel Caps
可能对某人有帮助:)
我可能会迟到,但我想分享对 answer or 评论的一点改进。
基本上,如果我们使用 upper camel case
符号(如“DuckDuckGo”),该代码将无法正常工作,因为它会在字符串的开头添加一个 space。
为了解决这个问题,这是代码的一个稍微修改的版本,使用 Swift 3.x,并且它与大小写兼容:
extension String {
func camelCaseToWords() -> String {
return unicodeScalars.reduce("") {
if CharacterSet.uppercaseLetters.contains() {
if [=10=].count > 0 {
return ([=10=] + " " + String())
}
}
return [=10=] + String()
}
}
}
一行解决方案
我同意@aircraft,正则表达式可以在一个LOC中解决这个问题!
// Swift 5 (and probably 4?)
extension String {
func titleCase() -> String {
return self
.replacingOccurrences(of: "([A-Z])",
with: " ",
options: .regularExpression,
range: range(of: self))
.trimmingCharacters(in: .whitespacesAndNewlines)
.capitalized // If input is in llamaCase
}
}
支持 this JS answer。
P.S。我有 snake_case → CamelCase
here.
的要点
P.P.S。我为 New Swift(目前为 5.1)更新了这个,然后看到@busta 的回答,并用我的 startIndex..<endIndex
换成了他的 range(of: self)
。归功于你们!
更好的全速解决方案...基于 AmitaiB 答案
extension String {
func titlecased() -> String {
return self.replacingOccurrences(of: "([A-Z])", with: " ", options: .regularExpression, range: self.range(of: self))
.trimmingCharacters(in: .whitespacesAndNewlines)
.capitalized
}
}
extension String {
func titlecased() -> String {
return self
.replacingOccurrences(of: "([a-z])([A-Z](?=[A-Z])[a-z]*)", with: " ", options: .regularExpression)
.replacingOccurrences(of: "([A-Z])([A-Z][a-z])", with: " ", options: .regularExpression)
.replacingOccurrences(of: "([a-z])([A-Z][a-z])", with: " ", options: .regularExpression)
.replacingOccurrences(of: "([a-z])([A-Z][a-z])", with: " ", options: .regularExpression)
}
}
在
"ThisStringHasNoSpacesButItDoesHaveCapitals"
"IAmNotAGoat"
"LOLThatsHilarious!"
"ThisIsASMSMessage"
出
"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.)
enter link description here
Swift5个解
extension String {
func camelCaseToWords() -> String {
return unicodeScalars.reduce("") {
if CharacterSet.uppercaseLetters.contains() {
if [=10=].count > 0 {
return ([=10=] + " " + String())
}
}
return [=10=] + String()
}
}
}
这是我使用 Unicode 字符 类 得出的结果:(Swift 5)
extension String {
var titleCased: String {
self
.replacingOccurrences(of: "(\p{UppercaseLetter}\p{LowercaseLetter}|\p{UppercaseLetter}+(?=\p{UppercaseLetter}))",
with: " ",
options: .regularExpression,
range: range(of: self)
)
.capitalized
}
}
输出:
fillPath ➝ Fill Path
ThisStringHasNoSpaces ➝ This String Has No Spaces
IAmNotAGoat ➝ I Am Not A Goat
LOLThatsHilarious! ➝ Lol Thats Hilarious!
ThisIsASMSMessage ➝ This Is Asms Message
Swift方式:
extension String {
var titlecased: String {
map { ([=10=].isUppercase ? " " : "") + String([=10=]) }
.joined(separator: "")
.trimmingCharacters(in: .whitespaces)
}
}
Swift 5+
对之前答案的小风格改进
import Foundation
extension String {
func camelCaseToWords() -> String {
unicodeScalars.reduce("") {
guard CharacterSet.uppercaseLetters.contains(),
[=10=].count > 0
else { return [=10=] + String() }
return ([=10=] + " " + String())
}
}
}
通常建议使用 guard let
语句,因为它们为不匹配的情况提供“提前退出”并减少代码的整体嵌套级别(这通常会大大提高可读性......记住,可读性很重要!)
我想在新字符串中将 CamelCase 字符串分隔成 space 分隔的单词。这是我目前所拥有的:
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"
我怀疑这可能不是转换为 space 分隔词的最有效方法,如果我在紧密循环或 1000 次中调用它的话。在 Swift 中有更有效的方法吗?
[编辑 1:] 我需要的解决方案应该对 Unicode 标量保持通用,而不是特定于罗马 ASCII "A..Z"。
[编辑 2:] 解决方案还应该跳过第一个字母,即不在第一个字母前添加 space。
[编辑 3:]更新了 Swift 4 语法,并添加了大写字母缓存,这提高了非常长的字符串和紧密循环的性能。
我可以用更少的代码行(并且没有 CharacterSet)来完成这个扩展,但是,是的,如果你想在大写字母前面插入空格,你基本上必须枚举每个字符串。
extension String {
var differentCamelCaps: String {
var newString: String = ""
for eachCharacter in self {
if "A"..."Z" ~= eachCharacter {
newString.append(" ")
}
newString.append(eachCharacter)
}
return newString
}
}
print("ÄnotherCamelCaps".differentCamelCaps) // Änother Camel Caps
如果你想让它更高效,你可以使用Regular Expressions
。
extension String {
func replace(regex: NSRegularExpression, with replacer: (_ match:String)->String) -> String {
let str = self as NSString
let ret = str.mutableCopy() as! NSMutableString
let matches = regex.matches(in: str as String, options: [], range: NSMakeRange(0, str.length))
for match in matches.reversed() {
let original = str.substring(with: match.range)
let replacement = replacer(original)
ret.replaceCharacters(in: match.range, with: replacement)
}
return ret as String
}
}
let camelCaps = "aCamelCaps" // there are 3 Capital character
let pattern = "[A-Z]"
let regular = try!NSRegularExpression(pattern: pattern)
let camelCapped:String = camelCaps.replace(regex: regular) { " \([=10=])" }
print("Uppercase characters replaced: \(camelCapped)")
据我在我的旧 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: " [=10=]")
}
}
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
(请勿尝试在 Playground 中测试上面的代码。这些数字取自作为 CommandLine 应用程序执行的单个试验。)
extension String {
func camelCaseToWords() -> String {
return unicodeScalars.dropFirst().reduce(String(prefix(1))) {
return CharacterSet.uppercaseLetters.contains()
? [=10=] + " " + String()
: [=10=] + String()
}
}
}
print("ÄnotherCamelCaps".camelCaseToWords()) // Änother Camel Caps
可能对某人有帮助:)
我可能会迟到,但我想分享对
基本上,如果我们使用 upper camel case
符号(如“DuckDuckGo”),该代码将无法正常工作,因为它会在字符串的开头添加一个 space。
为了解决这个问题,这是代码的一个稍微修改的版本,使用 Swift 3.x,并且它与大小写兼容:
extension String {
func camelCaseToWords() -> String {
return unicodeScalars.reduce("") {
if CharacterSet.uppercaseLetters.contains() {
if [=10=].count > 0 {
return ([=10=] + " " + String())
}
}
return [=10=] + String()
}
}
}
一行解决方案
我同意@aircraft,正则表达式可以在一个LOC中解决这个问题!
// Swift 5 (and probably 4?)
extension String {
func titleCase() -> String {
return self
.replacingOccurrences(of: "([A-Z])",
with: " ",
options: .regularExpression,
range: range(of: self))
.trimmingCharacters(in: .whitespacesAndNewlines)
.capitalized // If input is in llamaCase
}
}
支持 this JS answer。
P.S。我有 snake_case → CamelCase
here.
P.P.S。我为 New Swift(目前为 5.1)更新了这个,然后看到@busta 的回答,并用我的 startIndex..<endIndex
换成了他的 range(of: self)
。归功于你们!
更好的全速解决方案...基于 AmitaiB 答案
extension String {
func titlecased() -> String {
return self.replacingOccurrences(of: "([A-Z])", with: " ", options: .regularExpression, range: self.range(of: self))
.trimmingCharacters(in: .whitespacesAndNewlines)
.capitalized
}
}
extension String {
func titlecased() -> String {
return self
.replacingOccurrences(of: "([a-z])([A-Z](?=[A-Z])[a-z]*)", with: " ", options: .regularExpression)
.replacingOccurrences(of: "([A-Z])([A-Z][a-z])", with: " ", options: .regularExpression)
.replacingOccurrences(of: "([a-z])([A-Z][a-z])", with: " ", options: .regularExpression)
.replacingOccurrences(of: "([a-z])([A-Z][a-z])", with: " ", options: .regularExpression)
}
}
在
"ThisStringHasNoSpacesButItDoesHaveCapitals"
"IAmNotAGoat"
"LOLThatsHilarious!"
"ThisIsASMSMessage"
出
"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.)
enter link description here
Swift5个解
extension String {
func camelCaseToWords() -> String {
return unicodeScalars.reduce("") {
if CharacterSet.uppercaseLetters.contains() {
if [=10=].count > 0 {
return ([=10=] + " " + String())
}
}
return [=10=] + String()
}
}
}
这是我使用 Unicode 字符 类 得出的结果:(Swift 5)
extension String {
var titleCased: String {
self
.replacingOccurrences(of: "(\p{UppercaseLetter}\p{LowercaseLetter}|\p{UppercaseLetter}+(?=\p{UppercaseLetter}))",
with: " ",
options: .regularExpression,
range: range(of: self)
)
.capitalized
}
}
输出:
fillPath ➝ Fill Path ThisStringHasNoSpaces ➝ This String Has No Spaces IAmNotAGoat ➝ I Am Not A Goat LOLThatsHilarious! ➝ Lol Thats Hilarious! ThisIsASMSMessage ➝ This Is Asms Message
Swift方式:
extension String {
var titlecased: String {
map { ([=10=].isUppercase ? " " : "") + String([=10=]) }
.joined(separator: "")
.trimmingCharacters(in: .whitespaces)
}
}
Swift 5+
对之前答案的小风格改进
import Foundation
extension String {
func camelCaseToWords() -> String {
unicodeScalars.reduce("") {
guard CharacterSet.uppercaseLetters.contains(),
[=10=].count > 0
else { return [=10=] + String() }
return ([=10=] + " " + String())
}
}
}
通常建议使用 guard let
语句,因为它们为不匹配的情况提供“提前退出”并减少代码的整体嵌套级别(这通常会大大提高可读性......记住,可读性很重要!)