componentsseparatedbystring by multiple separators in Swift - ios

So here is the string s:
"Hi! How are you? I'm fine. It is 6 p.m. Thank you! That's it."
I want them to be separated to a array as:
["Hi", "How are you", "I'm fine", "It is 6 p.m", "Thank you", "That's it"]
Which means the separators should be ". " + "? " + "! "
I've tried:
let charSet = NSCharacterSet(charactersInString: ".?!")
let array = s.componentsSeparatedByCharactersInSet(charSet)
But it will separate p.m. to two elements too. Result:
["Hi", " How are you", " I'm fine", " It is 6 p", "m", " Thank you", " That's it"]
I've also tried
let array = s.componentsSeparatedByString(". ")
It works well for separating ". " but if I also want to separate "? ", "! ", it become messy.
So any way I can do it? Thanks!

There is a method provided that lets you enumerate a string. You can do so by words or sentences or other options. No need for regular expressions.
let s = "Hi! How are you? I'm fine. It is 6 p.m. Thank you! That's it."
var sentences = [String]()
s.enumerateSubstringsInRange(s.startIndex..<s.endIndex, options: .BySentences) {
substring, substringRange, enclosingRange, stop in
sentences.append(substring!)
}
print(sentences)
The result is:
["Hi! ", "How are you? ", "I\'m fine. ", "It is 6 p.m. ", "Thank you! ", "That\'s it."]

rmaddy's answer is correct (+1). A Swift 3 implementation is:
var sentences = [String]()
string.enumerateSubstrings(in: string.startIndex ..< string.endIndex, options: .bySentences) { substring, substringRange, enclosingRange, stop in
sentences.append(substring!)
}
You can also use regular expression, NSRegularExpression, though it's much hairier than rmaddy's .bySentences solution. In Swift 3:
var sentences = [String]()
let regex = try! NSRegularExpression(pattern: "(^|\\s+)(\\w.*?[.!?]+)(?=(\\s+|$))")
regex.enumerateMatches(in: string, range: NSMakeRange(0, string.characters.count)) { match, flags, stop in
sentences.append((string as NSString).substring(with: match!.rangeAt(2)))
}
Or Swift 2:
let regex = try! NSRegularExpression(pattern: "(^|\\s+)(\\w.*?[.!?]+)(?=(\\s+|$))", options: [])
var sentences = [String]()
regex.enumerateMatchesInString(string, options: [], range: NSMakeRange(0, string.characters.count)) { match, flags, stop in
sentences.append((string as NSString).substringWithRange(match!.rangeAtIndex(2)))
}
The [.!?] syntax matches any of those three characters. The | means "or". The ^ matches the start of the string. The $ matches the end of the string. The \\s matches a whitespace character. The \\w matches a "word" character. The * matches zero or more of the preceding character. The + matches one or more of the preceding character. The (?=) is a look-ahead assertion (e.g. see if there's something there, but don't advance through that match).
I've tried to simplify this a bit, and it's still pretty complicated. Regular expressions offer rich text pattern matching, but, admittedly, it is a little dense when you first use it. But this rendition matches (a) repeated punctuation (e.g. "Thank you!!!"), (b) leading spaces, and (c) trailing spaces, too.

If the splitting basis is something a little more esoteric than sentences, this extension could work.
extension String {
public func components(separatedBy separators: [String]) -> [String] {
var output: [String] = [self]
for separator in separators {
output = output.flatMap { $0.components(separatedBy: separator) }
}
return output.map { $0.trimmingCharacters(in: .whitespaces)}
}
}
let artists = "Rihanna, featuring Calvin Harris".components(separated by: [", with", ", featuring"])

I tried to find a regex to solve this too: (([^.!?]+\s)*\S+(\.|!|\?))
Here the explanation from regexper and an example

