0

Here is the signature declared in the framework header:

const char* XLAPIENTRY xlSheetReadStrA(SheetHandle handle, int row, int col, FormatHandle* format);

here is the documentation for ReadStr: http://www.libxl.com/spreadsheet.html

here is how we currently read a cell:

func readNillableString(row:Int32, col:Int32) -> String? {
    let value:String? = String(format: "%s", xlSheetReadStrA(sheet, row, col, nil) ?? "").trimmingCharacters(in: .whitespacesAndNewlines)
    let set = NSMutableCharacterSet.alphanumeric()
    set.addCharacters(in: "., +%%()/[]:-_ ®÷")
    if self.rangeOfCharacter(from: set.inverted) != nil || self.isEmpty {
        return nil
    }
    if value?.isEmpty == true {
        return nil
    }
    return value
}

But in reality, all we're trying to do is convert a value in an excel cell into a Swift String

The main issue is empty cells, when we call xlSheetReadStrA on an empty cell, and take that char* return and cast it to a String, this is the result:

(lldb) po value
▿ Optional<String>
  - some : "hÿh\u{03}\u{01}"

It seems like we're doing a lot of unnecessary error handling, when we basically just want a way to return a swift String if there is a value, and nil if there isn't

That rangeOfCharacter call is just a way to determine if the cell is empty...


Does anybody have any LibXL experience, or can look at this parsing code and figure a better way to do this? I'm thinking potentially there is something more efficient with the original casting of the char* xlSheetReadStrA return value, that would remove the need for the following checks

A O
  • 5,516
  • 3
  • 33
  • 68

1 Answers1

1

I have never used LibXL, but your code cannot handle nil cases properly.

Try something like this:

func readNillableString(row:Int32, col:Int32) -> String? {
    guard let cStr = xlSheetReadStrA(sheet, row, col, nil) else {
        return nil
    }
    let value = String(cString: cStr).trimmingCharacters(in: .whitespacesAndNewlines)
    if value.isEmpty {
        return nil
    }
    return value
}

Just try and see what happens.

OOPer
  • 47,149
  • 6
  • 107
  • 142
  • @AO, seems the note below is not needed. – OOPer Aug 13 '18 at 23:28
  • Awesome, that works perfectly, great improvement. Looks like the missing piece was the `cString` initializer, which knew how to cast that `"hÿh\u{03}\u{01}"` junk into an empty `String`? – A O Aug 13 '18 at 23:33
  • The main issue is that `xlSheetReadStrA()` returns `nil` for empty cells or non-string cells. When it returns `nil`, `?? ""` generates something unpredictable. (As the return type is a pointer, not a String.) You can avoid such cases by using `guard let`. – OOPer Aug 13 '18 at 23:39