I am styling some dynamic markdown, however the framework I am using for styling doesnt support nested tags for links.
I need to parse the string and close the styling markdown tags effectively this :
"__Some bold text [FIRST LINK](https://FIRSTLINK.COM \"FIRST LINK\"), more bold text.__\n\n additional text \n\n
*some italic text[SECOND LINK](https://SECONDLINK.COM) ending text,*"
to this:
"__Some bold text __[FIRST LINK](https://FIRSTLINK.COM \"FIRST LINK\")__, more bold text.__\n\n additional text \n\n
*some italic text*[SECOND LINK](https://SECONDLINK.COM)* ending text,*"
This is only really going to be for bold and italic text. I started going down the route of
var str = "__Some bold text [FIRST LINK](https://FIRSTLINK.COM \"FIRST LINK\"), more bold text.__\n\n additional text \n\n *some italic text[SECOND LINK](https://SECONDLINK.COM) ending text,*"
let bold = str.components(separatedBy: "__")
for var string in bold {
if let matchedIndex = string.index(of: "[") {
string.insert(contentsOf: "__", at: matchedIndex)
}
}
But wondered, is there a better way to do this in Swift?
Edit - for clarity - essentially I need to modify the existing string to have closed tags prior to a link tag and re opened after a link tag - this prevents the links from being nested with the style tags and allows the styler framework to apply attributed strings accordingly
EDIT --- in line with #Linus comment here is the results of the regex (note running these out side of an extension in order to be able to test in a playground
var str = "__Some bold text [FIRST LINK](https://FIRSTLINK.COM \"FIRST LINK\"), more bold text.__\n additional text \n *some italic text[SECOND LINK](https://SECONDLINK.COM) ending text,*\n__sfdadhfjkh [THIRD LINK](https://THIRDLINK.COM \"THIRD LINK\"), more bold text.__"
do {
var regex = try NSRegularExpression(pattern: "(\\[.*?\\))" , options: [.caseInsensitive])
var newString = regex.stringByReplacingMatches(in: str, options: [], range: NSMakeRange(0, str.utf16.count), withTemplate: "__$1__")
print("\nFirst regex __$1__ \n\n\(newString)")
regex = try NSRegularExpression(pattern: "(\\[.*?\\))" , options: [.caseInsensitive])
var newerString = regex.stringByReplacingMatches(in: str, options: [], range: NSMakeRange(0, str.utf16.count), withTemplate: "*$1*")
print("\nSecond Regex *$1* \n\n"+newerString)
} catch { print("ERROR: searchFor regex (\("(\\[.*?\\))")) on string (\(str)) failed") }
Printed results
First regex __$1__
__Some bold text __[FIRST LINK](https://FIRSTLINK.COM "FIRST LINK")__, more bold text.__
additional text
*some italic text__[SECOND LINK](https://SECONDLINK.COM)__ ending text,*
__sfdadhfjkh __[THIRD LINK](https://THIRDLINK.COM "THIRD LINK")__, more bold text.__
Second Regex *$1*
__Some bold text *[FIRST LINK](https://FIRSTLINK.COM "FIRST LINK")*, more bold text.__
additional text
*some italic text*[SECOND LINK](https://SECONDLINK.COM)* ending text,*
__sfdadhfjkh *[THIRD LINK](https://THIRDLINK.COM "THIRD LINK")*, more bold text.__
I need to have both Italic and strong tags amended on the same string in order to pass it to a view to be styled
I'm using the following String extension that allows you to find strings that match a certain regex pattern and replace it with some other string:
extension String {
mutating func replaceOccurrence(ofPattern pattern: String, with replacementString: String) {
do {
let regex = try NSRegularExpression(pattern: pattern, options: [.caseInsensitive])
self = regex.stringByReplacingMatches(in: self, options: [], range: NSMakeRange(0, utf16.count), withTemplate: replacementString)
} catch { print("ERROR: searchFor regex (\(pattern)) on string (\(self)) failed") }
}
}
Then, you could replace (\[.*?\)) with __$1__, like this:
str.replaceOccurrence(ofPattern: "(\\[.*?\\))", with: "__$1__")
Explanation
...in case you're unfamiliar with regular expressions:
The regex:
( - opening parenthesis, that creates a new group which is later used to insert the matched string back into the replacement string
\[ - matches a bracket; needs to be escaped using \ to disable the bracket's regex meaning & match the actual character instead
.* - matches any character...
? - ...until...
\) - ...the next closing parenthesis; this one also needs to be escaped to match the actual character, and not create a new group
) - closes the group
The replacement:
__ - your replacement string: opening bold range in this case
$1 - inserts the previously matched group here
__ - again, your replacement string: closing bold range in this case
Fun-Fact: in Swift, you need to escape escaping characters, like \\ to make sure the code compiles, because Xcode thinks, you're trying to escape a character from the string at compile-time.
That's why the regex isn't (\[.*?\)), but (\\[.*?\\)).
Related
How can I match a break line from OCR text using regex?
For example I have this text:
"NAME JESUS LASTNAME"
I want to find a match with NAME and then get the next two lines
if (line.text.range(of: "^NAME+\\n", options: .regularExpression) != nil){
let name = line.text
print(name)
}
You can use a positive look behind to find NAME followed by a new line, and try to match a line followed by any text that ends on a new line or the end of a string "(?s)(?<=NAME\n).*\n.*(?=$|\n)":
For more info about the regex above you can check this
Playground testing:
let str = "NAME\nJESUS\nLASTNAME"
let pattern = "(?s)(?<=NAME\n).*\n.*(?=$|\n)"
if let range = str.range(of: pattern, options: .regularExpression) {
let text = String(str[range])
print(text)
}
This will print
JESUS
LASTNAME
You can use
(?m)(?<=^NAME\n).*\n.*
See the regex demo. Details:
(?m) - a multiline option making ^ match start of a line
(?<=^NAME\n) - a positive lookbehind that matches a location that is immediately preceeded with start of a line, NAME and then a line feed char
.*\n.* - two subsequent lines (.* matches zero or more chars other than line break chars as many as possible).
See the Swift fiddle:
import Foundation
let line_text = "NAME\nJESUS\nLASTNAME"
if let rng = line_text.range(of: #"(?m)(?<=^NAME\n).*\n.*"#, options: .regularExpression) {
print(String(line_text[rng]))
}
// => JESUS
// LASTNAME
I am facing an issue with replacing some characters in an attributed string with some attributed characters.
let replaceString = NSMutableAttributedString(string: playerNames[index], attributes: attributes)
taskText.mutableString.replaceOccurrences(of: searchString[Int(index)], with: playerNames[index], options: .caseInsensitive, range: NSRange(location: 0, length: taskText.length))
The problem is, that I can't add the replaceString to the replaceOccurrences function because I can't use attributed strings here. Is there any method other method?
I just want to replace some "#1" and "#2" strings with some names like "Anna" but with bold font to make it more visible.
I have the RSS page with the html tag like this:
<description>
<![CDATA[
<a href='https://www.24h.com.vn/bong-da/psg-trao-than-dong-mbappe-sieu-luong-bong-chi-kem-messi-real-vo-mong-c48a1112120.html' title='PSG trao thần đồng Mbappe siĂªu lÆ°Æ¡ng bổng: Chỉ kĂ©m Messi, Real vỡ má»™ng'><img width='130' height='100' src='https://image.24h.com.vn/upload/4-2019/images/2019-12-27/1577463916-359-thumbnail.jpg' alt='PSG trao thần đồng Mbappe siĂªu lÆ°Æ¡ng bổng: Chỉ kĂ©m Messi, Real vỡ má»™ng' title='PSG trao thần đồng Mbappe siĂªu lÆ°Æ¡ng bổng: Chỉ kĂ©m Messi, Real vỡ má»™ng' /></a><br />PSG trong ná»— lá»±c giữ chĂ¢n “sĂ¡t thủ†Kylian Mbappe, sẵn sĂ ng tăng lÆ°Æ¡ng khổng lồ - má»™t Ä‘á»™ng thĂ¡i nhằm xua Ä‘uổi Real Madrid.
]]>
</description>
Please help me how can i get the value of src to show the image. I also try Getting img url from RSS feed swift but it doesn't work. Here is my code to get src (the code always run to image = "nil"):
let regex: NSRegularExpression = try! NSRegularExpression(pattern: "<img.*?src=\"([^\"]*)\"", options: .caseInsensitive)
let range = NSMakeRange(0, description.count)
if let textCheck = regex.firstMatch(in: description, options: .withoutAnchoringBounds, range: range) {
let text = (description as NSString).substring(with: textCheck.range(at: 1))
image = text
} else {
image = "nil"
}
Thank for your helping !
You need to change your regex to be able to match single-quotes as well, not just double quotes, since the html string you're trying to parse contains single quotes, not double quotes like the one in the linked Q&A.
let regex: NSRegularExpression = try! NSRegularExpression(pattern: "<img.*?src=[\"\']([^\"\']*)[\"\']", options: .caseInsensitive)
If you are sure you only need to match single quotes, you can simplify the pattern by replacing [\"\'] with \'. Currently, the regex pattern will match both single and double quotes.
I referred this SO post to remove whitespaces and newline characters from a string. But in my string, I may have extra whitespaces as well as extra newline characters. I want to remove the unnecessary \n's and whitespaces from that string.
But if there is a string like so..."This \n is a st\tri\rng" then I don't want Thisisastring as the result but instead something like this..
This is a string
To replace contiguous spaces with a single space, replace Regular Expression \s+ with a single space:
let str = "This \n\n is a string"
if let regex = try? NSRegularExpression(pattern: "\\s+", options: NSRegularExpression.Options.caseInsensitive)
{
let result = regex.stringByReplacingMatches(in: str, options: [], range: NSMakeRange(0, str.count), withTemplate: " ")
print(result) //output: "This is a string"
}
I try to include ' symbol to Regular Expressions
I use this function
func matches(for regex: String, in text: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex)
let results = regex.matches(in: text,
range: NSRange(text.startIndex..., in: text))
return results.map {
text.substring(with: Range($0.range, in: text)!)
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
and this Regular Expressions
let matched = matches(for: "^[‘]|[0-9]|[a-zA-Z]+$", in: string)
when I search I can find numbers and english letters
but not ' symbol
I guess that what you really want is this:
"['0-9a-zA-Z]+"
Note that I have removed the ^ (text start) and $ (text end) characters because then your whole text would have to match.
I have merged the groups because otherwise you would not match the text as a whole word. You would get separate apostrophe and then the word.
I have changed the ‘ character into the proper ' character. The automatic conversion from the simple apostrophe is caused by iOS 11 Smart Punctuation. You can turn it off on an input using:
input.smartQuotesType = .no
See https://developer.apple.com/documentation/uikit/uitextinputtraits/2865931-smartquotestype