Get the string up to a specific character - swift

var hello = "hello, how are you?"
var hello2 = "hello, how are you #tom?"
i want to delete every letter behind the # sign.
result should be
var hello2 = "hello, how are you #tom?"
->
hello2.trimmed()
print(hello2.trimmed())
-> "hello, how are you"
Update
As i want to use it to link multiple users and replace the space behind #sign with the correct name, I always need the reference to the latest occurrence of the #sign to replace it.
text3 = "hey i love you #Tom #Marcus #Peter"
Example what the final version should look like
to start off
var text = "hello #tom #mark #mathias"
i want to always get the index of the latest # sign in the text

Expanding on #appzYourLife answer, the following will also trim off the whitespace characters after removing everything after the # symbol.
import Foundation
var str = "hello, how are you #tom"
if str.contains("#") {
let endIndex = str.range(of: "#")!.lowerBound
str = str.substring(to: endIndex).trimmingCharacters(in: .whitespacesAndNewlines)
}
print(str) // Output - "hello, how are you"
UPDATE:
In response to finding the last occurance of the # symbol in the string and removing it, here is how I would approach it:
var str = "hello, how are you #tom #tim?"
if str.contains("#") {
//Reverse the string
var reversedStr = String(str.characters.reversed())
//Find the first (last) occurance of #
let endIndex = reversedStr.range(of: "#")!.upperBound
//Get the string up to and after the # symbol
let newStr = reversedStr.substring(from: endIndex).trimmingCharacters(in: .whitespacesAndNewlines)
//Store the new string over the original
str = String(newStr.characters.reversed())
//str = "hello, how are you #tom"
}
Or looking at #appzYourLife answer use range(of:options:range:locale:) instead of literally reversing the characters
var str = "hello, how are you #tom #tim?"
if str.contains("#") {
//Find the last occurrence of #
let endIndex = str.range(of: "#", options: .backwards, range: nil, locale: nil)!.lowerBound
//Get the string up to and after the # symbol
let newStr = str.substring(from: endIndex).trimmingCharacters(in: .whitespacesAndNewlines)
//Store the new string over the original
str = newStr
//str = "hello, how are you #tom"
}
As an added bonus, here is how I would approach removing every # starting with the last and working forward:
var str = "hello, how are you #tom and #tim?"
if str.contains("#") {
while str.contains("#") {
//Reverse the string
var reversedStr = String(str.characters.reversed())
//Find the first (last) occurance of #
let endIndex = reversedStr.range(of: "#")!.upperBound
//Get the string up to and after the # symbol
let newStr = reversedStr.substring(from: endIndex).trimmingCharacters(in: .whitespacesAndNewlines)
//Store the new string over the original
str = String(newStr.characters.reversed())
}
//after while loop, str = "hello, how are you"
}

let text = "hello, how are you #tom?"
let trimSpot = text.index(of: "#") ?? text.endIndex
let trimmed = text[..<trimSpot]
Since a string is a collection of Character type, it can be accessed as such. The second line finds the index of the # sign and assigns its value to trimSpot, but if it is not there, the endIndex of the string is assigned through the use of the nil coalescing operator
??
The string, or collection of Characters, can be provided a range that will tell it what characters to get. The expression inside of the brackets,
..<trimSpot
is a range from 0 to trimSpot-1. So,
text[..<trimSpot]
returns an instance of type Substring, which points at the original String instance.

You need to find the range of the "#" and then use it to create a substring up to the index before.
import Foundation
let text = "hello, how are you #tom?"
if let range = text.range(of: "#") {
let result = text.substring(to: range.lowerBound)
print(result) // "hello, how are you "
}
Considerations
Please note that, following the logic you described and using the input text you provided, the output string will have a blank space as last character
Also note that if multiple # are presente in the input text, then the first occurrence will be used.
Last index [Update]
I am adding this new section to answer the question you posted in the comments.
If you have a text like this
let text = "hello #tom #mark #mathias"
and you want the index of the last occurrency of "#" you can write
if let index = text.range(of: "#", options: .backwards)?.lowerBound {
print(index)
}

