1

I am attempting to rewrite this Objective C method that has similar functionality to Python's lstrip (which removes characters from the LH of a string) and rstrip (strips the RH of a string of characters).

The lstrip version works great:

// this works
func lstrip(st_in: String,
            trimSet: NSCharacterSet=NSCharacterSet.whitespaceAndNewlineCharacterSet())-> String {
    let wanted=trimSet.invertedSet
    if let r=st_in.rangeOfCharacterFromSet(wanted) {
        return st_in.substringFromIndex(r.startIndex)
    }
    return ""
}

I am having trouble with the rstrip version however.

This is what I have:

// does not compile
func rstrip(st_in: String,
    trimSet: NSCharacterSet=NSCharacterSet.whitespaceAndNewlineCharacterSet())-> String {
        let wanted=trimSet.invertedSet
        let tgt=String(st_in.characters.reverse())
        if let r=tgt.rangeOfCharacterFromSet(wanted) {
            // st_in.characters.count - r.startIndex
            return st_in.substringToIndex(st_in.startIndex.advancedBy((st_in.characters.count as Int) - (r.startIndex as Int)))
        }
        return ""
}

Issues:

  1. The Swift version of .rangeOfCharacterFromSet does not seem to support options:NSBackwardsSearch -- I use forward search on st_in.characters.reverse() which seems to work;
  2. The major issue (and my question is) st_in.substringToIndex(st_in.startIndex.advancedBy((st_in.characters.count as Int) - (r.startIndex as Int))) does not compile. I can see in the debugger that the values are correct, but r.startIndex is type Index and I cannot use that value directly for arithmetic nor can I coerce that to an Int value nor can I access the value I see in it.

So given this in the debugger:

56> st 
$R33: String = "  \t\n  I needz trimin'¡    \t\n\n\n\n"
 57> String(st.characters.reverse()) 
$R34: String = "\n\n\n\n\t    ¡'nimirt zdeen I  \n\t  "
 58> String(st.characters.reverse()).rangeOfCharacterFromSet(trimSet.invertedSet)!.startIndex 
$R35: Index = {
  _base = {
    _position = 9
    _core = {
      _baseAddress = 0x0000000101207ee0
      _countAndFlags = 9223372036854775839
      _owner = Some {
        instance_type = 0x0000000101207ec0
      }
    }
  }
  _lengthUTF16 = 1
}

You can see that _position=9 in the Index structure -- just what I need in st_in.substringToIndex(st_in.startIndex.advancedBy(st_in.characters.count - r.startIndex)) but I do not know how to access it in Swift.

Ideas?

Community
  • 1
  • 1
dawg
  • 98,345
  • 23
  • 131
  • 206

2 Answers2

1

rangeOfCharacterFromSet() does support search from the end of the source string, but the Objective-C NSBackwardsSearch is imported to Swift as .BackwardsSearch:

func rstrip(st_in: String,
    trimSet: NSCharacterSet = NSCharacterSet.whitespaceAndNewlineCharacterSet())-> String {
        let wanted = trimSet.invertedSet
        if let r = st_in.rangeOfCharacterFromSet(wanted, options: .BackwardsSearch) {
            return st_in.substringToIndex(r.endIndex)
        }
        return ""
}

Example:

let s1 = " foo bar      "
let s2 = rstrip(s1)

print(">\(s1)<")
print(">\(s2)<")

// Output:
// > foo bar      <
// > foo bar<

So you don't need to operate on the reversed string. But to answer your question anyway: This is how it would work:

func rstrip(st_in: String,
    trimSet: NSCharacterSet=NSCharacterSet.whitespaceAndNewlineCharacterSet())-> String {
        let wanted = trimSet.invertedSet
        let tgt = String(st_in.characters.reverse())
        if let r = tgt.rangeOfCharacterFromSet(wanted) {
            let length = tgt.startIndex.distanceTo(r.startIndex)
            return st_in.substringToIndex(st_in.endIndex.advancedBy(-length))
        }
        return ""
}

Instead of casting a string index to Int (which does not compile), distanceTo() is used to compute the length as a Distance (which happens to be Int for String.CharacterView), and that can be used in arithmetic, e.g. -length.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
1

i use a little bit different approach. (i try to use 'pure Swift' and avoid to import Foundation as much, as possible. maybe i change my mind with Swift3.0 )

extension String {
    var rtrim: String {
        var s = self.characters
        while s.last == " " { s = s.dropLast() }
        return String(s)
    }
    var ltrim: String {
        var s = self.characters
        while s.first == " " { s = s.dropFirst() }
        return String(s)
    }
    var trim:String {
        return self.ltrim.rtrim
    }
}

let str = "\nalfa\nbeta\nga ma\n   delta   \n\na"
let c = str.characters
let trimmedLines = c.split("\n", allowEmptySlices: false).map{ String($0).trim }
print(str.debugDescription, trimmedLines)

// "\nalfa\nbeta\nga ma\n   delta   \n\na" ["alfa", "beta", "ga ma", "delta", "a"]
user3441734
  • 16,722
  • 2
  • 40
  • 59