Swift read large file into an array - swift

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

Related

swift replacingOccurrences regular expression with number calculation

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

Swift: Byte array to decimal value

In my project, I communicate with a bluetooth device, the bluetooth device must send me a timestamp second, I received in byte:
[2,6,239]
When I convert converted to a string:
let payloadString = payload.map {
String(format: "%02x", $0)
}
Output:
["02", "06","ef"]
When I converted from the website 0206ef = 132847 seconds
How can I directly convert my aray [2,6,239] in second (= 132847 seconds)?
And if it's complicated then translate my array ["02", "06,"ef"] in second (= 132847 seconds)
The payload contains the bytes of the binary representation of the value.
You convert it back to the value by shifting each byte into its corresponding position:
let payload: [UInt8] = [2, 6, 239]
let value = Int(payload[0]) << 16 + Int(payload[1]) << 8 + Int(payload[2])
print(value) // 132847
The important point is to convert the bytes to integers before shifting, otherwise an overflow error would occur. Alternatively,
with multiplication:
let value = (Int(payload[0]) * 256 + Int(payload[1])) * 256 + Int(payload[2])
or
let value = payload.reduce(0) { $0 * 256 + Int($1) }
The last approach works with an arbitrary number of bytes – as long as
the result fits into an Int. For 4...8 bytes you better choose UInt64
to avoid overflow errors:
let value = payload.reduce(0) { $0 * 256 + UInt64($1) }
payloadString string can be reduced to hexStr and then converted to decimal
var payload = [2,6,239];
let payloadString = payload.map {
String(format: "%02x", $0)
}
//let hexStr = payloadString.reduce(""){$0 + $1}
let hexStr = payloadString.joined()
if let value = UInt64(hexStr, radix: 16) {
print(value)//132847
}

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)
}

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.

Difficulty getting readLine() to work as desired on HackerRank

I'm attempting to submit the HackerRank Day 6 Challenge for 30 Days of Code.
I'm able to complete the task without issue in an Xcode Playground, however HackerRank's site says there is no output from my method. I encountered an issue yesterday due to browser flakiness, but cleaning caches, switching from Safari to Chrome, etc. don't seem to resolve the issue I'm encountering here. I think my problem lies in inputString.
Task
Given a string, S, of length N that is indexed from 0 to N-1, print its even-indexed and odd-indexed characters as 2 space-separated strings on a single line (see the Sample below for more detail).
Input Format
The first line contains an integer, (the number of test cases).
Each line of the subsequent lines contain a String, .
Constraints
1 <= T <= 10
2 <= length of S < 10,000
Output Format
For each String (where 0 <= j <= T-1), print S's even-indexed characters, followed by a space, followed by S's odd-indexed characters.
This is the code I'm submitting:
import Foundation
let inputString = readLine()!
func tweakString(string: String) {
// split string into an array of lines based on char set
var lineArray = string.components(separatedBy: .newlines)
// extract the number of test cases
let testCases = Int(lineArray[0])
// remove the first line containing the number of unit tests
lineArray.remove(at: 0)
/*
Satisfy constraints specified in the task
*/
guard lineArray.count >= 1 && lineArray.count <= 10 && testCases == lineArray.count else { return }
for line in lineArray {
switch line.characters.count {
// to match constraint specified in the task
case 2...10000:
let characterArray = Array(line.characters)
let evenCharacters = characterArray.enumerated().filter({$0.0 % 2 == 0}).map({$0.1})
let oddCharacters = characterArray.enumerated().filter({$0.0 % 2 == 1}).map({$0.1})
print(String(evenCharacters) + " " + String(oddCharacters))
default:
break
}
}
}
tweakString(string: inputString)
I think my issue lies the inputString. I'm taking it "as-is" and formatting it within my method. I've found solutions for Day 6, but I can't seem to find any current ones in Swift.
Thank you for reading. I welcome thoughts on how to get this thing to pass.
readLine() reads a single line from standard input, which
means that your inputString contains only the first line from
the input data. You have to call readLine() in a loop to get
the remaining input data.
So your program could look like this:
func tweakString(string: String) -> String {
// For a single input string, compute the output string according to the challenge rules ...
return result
}
let N = Int(readLine()!)! // Number of test cases
// For each test case:
for _ in 1...N {
let input = readLine()!
let output = tweakString(string: input)
print(output)
}
(The forced unwraps are acceptable here because the format of
the input data is documented in the challenge description.)
Hi Adrian you should call readLine()! every row . Here an example answer for that challenge;
import Foundation
func letsReview(str:String){
var evenCharacters = ""
var oddCharacters = ""
var index = 0
for char in str.characters{
if index % 2 == 0 {
evenCharacters += String(char)
}
else{
oddCharacters += String(char)
}
index += 1
}
print (evenCharacters + " " + oddCharacters)
}
let rowCount = Int(readLine()!)!
for _ in 0..<rowCount {
letsReview(str:String(readLine()!)!)
}