swift replacingOccurrences regular expression with number calculation - swift

I have [size=some_number%]some_text[/size], and I want to replace it with
<font size="some_number*font_size">sometext</font>
where font_size is some int variableI know how to extract some_number and some_text with regular expression, but how can I do the multiplie calculation ? Is there a way to do it in swift justing using replacingOccurrences?
#"\[size=(d+)%\]([\s\S]*?)\[\/size\]"#

Is there a way to do it in swift justing using replacingOccurrences?
ICU regular expressions don't do math. You'll have to deal with these one at a time, doing the search, performing the calculation, and then doing the replace for that occurrence, repeating that in a loop. Loop in reverse to avoid index-shifting issues.
For instance:
var s = """
yo[size=6%]ooo[/size]heyho[size=10%]yyy[/size]ha
"""
let font_size = 10
let reg = try! NSRegularExpression(pattern: "\\[size=(\\d+)%\\]([\\s\\S]*?)\\[\\/size\\]", options: [])
let matches = reg.matches(in: s, options: [], range: NSRange(location: 0, length: s.utf16.count))
let rev = matches.reversed() // work backwards for replacement
for match in rev {
let r = match.range
let size = s[Range(match.range(at:1), in:s)!]
let text = s[Range(match.range(at:2), in:s)!]
let prefix = "<font size=\""
let num = String(Int(size)!*font_size)
let rest = "\">" + text + "</font>"
s = s.replacingCharacters(in: Range(r, in:s)!, with: prefix + num + rest)
}
print(s)
Now s is
yo<font size="60">ooo</font>heyho<font size="100">yyy</font>ha

Related

Swift split substring based on word wrap

junior developer here. I am currently trying to achieve a substring that is split every n characters of a String.
This is my code for the function
public func split(every: Int) -> [String] {
var result = [String]()
for i in stride(from: 0, to: self.count, by: every) {
let startIndex = self.index(self.startIndex, offsetBy: i)
let endIndex = self.index(startIndex, offsetBy: every, limitedBy: self.endIndex) ?? self.endIndex
result.append(String(self[startIndex..<endIndex]))
}
return result
}
The above code works as expected. But there is one lacking from the code above, which is the word wrapping. Here is the sample String
let itemName = "Japanese Matcha SM w RB -L Special Edition And Americano MS w Brown Sugar Limited Edition"
print(itemName.split(every: 26))
The result will be
["Japanese Matcha SM w RB -L", " Special Edition And Ameri", "cano MS w Brown Sugar Limi", "ted Edition"]
Notice the
[" Special Edition And Ameri"], ["cano MS w Brown Sugar Limi"]
I am trying to figure out how to do the word wrap algorithm based on every n character, but couldn't find any clue.
For example, from above case, how to generate the array becomes,
[" Special Edition And"], ["Americano MS w Brown"], ["Sugar"]
So as you can see, the algorithm might check whether every n characters has a word that is being cut out (dynamic check based on the n characters), hence will move the cut word into the next array.
So in that case, the algorithm will cleverly bypass the every n character, might be less, but not more than n characters, if there is any word not being wrapped.
Is my explanation clear? Can anyone guide me please? Thanks
This is some simple implementation of this algorithm, you can start with that.
First we cut string by words, then add them to temporary string until we meet characters limit.
let itemName = "Japanese Matcha SM w RB -L Special Edition And Americano MS w Brown Sugar Limited Edition"
let table = itemName.split(separator: " ")
let limit = 26
var tempString = ""
var finalResult: [String] = []
for item in table {
tempString += item + " "
if tempString.count >= limit {
finalResult.append(tempString)
tempString = ""
}
}
print(finalResult)
How about this?
extension String {
func split(every: Int) -> [String] {
var result = [String]()
let words = self.split(separator: " ")
var line = String(words.first!)
words.dropFirst().forEach { word in
let word = " " + String(word)
if line.count + word.count <= every {
line.append(word)
} else {
result.append(line)
line = word
}
}
result.append(line)
return result
}
}

