Related
I know that NSRegularExpression works on Unicode code points and (normal) JavaScript regex works on UTF-16 code units, but I don't know what should I change in my regex.
Regex: <text[^>]+>([^<]+)<\/text>
Works here: regex101
My parsing method:
func parseCaptions(text: String) -> String? {
let textRange = NSRange(location: 0, length: text.count)
let regex = try! NSRegularExpression(pattern: "<text[^>]+>([^<]+)<\\/text>")
let matches = regex.matches(in: text, range: textRange)
var result: String?
for match in matches {
let range = match.range
let first = text.index(text.startIndex, offsetBy: range.location)
let last = text.index(text.startIndex, offsetBy: range.location + range.length)
var string = String(text[first...last])
string = string.replacingOccurrences(of: "\n", with: " ")
string = string.replacingOccurrences(of: "'", with: "'")
string = string.replacingOccurrences(of: """, with: "\"")
string.append("\n")
result = string
}
return result
}
It's not the Regex the issue, it's what you do with the matches.
You do:
var result: String?
for match in matches {
let range = match.range
let first = text.index(text.startIndex, offsetBy: range.location)
let last = text.index(text.startIndex, offsetBy: range.location + range.length)
var string = String(text[first...last])
...
result = string
}
return result
So you're overwriting each time result with the last match.
A solution:
func parseCaptions(text: String) -> String {
//NSRange, based on NSString use UTF16 for counting, while Swift.String use UTF8 by default, so `text.count` might be wrong
let textRange = NSRange(location: 0, length: text.utf16.count)
let regex = try! NSRegularExpression(pattern: "<text[^>]+>([^<]+)<\\/text>")
let matches = regex.matches(in: text, range: textRange)
var result: String = ""
for match in matches {
let textNSRange = match.range(at: 1)
let textRange = Range(textNSRange, in: text)!
var string = String(text[textRange])
string = string.replacingOccurrences(of: "\n", with: " ")
string = string.replacingOccurrences(of: "'", with: "'")
string = string.replacingOccurrences(of: """, with: "\"")
string.append("\n")
result.append(string)
}
return result
}
So, with input:
This XML file does not appear to have any style information associated with it. The document tree is shown below.
<transcript>
<text start="9.462" dur="1.123">Aaaah</text>
<text start="70.507" dur="5.51">So guys, apparently we control Rewind this year.</text>
<text start="76.017" dur="4.842">
Y'all we can do whatever we want. What do we do?
</text>
</transcript>
We get:
Aaaah
So guys, apparently we control Rewind this year.
Y'all we can do whatever we want. What do we do?
Trying to split values in swift, and it works, but it is cAsE sEnSiTiVe. Is there a way of splitting them in swift 4 ignoring case?
"Hello There!"
"hello there!"
I am currently using String.components(seperatedBy: "Th") but that does only split the second string, "hello there!". Is there a way to split them both?
You can write something like this:
import Foundation
let str1 = "Hello There!"
let str2 = "hello there!"
extension String {
func caseInsensitiveSplit(separator: String) -> [String] {
//Thanks for Carpsen90. Please see comments below.
if separator.isEmpty {
return [self] //generates the same output as `.components(separatedBy: "")`
}
let pattern = NSRegularExpression.escapedPattern(for: separator)
let regex = try! NSRegularExpression(pattern: pattern, options: .caseInsensitive)
let matches = regex.matches(in: self, options: [], range: NSRange(0..<self.utf16.count))
let ranges = (0..<matches.count+1).map { (i: Int)->NSRange in
let start = i == 0 ? 0 : matches[i-1].range.location + matches[i-1].range.length
let end = i == matches.count ? self.utf16.count: matches[i].range.location
return NSRange(location: start, length: end-start)
}
return ranges.map {String(self[Range($0, in: self)!])}
}
}
print( str1.caseInsensitiveSplit(separator: "th") ) //->["Hello ", "ere!"]
print( str2.caseInsensitiveSplit(separator: "th") ) //->["hello ", "ere!"]
But I wonder what you want to do with "hello " and "ere!".
(You lose case-info of the separator, if it matched with "th", "tH", "Th" or "TH".)
If you can explain what you really want to do, someone would show you a better solution for it.
Just for fun another version using NSRegularExpression but extending StringProtocol:
Swift 4 or later
extension StringProtocol {
func caseInsensitiveComponents<T>(separatedBy separator: T) -> [SubSequence] where T: StringProtocol, Index == String.Index {
var index = startIndex
return ((try? NSRegularExpression(pattern: NSRegularExpression.escapedPattern(for: String(separator)), options: .caseInsensitive))?.matches(in: String(self), range: NSRange(location: 0, length: utf16.count))
.compactMap {
guard let range = Range($0.range, in: String(self))
else { return nil }
defer { index = range.upperBound }
return self[index..<range.lowerBound]
} ?? []) + [self[index...]]
}
}
Playground testing
let str1 = "Hello There!"
let str2 = "hello there!"
str1.caseInsensitiveComponents(separatedBy: "Th") // ["Hello ", "ere!"]
str2.caseInsensitiveComponents(separatedBy: "Th") // ["Hello ", "ere!"]
An possible solution is to get the range of the separator with .caseInsensitive option and extract the substrings from its lower- and upperBound.
extension StringProtocol where Index == String.Index {
func caseInsensitiveComponents(separatedBy separator: String) -> [SubSequence]
{
var result = [SubSequence]()
var currentIndex = startIndex
while let separatorRange = self[currentIndex...].range(of: separator, options: .caseInsensitive) {
result.append(self[currentIndex..<separatorRange.lowerBound])
currentIndex = separatorRange.upperBound
}
return result + [self[currentIndex...]]
}
}
You could lower case the whole string first:
let s = "Hello There!".lowercased().components(separatedBy: "th")
Not sure if i understood what you are trying to achieve, but you can try this.
var str = "Hello There!"
var str2 = "hello there!"
override func viewDidLoad() {
super.viewDidLoad()
let split1 = str.lowercased().replacingOccurrences(of: "th", with: " ")
let split2 = str2.lowercased().replacingOccurrences(of: "th", with: " ")
print(split1) //hello ere!
print(split2) //hello ere!
}
If you want to filter for a list of items containing a certain keyword, split the strings is the wrong approach. All you need is a simple check whether that keyword appears in the item:
let queryStr = "th"
let items = [
"Hello There!",
"hello there!"
]
let filterdItems = items.filter {
return $0.range(of: queryStr, options: .caseInsensitive) != nil
}
I am looking for a way to replace characters in a Swift String.
Example: "This is my string"
I would like to replace " " with "+" to get "This+is+my+string".
How can I achieve this?
This answer has been updated for Swift 4 & 5. If you're still using Swift 1, 2 or 3 see the revision history.
You have a couple of options. You can do as #jaumard suggested and use replacingOccurrences()
let aString = "This is my string"
let newString = aString.replacingOccurrences(of: " ", with: "+", options: .literal, range: nil)
And as noted by #cprcrack below, the options and range parameters are optional, so if you don't want to specify string comparison options or a range to do the replacement within, you only need the following.
let aString = "This is my string"
let newString = aString.replacingOccurrences(of: " ", with: "+")
Or, if the data is in a specific format like this, where you're just replacing separation characters, you can use components() to break the string into and array, and then you can use the join() function to put them back to together with a specified separator.
let toArray = aString.components(separatedBy: " ")
let backToString = toArray.joined(separator: "+")
Or if you're looking for a more Swifty solution that doesn't utilize API from NSString, you could use this.
let aString = "Some search text"
let replaced = String(aString.map {
$0 == " " ? "+" : $0
})
You can use this:
let s = "This is my string"
let modified = s.replace(" ", withString:"+")
If you add this extension method anywhere in your code:
extension String
{
func replace(target: String, withString: String) -> String
{
return self.stringByReplacingOccurrencesOfString(target, withString: withString, options: NSStringCompareOptions.LiteralSearch, range: nil)
}
}
Swift 3:
extension String
{
func replace(target: String, withString: String) -> String
{
return self.replacingOccurrences(of: target, with: withString, options: NSString.CompareOptions.literal, range: nil)
}
}
Swift 3, Swift 4, Swift 5 Solution
let exampleString = "Example string"
//Solution suggested above in Swift 3.0
let stringToArray = exampleString.components(separatedBy: " ")
let stringFromArray = stringToArray.joined(separator: "+")
//Swiftiest solution
let swiftyString = exampleString.replacingOccurrences(of: " ", with: "+")
Did you test this :
var test = "This is my string"
let replaced = test.stringByReplacingOccurrencesOfString(" ", withString: "+", options: nil, range: nil)
var str = "This is my string"
print(str.replacingOccurrences(of: " ", with: "+"))
Output is
This+is+my+string
Swift 5.5
I am using this extension:
extension String {
func replaceCharacters(characters: String, toSeparator: String) -> String {
let characterSet = CharacterSet(charactersIn: characters)
let components = components(separatedBy: characterSet)
let result = components.joined(separator: toSeparator)
return result
}
func wipeCharacters(characters: String) -> String {
return self.replaceCharacters(characters: characters, toSeparator: "")
}
}
Usage:
"<34353 43434>".replaceCharacters(characters: "< >", toSeparator:"+") // +34353+43434+
"<34353 43434>".wipeCharacters(characters: "< >") // 3435343434
Swift 4:
let abc = "Hello world"
let result = abc.replacingOccurrences(of: " ", with: "_",
options: NSString.CompareOptions.literal, range:nil)
print(result :\(result))
Output:
result : Hello_world
A Swift 3 solution along the lines of Sunkas's:
extension String {
mutating func replace(_ originalString:String, with newString:String) {
self = self.replacingOccurrences(of: originalString, with: newString)
}
}
Use:
var string = "foo!"
string.replace("!", with: "?")
print(string)
Output:
foo?
A category that modifies an existing mutable String:
extension String
{
mutating func replace(originalString:String, withString newString:String)
{
let replacedString = self.stringByReplacingOccurrencesOfString(originalString, withString: newString, options: nil, range: nil)
self = replacedString
}
}
Use:
name.replace(" ", withString: "+")
Swift 3 solution based on Ramis' answer:
extension String {
func withReplacedCharacters(_ characters: String, by separator: String) -> String {
let characterSet = CharacterSet(charactersIn: characters)
return components(separatedBy: characterSet).joined(separator: separator)
}
}
Tried to come up with an appropriate function name according to Swift 3 naming convention.
Less happened to me, I just want to change (a word or character) in the String
So I've use the Dictionary
extension String{
func replace(_ dictionary: [String: String]) -> String{
var result = String()
var i = -1
for (of , with): (String, String)in dictionary{
i += 1
if i<1{
result = self.replacingOccurrences(of: of, with: with)
}else{
result = result.replacingOccurrences(of: of, with: with)
}
}
return result
}
}
usage
let mobile = "+1 (800) 444-9999"
let dictionary = ["+": "00", " ": "", "(": "", ")": "", "-": ""]
let mobileResult = mobile.replace(dictionary)
print(mobileResult) // 001800444999
Xcode 11 • Swift 5.1
The mutating method of StringProtocol replacingOccurrences can be implemented as follow:
extension RangeReplaceableCollection where Self: StringProtocol {
mutating func replaceOccurrences<Target: StringProtocol, Replacement: StringProtocol>(of target: Target, with replacement: Replacement, options: String.CompareOptions = [], range searchRange: Range<String.Index>? = nil) {
self = .init(replacingOccurrences(of: target, with: replacement, options: options, range: searchRange))
}
}
var name = "This is my string"
name.replaceOccurrences(of: " ", with: "+")
print(name) // "This+is+my+string\n"
var str = "This is my string"
str = str.replacingOccurrences(of: " ", with: "+")
print(str)
This is easy in swift 4.2. just use replacingOccurrences(of: " ", with: "_") for replace
var myStr = "This is my string"
let replaced = myStr.replacingOccurrences(of: " ", with: "_")
print(replaced)
Since Swift 2, String does no longer conform to SequenceType. In other words, you can not iterate through a string with a for...in loop.
The simple and easy way is to convert String to Array to get the benefit of the index just like that:
let input = Array(str)
I remember when I tried to index into String without using any conversion. I was really frustrated that I couldn’t come up with or reach a desired result, and was about to give up.
But I ended up creating my own workaround solution, and here is the full code of the extension:
extension String {
subscript (_ index: Int) -> String {
get {
String(self[self.index(startIndex, offsetBy: index)])
}
set {
remove(at: self.index(self.startIndex, offsetBy: index))
insert(Character(newValue), at: self.index(self.startIndex, offsetBy: index))
}
}
}
Now that you can read and replace a single character from string using its index just like you originally wanted to:
var str = "cat"
for i in 0..<str.count {
if str[i] == "c" {
str[i] = "h"
}
}
print(str)
It’s simple and useful way to use it and get through Swift’s String access model.
Now that you’ll feel it’s smooth sailing next time when you can loop through the string just as it is, not casting it into Array.
Try it out, and see if it can help!
I've implemented this very simple func:
func convap (text : String) -> String {
return text.stringByReplacingOccurrencesOfString("'", withString: "''")
}
So you can write:
let sqlQuery = "INSERT INTO myTable (Field1, Field2) VALUES ('\(convap(value1))','\(convap(value2)')
I think Regex is the most flexible and solid way:
var str = "This is my string"
let regex = try! NSRegularExpression(pattern: " ", options: [])
let output = regex.stringByReplacingMatchesInString(
str,
options: [],
range: NSRange(location: 0, length: str.characters.count),
withTemplate: "+"
)
// output: "This+is+my+string"
Swift extension:
extension String {
func stringByReplacing(replaceStrings set: [String], with: String) -> String {
var stringObject = self
for string in set {
stringObject = self.stringByReplacingOccurrencesOfString(string, withString: with)
}
return stringObject
}
}
Go on and use it like let replacedString = yorString.stringByReplacing(replaceStrings: [" ","?","."], with: "+")
The speed of the function is something that i can hardly be proud of, but you can pass an array of String in one pass to make more than one replacement.
Here is the example for Swift 3:
var stringToReplace = "This my string"
if let range = stringToReplace.range(of: "my") {
stringToReplace?.replaceSubrange(range, with: "your")
}
Here's an extension for an in-place occurrences replace method on String, that doesn't no an unnecessary copy and do everything in place:
extension String {
mutating func replaceOccurrences<Target: StringProtocol, Replacement: StringProtocol>(of target: Target, with replacement: Replacement, options: String.CompareOptions = [], locale: Locale? = nil) {
var range: Range<Index>?
repeat {
range = self.range(of: target, options: options, range: range.map { self.index($0.lowerBound, offsetBy: replacement.count)..<self.endIndex }, locale: locale)
if let range = range {
self.replaceSubrange(range, with: replacement)
}
} while range != nil
}
}
(The method signature also mimics the signature of the built-in String.replacingOccurrences() method)
May be used in the following way:
var string = "this is a string"
string.replaceOccurrences(of: " ", with: "_")
print(string) // "this_is_a_string"
If you don't want to use the Objective-C NSString methods, you can just use split and join:
var string = "This is my string"
string = join("+", split(string, isSeparator: { $0 == " " }))
split(string, isSeparator: { $0 == " " }) returns an array of strings (["This", "is", "my", "string"]).
join joins these elements with a +, resulting in the desired output: "This+is+my+string".
you can test this:
let newString = test.stringByReplacingOccurrencesOfString(" ", withString: "+", options: nil, range: nil)
Swift 5.5
But this might work in earlier versions.
I'm frequently replacing because I want to replace "any whitespace or -" with a _ or something like that. This extension on string lets me do that.
extension String {
func removingCharacters(_ characters:CharacterSet) -> Self {
Self(self.unicodeScalars.filter {
!characters.contains($0)
})
}
func removingCharacters(in string:String) -> Self {
Self(self.unicodeScalars.filter {
!CharacterSet(charactersIn:string).contains($0)
})
}
func replacingCharacters(_ characters:CharacterSet, with newChar:Character) -> Self {
String(self.compactMap( {
CharacterSet(charactersIn: "\($0.1)").isSubset(of: characters)
? newChar : $0.1
}))
}
func replacingCharacters(in string:String, with newChar:Character) -> Self {
String(self.compactMap( {
CharacterSet(charactersIn: "\($0)").isSubset(of: CharacterSet(charactersIn:string))
? newChar : $0
}))
}
}
usage:
print("hello \n my name\t is Joe".removingCharacters(.whitespacesAndNewlines))
print("hello \n my name\t is Joe".removingCharacters(in: " \t\n"))
print("ban annan anann ana".replacingCharacters(.whitespacesAndNewlines, with: "_"))
print("ban-annan anann ana".replacingCharacters(in: " -", with: "_"))
Obviously for a single character the .replacingOccurrences(of: " ", with: "+") is better.
I have not done a performance comparison to the
let toArray = aString.components(separatedBy: characterSet)
let backToString = toArray.joined(separator: "+")
style done in Ramis's extension. I'd be interested if someone does.
See also replacing emoji's: https://stackoverflow.com/a/63416058/5946596
I would like to separate a CamelCase string into space-separated words in a new string. Here is what I have so far:
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"
I'm inclined to suspect that this may not be the most efficient way to convert to space-separated words, if I call it in a tight loop, or 1000's of times. Are there more efficient ways to do this in Swift?
[Edit 1:] The solution I require should remain general for Unicode scalars, not specific to Roman ASCII "A..Z".
[Edit 2:] The solution should also skip the first letter, i.e. not prepend a space before the first letter.
[Edit 3:] Updated for Swift 4 syntax, and added caching of uppercaseLetters, which improves performance in very long strings and tight loops.
extension String {
func camelCaseToWords() -> String {
return unicodeScalars.dropFirst().reduce(String(prefix(1))) {
return CharacterSet.uppercaseLetters.contains($1)
? $0 + " " + String($1)
: $0 + String($1)
}
}
}
print("ÄnotherCamelCaps".camelCaseToWords()) // Änother Camel Caps
May be helpful for someone :)
One Line Solution
I concur with #aircraft, regular expressions can solve this problem in one 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
}
}
Props to this JS answer.
P.S. I have a gist for snake_case → CamelCase here.
P.P.S. I updated this for New Swift (currently 5.1), then saw #busta's answer, and swapped out my startIndex..<endIndex for his range(of: self). Credit where it's due y'all!
a better full swifty solution... based on AmitaiB answer
extension String {
func titlecased() -> String {
return self.replacingOccurrences(of: "([A-Z])", with: " $1", options: .regularExpression, range: self.range(of: self))
.trimmingCharacters(in: .whitespacesAndNewlines)
.capitalized
}
}
I might be late but I want to share a little improvement to Augustine P A answer or Leo Dabus comment.
Basically, that code won't work properly if we are using upper camel case notation (like "DuckDuckGo") because it will add a space at the beginning of the string.
To address this issue, this is a slightly modified version of the code, using Swift 3.x, and it's compatible with both upper and lower came case:
extension String {
func camelCaseToWords() -> String {
return unicodeScalars.reduce("") {
if CharacterSet.uppercaseLetters.contains($1) {
if $0.count > 0 {
return ($0 + " " + String($1))
}
}
return $0 + String($1)
}
}
}
As far as I tested on my old MacBook, your code seems to be efficient enough for short strings:
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
(Do not try to test the code above in the Playground. The numbers are taken from a single trial executed as a CommandLine app.)
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)
}
}
In
"ThisStringHasNoSpacesButItDoesHaveCapitals"
"IAmNotAGoat"
"LOLThatsHilarious!"
"ThisIsASMSMessage"
Out
"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
I can do this extension in less lines of code (and without a CharacterSet), but yes, you basically have to enumerate each String if you want to insert spaces in front of capital letters.
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
Swift 5 solution
extension String {
func camelCaseToWords() -> String {
return unicodeScalars.reduce("") {
if CharacterSet.uppercaseLetters.contains($1) {
if $0.count > 0 {
return ($0 + " " + String($1))
}
}
return $0 + String($1)
}
}
}
If you want to make it more efficient, you can use 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) { " \($0)" }
print("Uppercase characters replaced: \(camelCapped)")
Here's what I came up with using Unicode character classes: (Swift 5)
extension String {
var titleCased: String {
self
.replacingOccurrences(of: "(\\p{UppercaseLetter}\\p{LowercaseLetter}|\\p{UppercaseLetter}+(?=\\p{UppercaseLetter}))",
with: " $1",
options: .regularExpression,
range: range(of: self)
)
.capitalized
}
}
Output:
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 way:
extension String {
var titlecased: String {
map { ($0.isUppercase ? " " : "") + String($0) }
.joined(separator: "")
.trimmingCharacters(in: .whitespaces)
}
}
Swift 5+
Small style improvements on previous answers
import Foundation
extension String {
func camelCaseToWords() -> String {
unicodeScalars.reduce("") {
guard CharacterSet.uppercaseLetters.contains($1),
$0.count > 0
else { return $0 + String($1) }
return ($0 + " " + String($1))
}
}
}
Using guard let statements is usually recommended, as they provide an "early exit" for non matching cases and decrease the overall nesting levels of your code (which usually improves readability quite a lot... and remember, readability counts!)
Solution with REGEX
let camelCase = "SomeATMInTheShop"
let regexPattern = "[A-Z-_&](?=[a-z0-9]+)|[A-Z-_&]+(?![a-z0-9])"
let newValue = camelCase.replacingOccurrences(of: regexPattern, with: " $0", options: .regularExpression, range: nil)
Otuput ==> Some ATM In The Shop
I've got a problem with removing whitespaces at the beginning and end of string. For e.g. I've got a string like:
\r\n\t- Someone will come here?\n- I don't know for sure...\r\n\r\n
And I need to remove whitespaces only at the end and beginning (string should be look like:
- Someone will come here?\n- I don't know for sure...
Also there could be a lot of variants of string end: "\r\n\r\n", "\r\n", "\n\r\n" and so on...
Thanks.
Your string contains not only whitespace but also new line characters.
Use stringByTrimmingCharactersInSet with whitespaceAndNewlineCharacterSet.
let string = "\r\n\t- Someone will come here?\n- I don't know for sure...\r\n\r\n"
let trimmedString = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
In Swift 3 it's more cleaned up:
let trimmedString = string.trimmingCharacters(in: .whitespacesAndNewlines)
String trimming first and last whitespaces and newlines in Swift 4+
" 2 space ".trimmingCharacters(in: .whitespacesAndNewlines)
result:
"2 space"
//here i have used regular expression and replaced white spaces at the start and end
let stringPassing : NSString? = "hdfjkhsdj hfjksdhf sdf "
do {
print("old->\(stringPassing)")
let pattern : String = "(^\\s+)|(\\s+)$"
let regex = try NSRegularExpression(pattern: pattern , options: [])
let newMatched = regex.matchesInString(stringPassing! as String, options: [], range: NSMakeRange(0,stringPassing!.length))
if(newMatched.count > 0){
let modifiedString = regex.stringByReplacingMatchesInString(stringPassing! as String, options: [] , range: NSMakeRange(0,stringPassing!.length), withTemplate: "")
print("new->\(modifiedString)")
}
} catch let error as NSError {
print(error.localizedDescription)
}
You can use this extension and just call "yourString".trim()
extension String
{
func trim() -> String
{
return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
}
}
If you want to remove Whitespace from the start of the string Try this Code.
func removeSpace()->String{
let txt = myString
var count:Int! = 0
for i in txt{
if i == " "{
count += 1
}else{
if count != 0{
return String(txt.dropFirst(count))
}else{
return txt
}
}
}
return ""
}
`
Remove all whiteSpaces from the start of string
func removeWhiteSpaces(str:String) -> String{
var newStr = str
for i in 0..<str.count{
let index = str.index(str.startIndex, offsetBy: i)
print(str[index])
if str[index] != " "{
return newStr
}
else{
newStr.remove(at: newStr.startIndex)
}
}
return newStr
}
In this code to restrict Textfield beginning white space
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
if (textField.text?.count)! == 0 && string == " " {
return false
}
else{
return true
}
}
In Swift 4
Use it on any String type variable.
extension String {
func trimWhiteSpaces() -> String {
let whiteSpaceSet = NSCharacterSet.whitespaces
return self.trimmingCharacters(in: whiteSpaceSet)
}
}
And Call it like this
yourString.trimWhiteSpaces()
You could first remove from the beginning and then from the end like so:
while true {
if sentence.characters.first == "\r\n" || sentence.characters.first == "\n" {
sentence.removeAtIndex(sentence.startIndex)
} else {
break
}
}
while true {
if sentence.characters.last == "\r\n" || sentence.characters.last == "\n" {
sentence.removeAtIndex(sentence.endIndex.predecessor())
} else {
break
}
}
let string = " exam ple "
let trimmed = string.replacingOccurrences(of: "(^\s+)|(\s+)$", with: "", options: .regularExpression)
print(">" + trimmed3 + "<")
// prints >exam ple<