Well I've found a regex too from here
var pattern = "(?<=[.?!;…])\\s+(?=[\\p{Lu}\\p{N}])"
let s = "Hi! How are you? I'm fine. It is 6 p.m. Thank you! That's it."
let sReplaced = s.stringByReplacingOccurrencesOfString(pattern, withString:"[*-SENTENCE-*]" as String, options:NSStringCompareOptions.RegularExpressionSearch, range:nil)
let array = sReplaced.componentsSeparatedByString("[*-SENTENCE-*]")
Perhaps it's not a good way as it has to first replace and than separate the string. :)
UPDATE:
For regex part, if you also want to match Chinese/Japanese punctuations (which space after each punctuation is not necessary), you can use the following one:
((?<=[.?!;…])\\s+|(?<=[。!?;…])\\s*)(?=[\\p{L}\\p{N}])

Related

remove exact word phrase from string in Swift or Objective-C

I would like to remove an exact combination of words from a string in Swift or Objective-C without removing portions of a word.
You can remove a single word from a string by converting the strings into arrays:
NSString *str = #"Did the favored horse win the race?";
NSString *toRemove = #"horse";
NSMutableArray *mutArray = [str componentsSeparatedByString:#" "];
NSArray *removeArray = [toRemove componentsSeparatedByString:#" "];
[mutarr removeObjectsInArray:removeArr];
You can also remove a two word string from another string if you don't care about whole words using:
str = [str stringByReplacingOccurrencesOfString:#"favored horse " withString:#""];
although you have to work around the spacing issue.
This would fail, however, on a string such as:
str = [str stringByReplacingOccurrencesOfString:#"red horse " withString:#""];
Which would give "Did the favo horse win the race"
How can you remove a multiple word term cleanly without removing partial words leaving fragments?
Thanks for any suggestions.
// Convert string to array of words
let words = string.components(separatedBy: " ")
// Do the same for your search words
let wordsToRemove = "red horse".components(separatedBy: " ")
// remove only the full matching words, and reform the string
let result = words.filter { !wordsToRemove.contains($0) }.joined(separator: " ")
// result = "Did the favored win the race?"
The caveat to this method is that it will remove those exact words anywhere in your original string. If you want the result to only remove the words where they appear in that exact order, then just use a space at the front of the parameter for replacingOccurrencesOf.
If you want to remove some words try to use this extension:
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
}
}
how to use:
let str = "Did the favored horse win the race?"
let dictionary = ["horse ": "", "the ": ""]
let result = str.replace(dictionary)
print("result: \(result)")
Output:
result: Did favored win race?
For one word:
let str = "Did the favored horse win the race?"
let result = str.replacingOccurrences(of: "horse ", with: "", options: .literal, range:nil)
print("result: \(result)")
Output:
result: Did the favored win the race?
don't forget to include the space in the word that you want to remove... Hope this help
You could also take account of the leading space and replace the whole match with a single space:
str = [str stringByReplacingOccurrencesOfString:#" red horse " withString:#" "];
Or, and you may need to tweak this example, you could just use a Regular Expression - this is the sort of thing they are designed for, and the syntax is nice in Swift

Replace matches using regex by modifying matched string in swift

Im trying to replace matched strings using regex in swift, my requirement is as below
originalString = "It is live now at Germany(DE)"
i want the string within the (" ") i.eDE to be separated by space i.e. "D E"
so replacedString should be "It is live now at Germany(D E)"
i tried below code
var value: NSMutableString = "It is live now at Germany(DE)"
let pattern = "(\\([A-Za-z ]+\\))"
let regex = try? NSRegularExpression(pattern: pattern)
regex?.replaceMatches(in: value, options: .reportProgress, range:
NSRange(location: 0,length: value.length), withTemplate: " $1 ")
print(value)
output is It is live now at Germany (DE), i know it's not what is required.
here it is based on the template where we cannot modify based on matched string value. Is there any way to achieve this ?
Thanks in advance
You may use
var value: NSMutableString = "It is live now at Germany(DE) or (SOFE)"
let pattern = "(?<=\\G(?<!\\A)|\\()[A-Za-z](?=[A-Za-z]+\\))"
let regex = try? NSRegularExpression(pattern: pattern)
regex?.replaceMatches(in: value, options: .reportProgress, range: NSRange(location: 0,length: value.length), withTemplate: "$0 ")
print(value)
Or just
let val = "It is live now at Germany(DE) or (SOFE)"
let pattern = "(?<=\\G(?<!\\A)|\\()[A-Za-z](?=[A-Za-z]+\\))"
print( val.replacingOccurrences(of: pattern, with: "$0 ", options: .regularExpression, range: nil) )
Output: It is live now at Germany(D E) or (S O F E)
Pattern details
(?<=\\G(?<!\\A)|\\() - a positive lookbehind that matches a location right after ( or at the end of the preceding successful match
[A-Za-z] - matches and consumes any ASCII letter
(?=[A-Za-z]+\\)) - a positive lookahead that matches a location that is immediately followed with 1+ ASCII letters and then a ) char.
The $0 in the replacement inserts the whole match value back into the resulting string.

How to find multiple substrings within braces from a string?

I have a string [Desired Annual Income] /([Income per loan %] /100)
Using this string, I have to find two sub strings 'Desired Annual Income' and 'Income per loan %' in Swift3.
I am using below code to achieve this 'How do I get the substring between braces?':
let myString = "[Desired Annual Income] /([Income per loan %] /100)"
let start: NSRange = (myString as NSString).range(of: "[")
let end: NSRange = (myString as NSString).range(of: "]")
if start.location != NSNotFound && end.location != NSNotFound && end.location > start.location {
let result: String = (myString as NSString).substring(with: NSRange(location: start.location + 1, length: end.location - (start.location + 1)))
print(result)
}
But as an output I am getting only 'Desired Annual Income', How can I get all substrings?
Try this,
Hope it will work
let str = "[Desired Annual Income] /([Income per loan %] /100)"
let trimmedString = str.components(separatedBy: "]")
for i in 0..<trimmedString.count - 1{ // not considering last component since it's of no use hence count-1 times loop
print(trimmedString[i].components(separatedBy: "[").last ?? "")
}
Output:-
Desired Annual Income
Income per loan %
It's a very good use case for regular expressions (NSRegularExpression). The principle of regular expressions is to describe a "pattern" that you want to search in a string.
In that case you search something between two brackets.
The code is then:
let str = "[Desired Annual Income] /([Income per loan %] /100)"
if let regex = try? NSRegularExpression(pattern: "\\[(.+?)\\]", options: [.caseInsensitive]) {
var collectMatches: [String] = []
for match in regex.matches(in: str, options: [], range: NSRange(location: 0, length: (str as NSString).length)) {
// range at index 0: full match (including brackets)
// range at index 1: first capture group
let substring = (str as NSString).substring(with: match.range(at: 1))
collectMatches.append(substring)
}
print(collectMatches)
}
For the explanation about regular expressions, there are plenty of tutorial on internet. But in very short:
\\[ and \\]: opening and closing brackets characters (the double backslashes are because brackets have a meaning in regular expression, so you need to escape them. In a text editor one backslash is enough, but you need a second one because you are in a String and you need to escape the backslash to have a backslash.
(.+?) is a bit more complex: the parentheses are the "capture group", what you want to get. . means "any character", + one or more time, ? after a + is the greedy operator, which means that you want the capture to stop ASAP. If you don't put it, your capture can be in your case "Desired Annual Income] /([Income per loan %", depending of the regex library that you are using. Foundation seems to be greedy by default, that being said.
Regex are not always super easy/direct, but if you do often text processing, it's a very powerful tool to know.

Using regex with pipe separators in Swift 3 to filter out matches

Having looked at quite a few regex stackoverflow questions thus far, I'm a bit confused as to why my regex isn't filtering out the substrings I'd like to remove. I know for swift, I'll need to escape any other escapes that I have.
First, I have an extension on String like so:
extension String {
func stringByRemovingRegexMatches(pattern: String, replaceWith: String = "") -> String? {
do {
let regex = try NSRegularExpression(pattern: pattern, options: NSRegularExpression.Options.caseInsensitive)
print(regex)
let range = NSMakeRange(0, self.characters.count)
return regex.stringByReplacingMatches(in: self, options: [], range: range, withTemplate: replaceWith)
} catch {
return nil
}
}
}
Let's say for example the text is:
+++cta\n[![](http://media.test.com/1.jpg)](http://www.test.com/slideshow)\n\n+++\n\n<span class=\"s1\">Hello</span><span class=\"s2\"> World\n\n[cm_image id=\"13501068\"]<img style=\"width: 100%;\" src=\"http://www.test.com/1.jpg\" alt=\"\" />[/cm_image]\n\n[#c: /c/56be30deff2afb50ef000026]||||||
I want this to return Hello World\n in my Swift project, but it seems to only filter out the <span>:
+++cta\n[![](http://media.test.com/1.jpg)](http://www.test.com/slideshow)\n\n+++\n\nHello World\n\n[cm_image id=\"13501068\"]<img style=\"width: 100%;\" src=\"http://www.test.com/1.jpg\" alt=\"\" />[/cm_image]\n\n[#c: /c/56be30deff2afb50ef000026]||||||
My regex string in Swift is:
"\\+(^)\\+|\\\\n\\[(.*)\\|\\|\\|\\|\\|\\||\\\\n\\[c(.*)\\\\n\\\\n|\\+(.*)\\+\\\\n\\\\n|<[^>]+>"
You'll notice above I have a print statement of regex which matches what I'm putting into regexr, and the string is:
<NSRegularExpression: 0x7ff8d576df00> \+(^)\+|\\n\[(.*)\|\|\|\|\|\||\\n\[c(.*)\\n\\n|\+(.*)\+\\n\\n|<[^>]+> 0x1
\+(^)\+|\\n\[(.*)\|\|\|\|\|\||\\n\[c(.*)\\n\\n|\+(.*)\+\\n\\n|<[^>]+>
They're both the exact same.
For further clarification, I have a link to regexr here.
Am I doing something incorrect in Swift? Is it that swift doesn't recognize the (.*)? Any help would be greatly appreciated.
So making sure that I have ((.|\n)*) in between my startIndex and endIndex solved this problem because I want all characters or returns between my bounds.

Swift - Remove " character from string

I have a string which is "Optional("5")". I need to remove the "" surrounding the 5. I have removed the 'Optional' by doing:
text2 = text2.stringByReplacingOccurrencesOfString("Optional(", withString: "", options: NSStringCompareOptions.LiteralSearch, range: nil)
I am having difficulties removing the " characters as they designate the end of a string in the code.
Swift uses backslash to escape double quotes. Here is the list of escaped special characters in Swift:
\0 (null character)
\\ (backslash)
\t (horizontal tab)
\n (line feed)
\r (carriage return)
\" (double quote)
\' (single quote)
This should work:
text2 = text2.replacingOccurrences(of: "\\", with: "", options: NSString.CompareOptions.literal, range: nil)
Swift 3 and Swift 4:
text2 = text2.textureName.replacingOccurrences(of: "\"", with: "", options: NSString.CompareOptions.literal, range:nil)
Latest documents updated to Swift 3.0.1 have:
Null Character (\0)
Backslash (\\)
Horizontal Tab (\t)
Line Feed (\n)
Carriage Return (\r)
Double Quote (\")
Single Quote (\')
Unicode scalar (\u{n}), where n is between one and eight hexadecimal digits
If you need more details you can take a look to the official docs here
Here is the swift 3 updated answer
var editedText = myLabel.text?.replacingOccurrences(of: "\"", with: "")
Null Character (\0)
Backslash (\\)
Horizontal Tab (\t)
Line Feed (\n)
Carriage Return (\r)
Double Quote (\")
Single Quote (\')
Unicode scalar (\u{n})
To remove the optional you only should do this
println("\(text2!)")
cause if you dont use "!" it takes the optional value of text2
And to remove "" from 5 you have to convert it to NSInteger or NSNumber easy peasy. It has "" cause its an string.
Replacing for Removing is not quite logical.
String.filter allows to iterate a string char by char and keep only true assertion.
Swift 4 & 5
var aString = "Optional(\"5\")"
aString = aString.filter { $0 != "\"" }
> Optional(5)
Or to extend
var aString = "Optional(\"5\")"
let filteredChars = "\"\n\t"
aString = aString.filter { filteredChars.range(of: String($0)) == nil }
> Optional(5)
I've eventually got this to work in the playground, having multiple characters I'm trying to remove from a string:
var otherstring = "lat\" : 40.7127837,\n"
var new = otherstring.stringByTrimmingCharactersInSet(NSCharacterSet.init(charactersInString: "la t, \n \" ':"))
count(new) //result = 10
println(new)
//yielding what I'm after just the numeric portion 40.7127837
If you want to remove more characters for example "a", "A", "b", "B", "c", "C" from string you can do it this way:
someString = someString.replacingOccurrences(of: "[abc]", with: "", options: [.regularExpression, .caseInsensitive])
As Martin R says, your string "Optional("5")" looks like you did something wrong.
dasblinkenlight answers you so it is fine, but for future readers, I will try to add alternative code as:
if let realString = yourOriginalString {
text2 = realString
} else {
text2 = ""
}
text2 in your example looks like String and it is maybe already set to "" but it looks like you have an yourOriginalString of type Optional(String) somewhere that it wasn't cast or use correctly.
I hope this can help some reader.
Swift 5 (working). Only 1 line code.
For removing single / multiple characters.
trimmingCharacters(in: CharacterSet)
In action:
var yourString:String = "(\"This Is: Your String\")"
yourString = yourString.trimmingCharacters(in: ["("," ",":","\"",")"])
print(yourString)
Output:
ThisIsYourString
You are entering a Set that contains characters you're required to trim.
Let's say you have a string:
var string = "potatoes + carrots"
And you want to replace the word "potatoes" in that string with "tomatoes"
string = string.replacingOccurrences(of: "potatoes", with: "tomatoes", options: NSString.CompareOptions.literal, range: nil)
If you print your string, it will now be: "tomatoes + carrots"
If you want to remove the word potatoes from the sting altogether, you can use:
string = string.replacingOccurrences(of: "potatoes", with: "", options: NSString.CompareOptions.literal, range: nil)
If you want to use some other characters in your sting, use:
Null Character (\0)
Backslash (\)
Horizontal Tab (\t)
Line Feed (\n)
Carriage Return (\r)
Double Quote (\")
Single Quote (\')
Example:
string = string.replacingOccurrences(of: "potatoes", with: "dog\'s toys", options: NSString.CompareOptions.literal, range: nil)
Output: "dog's toys + carrots"
If you are getting the output Optional(5) when trying to print the value of 5 in an optional Int or String, you should unwrap the value first:
if value != nil
{ print(value)
}
or you can use this:
if let value = text {
print(value)
}
or in simple just 1 line answer:
print(value ?? "")
The last line will check if variable 'value' has any value assigned to it, if not it will print empty string
You've instantiated text2 as an Optional (e.g. var text2: String?).
This is why you receive Optional("5") in your string.
take away the ? and replace with:
var text2: String = ""
If you are getting the output Optional(5) when trying to print the value of 5 in an optional Int or String, you should unwrap the value first:
if let value = text {
print(value)
}
Now you've got the value without the "Optional" string that Swift adds when the value is not unwrapped before.

Resources