How to split the string and number from set of String in swift

here i have some set of string. i have to take particular string continue with number
Example :
1). \n\nRanjitha, 20\n\n\"N0? yE¥ WQRKINQ Il. iM gm N-LI?\']\" ......\n\nYou\'ve crossed paths 10 times\n\nPoth btDtlun\n\n
2).\n\nShruthi, 21\n\n
3).\n\nKhushbu,22\n©\n\n
4). \n\nVanitha, 22 \'r-e\'\"\n\nC? Bishop Cotton Women‘s Christian College\n\n® 5 kilometres away\n\n
This is the format i get from image using OCR.
i want to split the name and age separate.
Expecting outout : 1 st String - > name : Ranjitha , age : 20
can you any one tell the logic ?
A possible solution is regular expression.
The pattern searches for
One or more characters (will be captured)
A comma
An optional whitespace character
One or more digits (will be captured)
let string = """
1). \n\nRanjitha, 20\n\n\"N0? yE¥ WQRKINQ Il. iM gm N-LI?\']\" ......\n\nYou\'ve crossed paths 10 times\n\nPoth btDtlun\n\n
2).\n\nShruthi, 21\n\n
3).\n\nKhushbu,22\n©\n\n
4). \n\nVanitha, 22 \'r-e\'\"\n\nC? Bishop Cotton Women‘s Christian College\n\n® 5 kilometres away\n\n
"""
let pattern = "(\\w+),\\s?(\\d+)"
do {
let regex = try NSRegularExpression(pattern: pattern)
let matches = regex.matches(in: string, range: NSRange(string.startIndex..., in: string))
for match in matches {
let nameRange = Range(match.range(at: 1), in: string)!
print("name", string[nameRange])
let ageRange = Range(match.range(at: 2), in: string)!
print("age", string[ageRange])
}
} catch {
print("Regex Error:", error)
}

Swift read large file into an array

I'm trying to read a file into an array of strings in the Playground
let path = XCPlaygroundSharedDataDirectoryURL.appendingPathComponent("test.txt")
let data = try Data(contentsOf: path!)
let returnData = String(data: data, encoding: .utf8)
var matrixData = returnData!.components(separatedBy: "\n").first!
let rows = Int ( matrixData.components(separatedBy: " ").first! )!
let columns = Int( matrixData.components(separatedBy: " ")[1] )!
let realData = returnData!.components(separatedBy: "\n").dropFirst().joined()
realData.count
let inputString = realData.components(separatedBy: " ")
The first two numbers in the input file indicate that it is a grid of A * B.
The "real data" indicates that the array has begun.
For a small input file (4 * 4) realdata.count reveals 16 (correct)
For a large input file (1000 * 1000) realdata.count is 999001 which is WRONG.
Why?
Is it to do with a limitation on array of Strings?
For this to work I stripped the input string of \n first as I don't use them and then proceeded from there.
The only strange issue was an extra character at the end of the string, must have been a \n at the end of the input file.
let stripped = returnData!.replacingOccurrences(of: "\n", with: " ")
var inputString = stripped.components(separatedBy: " ").dropFirst(2)
inputString = inputString.dropLast()
//inputString is now the correct length

How to separate an variable array into smaller arrays using swift