Try regular expressions, they are much safer (if you know what you are doing...)
let hello2 = "hello, how are you #tom, #my #next #victim?"
let deletedStringsAfterAtSign = hello2.replacingOccurrences(of: "#\\w+", with: "", options: .regularExpression, range: nil)
print(deletedStringsAfterAtSign)
//prints "hello, how are you , ?"
And this code removes exactly what you need and leaves the characters after the strings clear, so you can see the , and ? still being there. :)
EDIT: what you asked in comments to this answer:
let hello2 = "hello, how are you #tom, #my #next #victim?"
if let elementIwannaAfterEveryAtSign = hello2.components(separatedBy: " #").last
{
let deletedStringsAfterAtSign = hello2.replacingOccurrences(of: "#\\w+", with: elementIwannaAfterEveryAtSign, options: .regularExpression, range: nil)
print(deletedStringsAfterAtSign)
//prints hello, how are you victim?, victim? victim? victim??
}

Related

How to replace Swift substring with an Un-Equal substring by using location?

Can’t use occurrence of pattern as text can change at any instance.
var originalString = "Hi there <un>"
var stringToPut = "Some Amazing Name"
// Change string between 10th index and 13th to the following.
var requiredString = "Hi there <Some Amazing Name>"
This is very easy for just 1 character or when the length of the replacing string is same. But breaks when the substrings are unequal in length as the length of parent string changes and exact location references cannot be made.
Hopefully this works.
let originalString = "Hi there <un>"
let subString = "Some Amazing Name"
let characters = Array(originalString)
let firstPart = characters[0..<9]
let lastPart = characters[13..<characters.count]
let finaString = ("\(String(firstPart))\(subString)\(String(lastPart))")
Or you can use replaceSubrange:
var originalString = "Hi there <un>"
var stringToPut = "Some Amazing Name"
// Change string between 10th index and 13th to the following.
var requiredString = "Hi there <Some Amazing Name>"
let startIndex = originalString.index(originalString.startIndex, offsetBy: 9)
let endIndex = originalString.index(originalString.startIndex, offsetBy: 12)
originalString.replaceSubrange(startIndex...endIndex, with: "Some Amazing Name") // "Hi there Some Amazing Name"
If you know the format of <un> the simplest method would be:
let newString = originalString.replacingOccurrences(of: "<un>", with: stringToPut, options: .literal, range: nil)

How to remove characters in String Swift 3?

Code to get the string before a certain character:
let string = "Hello World"
if let range = string.range(of: "World") {
let firstPart = string[string.startIndex..<range.lowerBound]
print(firstPart) // print Hello
}
To begin with, I have a program that converts Hex float to a Binary float and I want to remove all "0" from Binary string answer until first "1". Example:
Any ideas?
You can use Regular Expression:
var str = "001110111001"
str = str.replacingOccurrences(of: "0", with: "", options: [.anchored], range: nil)
The anchored option means search for 0s at the start of the string only.

How to get substring with specific ranges in Swift 4?

