How to Create String Split Extension with Regex in Swift

How to create String split extension with regex in Swift?

First, your split function has some redundancy. It is enough to return

return self.componentsSeparatedByString(splitter)

Second, to work with a regular expression you just have to create a NSRegularExpression and then perhaps replace all occurrences with your own "stop string" and finally separate using that. E.g.

extension String {
func split(regex pattern: String) -> [String] {
let template = "-|*~~*~~*|-" /// Any string that isn't contained in the original string (self).

let regex = try? NSRegularExpression(pattern: pattern)
let modifiedString = regex?.stringByReplacingMatches(
in: self,
range: NSRange(
location: 0,
length: count
),
withTemplate: template /// Replace with the template/stop string.
)

/// Split by the replaced string.
return modifiedString?.components(separatedBy: template) ?? []
}
}

How to Split String Using Regex Expressions

Swift doesn't have native regular expressions as of yet. But Foundation provides NSRegularExpression.

import Foundation

let toSearch = "323 ECO Economics Course 451 ENG English Course 789 MAT Mathematical Topography"

let pattern = "[0-9]{3} [A-Z]{3}"
let regex = try! NSRegularExpression(pattern: pattern, options: [])

// NSRegularExpression works with objective-c NSString, which are utf16 encoded
let matches = regex.matches(in: toSearch, range: NSMakeRange(0, toSearch.utf16.count))

// the combination of zip, dropFirst and map to optional here is a trick
// to be able to map on [(result1, result2), (result2, result3), (result3, nil)]
let results = zip(matches, matches.dropFirst().map { Optional.some($0) } + [nil]).map { current, next -> String in
let range = current.rangeAt(0)
let start = String.UTF16Index(range.location)
// if there's a next, use it's starting location as the ending of our match
// otherwise, go to the end of the searched string
let end = next.map { $0.rangeAt(0) }.map { String.UTF16Index($0.location) } ?? String.UTF16Index(toSearch.utf16.count)

return String(toSearch.utf16[start..<end])!
}

dump(results)

Running this will output

▿ 3 elements
- "323 ECO Economics Course "
- "451 ENG English Course "
- "789 MAT Mathematical Topography"

Swift - Splitting strings with regex - ignoring search string

You can define an extension like this:

extension String {
func split(usingRegex pattern: String) -> [String] {
//### Crashes when you pass invalid `pattern`
let regex = try! NSRegularExpression(pattern: pattern)
let matches = regex.matches(in: self, range: NSRange(0..<utf16.count))
let ranges = [startIndex..<startIndex] + matches.map{Range($0.range, in: self)!} + [endIndex..<endIndex]
return (0...matches.count).map {String(self[ranges[$0].upperBound..<ranges[$0+1].lowerBound])}
}
}

let str = "hi|thisZshouldZYbe|separated"
let separator = "\\||ZY?"
let result = str.split(usingRegex: separator)
print(result) //->["hi", "this", "should", "be", "separated"]

The above code does not work as you expect when you use "\\||Z|ZY", but I think you can modify your pattern to fit into this extension.

How to split filename from file extension in Swift?

This is with Swift 2, Xcode 7: If you have the filename with the extension already on it, then you can pass the full filename in as the first parameter and a blank string as the second parameter:

let soundURL = NSBundle.mainBundle()
.URLForResource("soundfile.ext", withExtension: "")

Alternatively nil as the extension parameter also works.

If you have a URL, and you want to get the name of the file itself for some reason, then you can do this:

soundURL.URLByDeletingPathExtension?.lastPathComponent

Swift 4

let soundURL = NSBundle.mainBundle().URLForResource("soundfile.ext", withExtension: "")
soundURL.deletingPathExtension().lastPathComponent

How to split a string in Swift

Try this:

var myString: String = "hello hi";
var myStringArr = myString.componentsSeparatedByString(" ")

Where myString is the name of your string, and myStringArr contains the components separated by the space.

Then you can get the components as:

var hello: String = myStringArr [0]
var hi: String = myStringArr [1]

Doc: componentsSeparatedByString

EDIT: For Swift 3, the above will be:

var myStringArr = myString.components(separatedBy: " ")

Doc: components(separatedBy:)

How to get components separated by regular expression, but also with separators?

Your code adds only the substrings between the matches (and before the first match and after the last match) to the result. What you need is also the substrings for the matches themselves. This can be done by creating an array with all indices where a match starts or ends, and then taking all substrings between consecutive indices:

extension String {
func split(usingRegex pattern: String) -> [String] {
let regex = try! NSRegularExpression(pattern: pattern)
let matches = regex.matches(in: self, range: NSRange(startIndex..., in: self))
let splits = [startIndex]
+ matches
.map { Range($0.range, in: self)! }
.flatMap { [ $0.lowerBound, $0.upperBound ] }
+ [endIndex]

return zip(splits, splits.dropFirst())
.map { String(self[$0 ..< $1])}
}
}

Example:

let string = "Hello45playground23today"
let output = string.split(usingRegex: "[0-9]+")
print(output) // ["Hello", "45", "playground", "23", "today"]

