I'm having trouble converting a String to Int in my Swift OS X Xcode project. I have some data saved in a text file in a comma delimited format. The contents of the text file is below:
1,Cessna 172,3,54.4,124,38.6112
(and a line break at the end)
I read the text file and seperate it, first by \n to get each line by itself, and then by , to get each element by itself. The code to do this is below:
if let dir : NSString = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true).first {
let path = dir.stringByAppendingPathComponent("FSPassengers/aircraft.txt")
do {
let content = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding)
if content != "" {
let astrContent:[String] = content.componentsSeparatedByString("\n")
for aeroplane in astrContent {
let aSeperated:[String] = aeroplane.componentsSeparatedByString(",")
print(aSeperated[0])
print(Int(aSeperated[0]))
//self.aAircraft.append(Aircraft(id: aSeperated[0], type: aSeperated[1], passengerCapacity: Int(aSeperated[2])!, cargoCapacityKg: Double(aSeperated[3])!, cruiseSpeed: Int(aSeperated[4])!, fuelLitresPerHour: Double(aSeperated[5])!))
}
}
}
catch {
print("Error")
}
}
The end result here will be to assign each record (each line of the text file) into the array aAircraft. This array is made up of a custom object called Aircraft. The custom class is below:
class Aircraft: NSObject {
var id:Int = Int()
var type:String = String()
var passengerCapacity:Int = Int()
var cargoCapacityKg:Double = Double()
var cruiseSpeed:Int = Int()
var fuelLitresPerHour:Double = Double()
override init() {}
init(id:Int, type:String, passengerCapacity:Int, cargoCapacityKg:Double, cruiseSpeed:Int, fuelLitresPerHour:Double) {
self.id = id
self.type = type
self.passengerCapacity = passengerCapacity
self.cargoCapacityKg = cargoCapacityKg
self.cruiseSpeed = cruiseSpeed
self.fuelLitresPerHour = fuelLitresPerHour
}
}
In the first code extract above, where I split the text file contents and attempt to assign them into the array, you will see that I have commented out the append line. I have done this to get the application to compile, at the moment it is throwing me errors.
The error revolves around the conversion of the String values to Int and Double values as required. For example, Aircraft.id, or aSeperated[0] needs to be an Int. You can see that I use the line Int(aSeperated[0]) to convert the String to Int in order to assign it into the custom object. However, this line of code is failing.
The two print statements in the first code extract output the following values:
1
Optional(1)
If I add a ! to the end of the second print statement to make them:
print(aSeperated[0])
print(Int(aSeperated[0])!)
I get the following output:
I understand what the error means, that it tried to unwrap an optional value because I force unwrapped it, and it couldn't find an Int value within the string I passed to it, but I don't understand why I am getting the error. The string value is 1, which is very clearly an integer. What am I doing wrong?
Because Casena 172 is not convertible to an Int. You also have other decimal numbers which you will lose precision when casting them to Int. Use NSScanner to create an initializer from a CSV string:
init(csvString: String) {
let scanner = NSScanner(string: csvString)
var type: NSString?
scanner.scanInteger(&self.id)
scanner.scanLocation += 1
scanner.scanUpToString(",", intoString: &type)
self.type = type as! String
scanner.scanLocation += 1
scanner.scanInteger(&self.passengerCapacity)
scanner.scanLocation += 1
scanner.scanDouble(&self.cargoCapacityKg)
scanner.scanLocation += 1
scanner.scanInteger(&self.cruiseSpeed)
scanner.scanLocation += 1
scanner.scanDouble(&self.fuelLitresPerHour)
}
Usage:
let aircraft = Aircraft(csvString: "1,Cessna 172,3,54.4,124,38.6112")
As #mrkxbt mentioned, the issue was related to the blank line after the data in the text file. The string was being split at the \n which was assigning two values into the array. The first value was a string containing the data and the second was an empty string, so obviously the second set of splitting (by ,) was failing. Amended and working code is below:
if let dir : NSString = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomainMask.AllDomainsMask, true).first {
let path = dir.stringByAppendingPathComponent("FSPassengers/aircraft.txt")
do {
let content = try NSString(contentsOfFile: path, encoding: NSUTF8StringEncoding)
if content != "" {
let astrContent:[String] = content.componentsSeparatedByString("\n")
for aeroplane in astrContent {
if aeroplane != "" {
let aSeperated:[String] = aeroplane.componentsSeparatedByString(",")
print(aSeperated[0])
print(Int(aSeperated[0])!)
self.aAircraft.append(Aircraft(id: Int(aSeperated[0])!, type: aSeperated[1], passengerCapacity: Int(aSeperated[2])!, cargoCapacityKg: Double(aSeperated[3])!, cruiseSpeed: Int(aSeperated[4])!, fuelLitresPerHour: Double(aSeperated[5])!))
}
}
}
}
catch {
print("Error")
}
}
Related
import java.util.Scanner;
class Palindrome_string
{
public static void main()
{
System.out.println("\f");
Scanner sc = new Scanner(System.in);
System.out.println("Enter a string");
String a = sc.nextLine();
int b = a.length();
String rev = "";
for (int i = b - 1; i >= 0; i--)
{
char c = a.charAt(i);
rev = rev + c;
}
System.out.println("Original word "+a);
System.out.println("Reversed word "+rev);
a = a.toLowerCase();
rev = rev.toLowerCase();
if (a == rev)
{
System.out.println("It is a palindrome");
}
else
{
System.out.println("It is not a palindrome");
}
sc.close();
}
}
The program compiles properly. Still, when running the program, the message which tells if it is a palindrome prints incorrectly. What changes do I make? Here is a picture of the output. Even though the word 'level' (which is a palindrome) has been inputted, it shows that it isn't a palindrome. What changes should I make? output pic
You should not use == to compare two strings because it compares the reference of the string, i.e. whether they are the same object or not.
Use .equals() instead. It tests for value equality. So in your case:
if (a.equals(rev))
{
System.out.println("It is a palindrome");
}
Also try not to use single-letter variable names except for index variables when iterating over a list etc. It's bad practice.
I don't understand what to do with the issue reported by the compiler. I tried to create a Range, but it says Index is not known:
//let range = matches.first!.range.location
let range = Range(
start:matches.first!.range.location,
end: matches.first!.range.location+matches.first!.range.length
)
id = text[range]
var t = text
t.removeRange(range)
return t
Compiler says: Cannot invoke 'removeRange' with an argument list of type '(Range)' on t.removeRange(range).
I'm pretty sure it's evident, but I lost a great deal of time on such a small issue… any help highly appreciated!
As your error says that:
Cannot invoke 'removeRange' with an argument list of type '(Range)'
Means there is a problem with your range instance type and removeRange function will only accept an argument with type Range<String.Index> and its syntax is :
/// Remove the indicated `subRange` of characters
///
/// Invalidates all indices with respect to `self`.
///
/// Complexity: O(\ `count(self)`\ ).
mutating func removeRange(subRange: Range<String.Index>)
And here is working example with removeRange:
var welcome = "hello there"
let range = advance(welcome.endIndex, -6)..<welcome.endIndex
welcome.removeRange(range)
println(welcome) //hello
Hope this will help.
Swift 2.2 example of removing first 4 characters:
let range = text.startIndex..<text.startIndex.advancedBy(4)
text.removeRange(range)
That first line feels verbose. I hope newer Swift versions improve upon it.
Here is the working equivalent snippet:
static func unitTest() {
let text = "a👿bbbbb🇩🇪c"
let tag = Tag(id: "🇩🇪")
tag.regex = "👿b+"
print ("Unit test tag.foundIn(\(text)) ? = \(tag.foundIn(text))")
}
func foundIn(text: String) -> (id:String, remainingText:String)? {
// if a regex is provided, use it to capture, and keep the capture as a tag ID
if let regex = regex {
let r = Regex(regex) // text =~ regex
let matches = r.matches(text)
if matches.count >= 1 {
let first = matches.first!.range
let start = advance(text.startIndex, first.location)
let end = advance(start, first.length-1)
let range = Range(start: start, end: end)
id = text[range]
var t = text
t.removeRange(range)
return (id, t)
}
return nil
}
else if let range = text.rangeOfString(id) {
var t = text
t.removeRange(range)
return (id, t)
}
else {
return nil
}
}
The unit test returns :
Unit test tag.foundIn(a👿bbbbb🇩🇪c) ? = Optional(("👿bbbbb", "a🇩🇪c"))
I'm trying to have a RTB show any line containing foo be red. Here's what I've attempted so far:
string foo = "foo";
if (richTextBox1.Lines.Contains(foo))
{
richTextBox1.Select(richTextBox1.Lines.Contains(foo);
richTextBox1.SelectionColor = Color.Red;
}
Is there a more effective way of going about this or is anyone able to see where I am going wrong?
The problem you have is with Select() function
Here's how you can do it:
string foo = "foo";
int lineNum = 0;
foreach (string line in richTextBox2.Lines)
{
if (line.Contains(foo))
{
richTextBox2.Select(richTextBox2.GetFirstCharIndexFromLine(lineNum), line.Length);
richTextBox2.SelectionColor = Color.Red;
}
lineNum++;
}
I have a string that is formatted like this: "XbfdASF;FBACasc|Piida;bfedsSA|XbbnSF;vsdfAs|"
Basiclly its an ID;ID| and then it repeats.
I have the first ID and I need to find it's partner Example: I have 'Piida' and I need to find the String that follows it after the ';' which is 'bfedsSA'
How do I do this?
The problem I am having is that the length of the IDs is dynamic so I need to get the index of '|' after the ID I have which is 'Piida' and then get the string that is between these indexes which in this case should be 'bfedsSA'.
There are many ways to do this, but the easiest is to split the string into an array using a separator.
If you know JavaScript, it's the equivalent of the .split() string method; Swift does have this functionality, but as you see there, it can get a little messy. You can extend String like this to make it a bit simpler. For completeness, I'll include it here:
import Foundation
extension String {
public func split(separator: String) -> [String] {
if separator.isEmpty {
return map(self) { String($0) }
}
if var pre = self.rangeOfString(separator) {
var parts = [self.substringToIndex(pre.startIndex)]
while let rng = self.rangeOfString(separator, range: pre.endIndex..<endIndex) {
parts.append(self.substringWithRange(pre.endIndex..<rng.startIndex))
pre = rng
}
parts.append(self.substringWithRange(pre.endIndex..<endIndex))
return parts
} else {
return [self]
}
}
}
Now, you can call .split() on strings like this:
"test".split("e") // ["t", "st"]
So, what you should do first is split up your ID string into segments by your separator, which will be |, because that's how your IDs are separated:
let ids: [String] = "XbfdASF;FBACasc|Piida;bfedsSA|XbbnSF;vsdfAs|".split("|")
Now, you have a String array of your IDs that would look like this:
["XbfdASF;FBACasc", "Piida;bfedsSA", "XbbnSF;vsdfAs"]
Your IDs are in the format ID;VALUE, so you can split them again like this:
let pair: [String] = ids[anyIndex].split(";") // ["ID", "VALUE"]
You can access the ID at index 0 of that array and the value at index 1.
Example:
let id: String = ids[1].split(";")[0]
let code: String = ids[1].split(";")[1]
println("\(id): \(code)") // Piida: bfedsSA
A function in swift takes any numeric type in Swift (Int, Double, Float, UInt, etc).
the function converts the number to a string
the function signature is as follows :
func swiftNumbers <T : NumericType> (number : T) -> String {
//body
}
NumericType is a custom protocol that has been added to numeric types in Swift.
inside the body of the function, the number should be converted to a string:
I use the following
var stringFromNumber = "\(number)"
which is not so elegant, PLUS : if the absolute value of the number is strictly inferior to 0.0001 it gives this:
"\(0.000099)" //"9.9e-05"
or if the number is a big number :
"\(999999999999999999.9999)" //"1e+18"
is there a way to work around this string interpolation limitation? (without using Objective-C)
P.S :
NumberFormater doesn't work either
import Foundation
let number : NSNumber = 9_999_999_999_999_997
let formatter = NumberFormatter()
formatter.minimumFractionDigits = 20
formatter.minimumIntegerDigits = 20
formatter.minimumSignificantDigits = 40
formatter.string(from: number) // "9999999999999996.000000000000000000000000"
let stringFromNumber = String(format: "%20.20f", number) // "0.00000000000000000000"
Swift String Interpolation
1) Adding different types to a string
2) Means the string is created from a mix of constants, variables, literals or expressions.
Example:
let length:Float = 3.14
var breadth = 10
var myString = "Area of a rectangle is length*breadth"
myString = "\(myString) i.e. = \(length)*\(breadth)"
Output:
3.14
10
Area of a rectangle is length*breadth
Area of a rectangle is length*breadth i.e. = 3.14*10
Use the Swift String initializer: String(format: <#String#>, arguments: <#[CVarArgType]#>)
For example:
let stringFromNumber = String(format: "%.2f", number)
String and Characters conforms to StringInterpolationProtocol protocol which provide more power to the strings.
StringInterpolationProtocol - "Represents the contents of a string literal with interpolations while it’s being built up."
String interpolation has been around since the earliest days of Swift, but in Swift 5.0 it’s getting a massive overhaul to make it faster and more powerful.
let name = "Ashwinee Dhakde"
print("Hello, I'm \(name)")
Using the new string interpolation system in Swift 5.0 we can extend String.StringInterpolation to add our own custom interpolations, like this:
extension String.StringInterpolation {
mutating func appendInterpolation(_ value: Date) {
let formatter = DateFormatter()
formatter.dateStyle = .full
let dateString = formatter.string(from: value)
appendLiteral(dateString)
}
}
Usage: print("Today's date is \(Date()).")
We can even provide user-defined names to use String-Interpolation, let's understand with an example.
extension String.StringInterpolation {
mutating func appendInterpolation(JSON JSONData: Data) {
guard
let JSONObject = try? JSONSerialization.jsonObject(with: JSONData, options: []),
let jsonData = try? JSONSerialization.data(withJSONObject: JSONObject, options: .prettyPrinted) else {
appendInterpolation("Invalid JSON data")
return
}
appendInterpolation("\n\(String(decoding: jsonData, as: UTF8.self))")
}
}
print("The JSON is \(JSON: jsonData)")
Whenever we want to provide "JSON" in the string interpolation statement, it will print the .prettyPrinted
Isn't it cool!!