This is using the example code from the official Swift4 doc
let greeting = "Hi there! It's nice to meet you! 👋"
let endOfSentence = greeting.index(of: "!")!
let firstSentence = greeting[...endOfSentence]
// firstSentence == "Hi there!"
But lets say let greeting = "Hello there world!"
and I want to retrieve only the second word (substring) in this sentence? So I only want the word "there".
I've tried using "world!" as an argument like
let endOfSentence = greeting.index(of: "world!")! but Swift 4 Playground doesn't like that. It's expecting 'Character' and my argument is a string.
So how can I get a substring of a very precise subrange? Or get nth word in a sentence for greater use in the future?
You can search for substrings using range(of:).
import Foundation
let greeting = "Hello there world!"
if let endIndex = greeting.range(of: "world!")?.lowerBound {
print(greeting[..<endIndex])
}
outputs:
Hello there
EDIT:
If you want to separate out the words, there's a quick-and-dirty way and a good way. The quick-and-dirty way:
import Foundation
let greeting = "Hello there world!"
let words = greeting.split(separator: " ")
print(words[1])
And here's the thorough way, which will enumerate all the words in the string no matter how they're separated:
import Foundation
let greeting = "Hello there world!"
var words: [String] = []
greeting.enumerateSubstrings(in: greeting.startIndex..<greeting.endIndex, options: .byWords) { substring, _, _, _ in
if let substring = substring {
words.append(substring)
}
}
print(words[1])
EDIT 2: And if you're just trying to get the 7th through the 11th character, you can do this:
import Foundation
let greeting = "Hello there world!"
let startIndex = greeting.index(greeting.startIndex, offsetBy: 6)
let endIndex = greeting.index(startIndex, offsetBy: 5)
print(greeting[startIndex..<endIndex])
For swift4,
let string = "substring test"
let start = String.Index(encodedOffset: 0)
let end = String.Index(encodedOffset: 10)
let substring = String(string[start..<end])
In Swift 5 encodedOffset (swift 4 func) is deprecated.
You will need to use utf160Offset
// Swift 5
let string = "Hi there! It's nice to meet you!"
let startIndex = 10 // random for this example
let endIndex = string.count
let start = String.Index(utf16Offset: startIndex, in: string)
let end = String.Index(utf16Offset: endIndex, in: string)
let substring = String(string[start..<end])
prints -> It's nice to meet you!
There is one mistake in the first answer.
Range<String.Index>.upperBound
The upperBound property should be the endIndex
For Example:
let text = "From Here Hello World"
if let result = text.range(of: "Hello World") {
let startIndex = result.upperBound
let endIndex = result.lowerBound
print(String(text[startIndex..<endIndex])) //"Hello World"
}
the simplest way I use is :
var str = "abcdefg"
String(Array(str)[2...4])
Old habits die hard. I did it the "Java" way and split the string up by spaces, then accessed the second word.
print(greeting.split(separator: " ")[1]) // "there /n"

Swift shift range by 2 or exclude the found character

this might be a basic question but I am having a hard time not including the - in the second
var title1 = "I will be part of string 1 - I am part of string 2"
let end = title1.range(of: "-", options: .backwards)?.lowerBound
let firstPartRange = title1.startIndex..<end!
var secondPart = title1.substring(with: firstPartRange) // Gives me "I will be part of string 1" which is correct
title1.substring(from: end!) // however this guy gives me "- I am part of string 2" & I only want to get "I am part of string 2" without the space and dash in front
Can I shift the range or change it's lowerBound somehow?. I know I can user separate component by function here but would like to learn how to offset my range
You just need to get the index after end or offset it by 2. Note that you should also make sure it doesn't pass the end index using the method index(theIndex, offsetBy: n, limitedBy: endIndex)
let title1 = "I will be part of string 1 - I am part of string 2"
if let end = title1.range(of: "-", options: .backwards)?.lowerBound {
let firstPartRange = title1.startIndex..<end
let secondPart = title1.substring(with: firstPartRange) // "I will be part of string 1 "
title1.substring(from: title1.index(after: end)) // " I am part of string 2"
// or to offset it by two you should also make sure it doesn't pass the end index
title1.substring(from: title1.index(end, offsetBy: 2, limitedBy: title1.endIndex) ?? title1.endIndex) // "I am part of string 2"
}
You are looking for index(_:offsetBy:). This is a method of the original string, like this:
var title1 = "I will be part of string 1 - I am part of string 2"
let end = title1.range(of: "-", options: .backwards)!.lowerBound
let ix = title1.index(end, offsetBy: 2)
title1.substring(from: ix) // "I am part of string 2"
You can separate the contents of string by using components(separatedBy: String) method which will return you with array of separated strings then you can remove white spaces from the last element.
var title1 = "I will be part of string 1 - I am part of string 2"
print(title1.components(separatedBy: "-").last!.trimmingCharacters(in: .whitespacesAndNewlines))
it will give you the desired result
"I am part of string 2"
Hope it helps!
You could use the components function and include the spaces in the separator:
title1.components(separatedBy:" - ")
As #matt suggest, you are looking for index(_:offsetBy:)
A little bit different approach using Swift standard library only (no Foundation)
let str = "I will be part of string 1 - I am part of string 2"
let parts = str.characters.split(separator: "-").map(String.init)
if you would like to trim all extra spaces
let partsTrimmed = parts.map {
$0.characters.split(separator: " ").map(String.init).joined(separator: " ")
}