I have a question I cannot figure out. Is there an easier way to separate an array into other arrays. In the code below I do a Trace route and filter out everything except the ping rates.
Then I break down that array into individual nodes. The only downside is if it only takes 2 hops then I get an error, "fatal error: Index out of range." because I have it set up to create 15 node arrays. I have also tried putting ..
let count = pingRate.count
if count < 15 {
aNodeArray += pingRate[14]
}
Is there an easier way? I wish there was a way to do a .count and then populate the Int the is required on each node.
let task = Process()
task.launchPath = "/bin/sh"
task.arguments = ["-c", "traceroute -nm 15 -q 1 8.8.8.8"]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
let output = NSString(data: data, encoding: String.Encoding.utf8.rawValue) as! String
var array = output.components(separatedBy: " ")
array = array.filter({ $0.contains(".")})
let pingRate: [[Double]] = array.enumerated().filter({ index, _ in
index % 2 != 0
}).map { [Double($0.1)!] }
let aNode = pingRate[0]
let bNode = pingRate[1]
let cNode = pingRate[2]
let dNode = pingRate[3]
let eNode = pingRate[4]
aNodeArray += aNode
bNodeArray += bNode
cNodeArray += cNode
dNodeArray += dNode
eNodeArray += eNode
Once I get that done then I sort and find the min, max, and average.
let sorted = aNodeArray.sorted()
let numbers = sorted
print(numbers)
let min = numbers[0]
var sum = 0 as Double
for number in numbers {
sum += number
}
let mean = Double(sum) /
Double (numbers.count)
let x = mean
let avg = round(1000.0 * x) / 1000.0
let maxBR = numbers.last! as Double
let max = round(1000.0 * maxBR) / 1000.0
print(min)
print(max)
print(avg)
Edit
I did stumble upon chunks and stride from...
Swift: what is the right way to split up a [String] resulting in a [[String]] with a given subarray size?
I am trying to figure a way to implement that for my needs
Edit
And looking at that code. Can anyone tell me why I get the Color spinning wheel until the process() is finished?
I know that there are more efficient algorithms to calculate your ping rate statistics but the code below should do the job.
let task = Process()
task.launchPath = "/bin/sh"
task.arguments = ["-c", "traceroute -nm 15 -q 1 8.8.8.8"]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
guard
let pingRates = String(data: data, encoding: .utf8)?
.components(separatedBy: " ")
.filter({ $0.contains(".") })
.flatMap({ Double($0) }),
pingRates.count > 0
else {
return
}
print("ping rate(min) = \(pingRates.min()!.rounded())")
print("ping rate(max) = \(pingRates.max()!.rounded())")
print("ping rate(avg) = \((pingRates.reduce(0, +) / Double(pingRates.count)).rounded())")
The trick to eliminating the IP addresses in your output is to let Double.init fail on them and then flatMap will remove those nil values.

Swift: String to ints without special function

I'm searching for a build-in function for converting a string like
"01092030" (format "[[dd]hh]mmss") to four individual integers.
dd => 01
hh => 09
mm => 20
ss => 30
dd and hh are optional. If missing they should be 0. I could do it by by developing a special function, but I like to use something like a regular expression.
I don't see the point of your "build-in function" or "regular expression" requirements, but whatever...
let s = "092030" as NSString
let pattern = "\\d\\d"
let reg = try! NSRegularExpression(pattern: pattern, options: [])
let matches = reg.matchesInString(s as String, options: [], range: NSMakeRange(0, s.length))
var result = matches.map {s.substringWithRange($0.range)}
while result.count < 4 {
result.insert("0", atIndex: 0)
}
// result is: ["0", "09", "20", "30"]
I'm also a little unclear on your output requirements. On the one hand, you say you want four "individual integers". But "09" is not an integer; it's a string representing an integer. So it seems to me that you actually want strings. If so, then result is the desired result. If not, then you need one more step:
let result2 = result.map{Int($0)!}
// result2 is: [0, 9, 20, 30]
Since the string contains date components you could alternatively use NSDateFormatter
let string = "01092030"
let dateFormatter = NSDateFormatter()
let dayMissing = string.characters.count < 8
let hourMissing = string.characters.count < 6
dateFormatter.dateFormat = hourMissing ? "mmss" : dayMissing ? "HHmmss" : "ddHHmmss"
let date = dateFormatter.dateFromString(string)!
let components = NSCalendar.currentCalendar().components([.Day, .Hour, .Minute, .Second], fromDate: date)
let second = components.second
let minute = components.minute
let hour = components.hour
let day = dayMissing ? 0 : components.day