The same can be done with an explicit loop (less sophisticated, but perhaps better readable):

extension String {
func split(usingRegex pattern: String) -> [String] {
let regex = try! NSRegularExpression(pattern: pattern)
let matches = regex.matches(in: self, range: NSRange(startIndex..., in: self))
var result: [String] = []
var pos = startIndex
for match in matches {
let range = Range(match.range, in: self)!
result.append(String(self[pos..<range.lowerBound]))
result.append(String(self[range]))
pos = range.upperBound
}
result.append(String(self[pos...]))
return result
}
}

Split string by components and keep components in place

For that you need to loop through the String and check its each characters that is it tokens or not. You can make extension of String for that like this.

extension String {

func stringTokens(splitMarks: Set<String>) -> [String] {

var string = ""
var desiredOutput = [String]()
for ch in self.characters {
if splitMarks.contains(String(ch)) {
if !string.isEmpty {
desiredOutput.append(string)
}
desiredOutput.append(String(ch))
string = ""
}
else {
string += String(ch)
}
}
if !string.isEmpty {
desiredOutput.append(string)
}
return desiredOutput
}
}

Now you can call this function like this way.

let input = "foo&bar|hello"
print(input.stringTokens(splitMarks: ["&", "|"]))

Output

["foo", "&", "bar", "|", "hello"]

Split a String into an array in Swift?

The Swift way is to use the global split function, like so:

var fullName = "First Last"
var fullNameArr = split(fullName) {$0 == " "}
var firstName: String = fullNameArr[0]
var lastName: String? = fullNameArr.count > 1 ? fullNameArr[1] : nil

with Swift 2

In Swift 2 the use of split becomes a bit more complicated due to the introduction of the internal CharacterView type. This means that String no longer adopts the SequenceType or CollectionType protocols and you must instead use the .characters property to access a CharacterView type representation of a String instance. (Note: CharacterView does adopt SequenceType and CollectionType protocols).

let fullName = "First Last"
let fullNameArr = fullName.characters.split{$0 == " "}.map(String.init)
// or simply:
// let fullNameArr = fullName.characters.split{" "}.map(String.init)

fullNameArr[0] // First
fullNameArr[1] // Last

Splitting a string in swift using multiple delimiters

This isn't very efficient, but it should do the job:

import Foundation

extension String {
func componentsSeperatedByStrings(ss: [String]) -> [String] {
let inds = ss.flatMap { s in
self.rangeOfString(s).map { r in [r.startIndex, r.endIndex] } ?? []
}
let ended = [startIndex] + inds + [endIndex]
let chunks = stride(from: 0, to: ended.count, by: 2)
let bounds = map(chunks) { i in (ended[i], ended[i+1]) }
return bounds
.map { (s, e) in self[s..<e] }
.filter { sl in !sl.isEmpty }
}
}



"KEY1=subKey1=value&subkey2=valueKEY2=subkey1=value&subkey2=valueKEY3=subKey1=value&subkey3=value".componentsSeperatedByStrings(["KEY1", "KEY2", "KEY3"])

// ["=subKey1=value&subkey2=value", "=subkey1=value&subkey2=value", "=subKey1=value&subkey3=value"]

Or, if you wanted it in dictionary form:

import Foundation

extension String {
func componentsSeperatedByStrings(ss: [String]) -> [String:String] {
let maybeRanges = ss.map { s in self.rangeOfString(s) }
let inds = maybeRanges.flatMap { $0.map { r in [r.startIndex, r.endIndex] } ?? [] }
let ended = [startIndex] + inds + [endIndex]
let chunks = stride(from: 0, to: ended.count, by: 2)
let bounds = map(chunks) { i in (ended[i], ended[i+1]) }
let values = bounds
.map { (s, e) in self[s..<e] }
.filter { sl in !sl.isEmpty }
let keys = filter(zip(maybeRanges, ss)) { (r, _) in r != nil }
var result: [String:String] = [:]
for ((_, k), v) in zip(keys, values) { result[k] = v }
return result
}
}


"KEY1=subKey1=value&subkey2=valueKEY2=subkey1=value&subkey2=valueKEY3=subKey1=value&subkey3=value".componentsSeperatedByStrings(["KEY1", "KEY2", "KEY3"])

// ["KEY3": "=subKey1=value&subkey3=value", "KEY2": "=subkey1=value&subkey2=value", "KEY1": "=subKey1=value&subkey2=value"]

For Swift 2:

import Foundation

extension String {
func componentsSeperatedByStrings(ss: [String]) -> [String] {
let unshifted = ss
.flatMap { s in rangeOfString(s) }
.flatMap { r in [r.startIndex, r.endIndex] }
let inds = [startIndex] + unshifted + [endIndex]
return inds.startIndex
.stride(to: inds.endIndex, by: 2)
.map { i in (inds[i], inds[i+1]) }
.flatMap { (s, e) in s == e ? nil : self[s..<e] }
}
}


Related Topics



Leave a reply



Submit