Find characters inside quotation marks in String

I'm trying to pull out the parts of a string that are in quotation marks, i.e. in "Rouge One" is an awesome movie I want to extract Rouge One.
This is what I have so far but can't figure out where to go from here: I create a copy of the text so that I can remove the first quotation mark so that I can get the index of the second.
if text.contains("\"") {
guard let firstQuoteMarkIndex = text.range(of: "\"") else {return}
var textCopy = text
let textWithoutFirstQuoteMark = textCopy.replacingCharacters(in: firstQuoteMarkIndex, with: "")
let secondQuoteMarkIndex = textCopy.range(of: "\"")
let stringBetweenQuotes = text.substring(with: Range(start: firstQuoteMarkIndex, end: secondQuoteMarkIndex))
}
There is no need to create copies or to replace substrings for this task.
Here is a possible approach:
Use text.range(of: "\"") to find the first quotation mark.
Use text.range(of: "\"", range:...) to find the second quotation mark, i.e. the first one after the range found in step 1.
Extract the substring between the two ranges.
Example:
let text = " \"Rouge One\" is an awesome movie"
if let r1 = text.range(of: "\""),
let r2 = text.range(of: "\"", range: r1.upperBound..<text.endIndex) {
let stringBetweenQuotes = text.substring(with: r1.upperBound..<r2.lowerBound)
print(stringBetweenQuotes) // "Rouge One"
}
Another option is a regular expression search with "positive lookbehind" and "positive lookahead" patterns:
if let range = text.range(of: "(?<=\\\").*?(?=\\\")", options: .regularExpression) {
let stringBetweenQuotes = text.substring(with: range)
print(stringBetweenQuotes)
}
var rouge = "\"Rouge One\" is an awesome movie"
var separated = rouge.components(separatedBy: "\"") // ["", "Rouge One", " is an awesome movie"]
separated.dropFirst().first
I would use .components(separatedBy:)
let stringArray = text.components(separatedBy: "\"")
Check if stringArray count is > 2 (there is at least 2 quotes).
Check if stringArray count is odd, aka count % 2 == 1.
If it is odd, all the even indices are between 2 quotes and they are what you want.
If it is even, all the even indices - 1 are between 2 quotes (the last one doesn't have an end quote).
This will allow you to also capture multiple sets of quoted strings, like:
"Rogue One" is a "Star Wars" movie.
Another option is to use regular expressions to find pairs of quotes:
let pattern = try! NSRegularExpression(pattern: "\\\"([^\"]+)\\\"")
// Small helper methods making it easier to work with enumerateMatches(in:...)
extension String {
subscript(utf16Range range: Range<Int>) -> String? {
get {
let start = utf16.index(utf16.startIndex, offsetBy: range.lowerBound)
let end = utf16.index(utf16.startIndex, offsetBy: range.upperBound)
return String(utf16[start..<end])
}
}
var fullUTF16Range: NSRange {
return NSRange(location: 0, length: utf16.count)
}
}
// Loop through *all* quoted substrings in the original string.
let str = "\"Rogue One\" is an awesome movie"
pattern.enumerateMatches(in: str, range: str.fullUTF16Range) { (result, flags, stop) in
// rangeAt(1) is the range representing the characters in the 1st
// capture group of the regular expression: ([^"]+)
if let result = result, let range = result.rangeAt(1).toRange() {
print("This was in quotes: \(str[utf16Range: range] ?? "<bad range>")")
}
}