This libary is not a class, but an extension to the Swift String Datatype. It creates string functions with the more familiar syntax.
The extensions add features using this syntax
returnValue = StringValue.function([arg])
in addition there are function versions of these with a syntax of
returnValue = function(StringValue[,arg])
These like all the ones I have already posted, and most of what I will post in the future follow a syntax similar to Xojo API 1.0
//
// libSTRINGS.swift
// rdsBASIC
//
// Created by Dave Sisemore on 04-Aug-2016
// Updated for Swift 4.1 on 22-Jul-2018
// Updated for Swift 5.1 on 17-Feb-2020
// Copyright © 2018 Dave Sisemore. All rights reserved.
//
import UIKit
import Foundation
extension String {
/// returns a string containing ONLY numeric digits from the source string
/// - example
/// var s="ABC123def456xyz789"
/// var t=s.onlyDigits()
/// t="123456789"
public func onlyDigits() -> String {
let filtredUnicodeScalars = unicodeScalars.filter{CharacterSet.decimalDigits.contains($0)}
return String(String.UnicodeScalarView(filtredUnicodeScalars))
}
/// Returns the integer code point for the first character in the string using the character's encoding.
/// result = stringVariable.Asc()
public func Asc() -> Int { return AscAt(self,Index:1) }
/// Returns the number of characters in the specified string.
/// result=stringVariable.Len()
public func Len() -> Int { return self.count }
/// Returns a portion of a string. The first character is numbered 1.
/// result=stringVariable.Mid(start [,length])
public func Mid( _ start:Int,_ length:Int=(-1)) -> String {
let strSize : Int = self.count
var mStart : Int = start
var mLen : Int = length
if mStart <= 0 { return "" }
if (mLen < 1) || (mStart+mLen>strSize) { mLen = strSize-mStart+1 }
mStart=mStart-1 // Swift strings are 0 based
return (self as NSString).substring( with: NSRange(location: mStart, length: mLen ) )
}
/// Returns the first n characters in a source string.
/// result=stringVariable.Left(count)
public func Left(_ count:Int) -> String {
if count<=0 { return "" }
return self.Mid(1,count)
}
/// Returns the last n characters from the string specified.
/// result=stringVariable.Right(count)
public func Right(_ count:Int) -> String {
let strSize = self.count
var indexFirst=strSize-count+1
if indexFirst<1 { indexFirst=1 }
return Mid(indexFirst,count)
}
/// Returns the position of the first occurrence of a string inside another string. The first character is numbered 1.
/// result = stringVariable.InStr(find,[checkcase])
/// return is not case sensitive unless checkcase=true
public func InStr(_ lookfor:String,_ checkcase:Bool=false) -> Int {
var self_test:String=self
var othr_test:String=lookfor
if !checkcase {
self_test=self_test.uppercased()
othr_test=othr_test.uppercased()
}
let myRange: NSRange = (self_test as NSString).range(of: othr_test)
var loc:Int=myRange.location
if (loc<0) || (loc>self_test.count) {
loc=0
} else {
loc+=1
}
return loc
}
/// same as above, but a starting index can be specified
/// result = stringVariable.InStr([start,] find)
/// return is not case sensitive unless checkcase=true
public func InStr(_ start:Int,_ lookfor:String,_ checkcase:Bool=false) -> Int {
var loc : Int=0
var strt : Int=start
if (strt<=0) { strt=1 }
if strt<self.Len() {
let temp:String=self.Mid(strt)
loc=temp.InStr(lookfor,checkcase)
if loc>0 { loc=loc+strt-1 }
}
return loc
}
/// compares to another string [-1 self<other 0=self=other +1=self>other]
/// compare is not case sensitive unless checkcase=true
public func Compare(_ other:String,_ checkcase:Bool=false) -> Int {
// -1 less than 0=equal 1=greater
var flag : Int=0
var self_test:String=self
var othr_test:String=other
if !checkcase {
self_test=self_test.uppercased()
othr_test=othr_test.uppercased()
}
if self_test==othr_test { flag=0 }
if self_test<othr_test { flag=(-1) }
if self_test>othr_test { flag=1 }
return flag
}
/// returns true if string begins with specified value
/// compare is not case sensitive unless checkcase=true
public func BeginsWith(_ other:String,_ checkcase:Bool=false) -> Bool {
var self_test:String=self
var othr_test:String=other
if !checkcase {
self_test=self_test.uppercased()
othr_test=othr_test.uppercased()
}
return self_test.hasPrefix(othr_test)
}
/// returns true if string ends with specified value
/// compare is not case sensitive unless checkcase=true
public func EndsWith(_ other:String,_ checkcase:Bool=false) -> Bool {
var self_test:String=self
var othr_test:String=other
if !checkcase {
self_test=self_test.uppercased()
othr_test=othr_test.uppercased()
}
return self_test.hasSuffix(othr_test)
}
/// Returns the string passed with leading and trailing whitespaces removed.
public func Trim() -> String {
return self.trimmingCharacters(in: .whitespacesAndNewlines)
}
/// Returns the string passed with leading (left side) whitespaces removed.
public func LTrim() -> String {
var x:Int
let cs:CharacterSet = .whitespacesAndNewlines
x=self.Len()
for i in(0...x-1) {
if !cs.contains(UnicodeScalar(characterAtIndex(i))!) {
return (self as NSString).substring(from: i)
}
}
return self
}
/// Returns the string data type passed with trailing (right side) whitespaces removed.
public func RTrim() -> String {
var x:Int
let cs:CharacterSet = .whitespacesAndNewlines
x=self.Len()
for i in(0...x-1).reversed() {
if !cs.contains(UnicodeScalar(characterAtIndex(i))!) {
return (self as NSString).substring(to: i+1)
}
}
return self
}
/// Returns a field from a row of data. The first field is numbered 1.
/// result=stringVariable.NthField(fieldNumber,[separator])
/// NOTE : arguments are in REVERSE order from Xojo
public func NthField(_ fieldNumber:Int,_ sep_by:String=",") -> String {
let arr:[String]=self.components(separatedBy: sep_by)
let x=fieldNumber-1
if x<0 || fieldNumber>arr.count { return "" }
return arr[x]
}
/// Returns the number of values (fields) in the string passed that are separated by the separator string passed.
/// result = stringVariable.CountFields([separator])
public func CountFields(_ sep_by:String=",") -> Int {
let arr:[String]=self.components(separatedBy: sep_by)
return arr.count
}
/// Creates a one-dimensional array from the String passed.
/// array=source.Split([delimiter])
public func Split(_ sep_by:String=",") -> [String] {
let arr:[String]=self.components(separatedBy: sep_by)
return arr
}
public func Lowercase() -> String { return self.lowercased() }
public func Uppercase() -> String { return self.uppercased() }
public func Titlecase() -> String { return self.capitalized }
/// Returns the Double equivalent of the passed string.
/// result=stringVariable.CDbl()
/// supports Hex, Oct and Binary values (&H, &O, &B)
public func CDbl() -> Double {
var retDbl:Double=0
var retStr:String = ""
var mTest :String = ""
if self.Len()>2 {
retStr = self.Mid(3)
mTest = self.Left(2)
}
switch mTest {
case "&H" : retDbl=Double(Hex2Int(retStr))
case "&O" : retDbl=Double(Oct2Int(retStr))
case "&B" : retDbl=Double(Bin2Int(retStr))
default : retDbl=(self as NSString).doubleValue
}
return retDbl
}
/// Returns the numeric form of a string as an Int64.
/// result=stringVariable.CLong
public func CLong() -> Int64 { return Int64(CDbl()) }
/// Alias for CDBL()
/// result=stringVariable.Val()
public func Val() -> Double { return CDbl() }
/// Returns the Integer equivalent of the passed string.
/// result=stringVariable.intVal()
/// supports Hex, Oct and Binary values (&H, &O, &B)
/// this is required due to Swift strict typecasting
public func intVal() -> Int { return Int(Val()) }
/// Returns true if string is empty ("")
/// result=stringVariable.Empty()
public func Empty() -> Bool { return self.isEmpty }
/// Replaces the first occurrence of a string with another string.
/// result=stringVariable.Replace(oldString, newString)
/// compare is not case sensitive unless checkcase=true
public func Replace(_ oldStr:String,_ newStr:String,_ checkcase:Bool=false) -> String {
var self_test:String=self
var othr_test:String=oldStr
if !checkcase {
self_test=self_test.uppercased()
othr_test=othr_test.uppercased()
}
var temp:String=""
let x:Int=self_test.InStr(othr_test)
let y:Int=x+oldStr.Len()
if x>0 {
if x>1 { temp=self.Left(x-1) }
temp=temp+newStr+self.Mid(y)
} else {
temp=self
}
return temp
}
/// Replaces all occurrences of a string with another string.
/// at this time seems checkcase is only a placeholder
public func ReplaceAll(_ oldStr:String,_ newStr:String,_ checkcase:Bool=false) -> String {
return self.replacingOccurrences(of: oldStr, with: newStr)//, options: nil, range: nil)
}
/// Indicates whether the value passed is numeric.
/// result=stringVariable.isNumeric()
public func isNumeric() -> Bool {
let length = self.count
let scanner = Scanner(string: self)
scanner.charactersToBeSkipped = nil
var value: Double = 0.0
if scanner.scanDouble(&value) && (scanner.scanLocation == length) {
return true
}
return false
}
/****** P R I V A T E S U P P O R T F U N C T I O N S ******/
private func AscAt(_ s:String,Index:Int) -> Int {
var x:UInt32=0
var i:Int=0
for c in s.unicodeScalars {
x=UnicodeScalar(c).value
i+=1
if (i==Index) { break }
}
return Int(x)
}
private func Base2Int(_ s:String,_ base:Int) -> Int {
var retInt:Int=0
// var i:Int
var x:Int
var valid:String=""
var c:String=""
let mStr:String = s.uppercased()
let strSize :Int = mStr.count
switch base {
case 2 : valid="01"
case 8 : valid="01234567"
case 16 : valid="0123456789ABCDEF"
default : return 0
}
for i in 1 ... strSize {
c=mStr.Mid(i,1)
x=valid.InStr(c)
if (x==0) {
retInt=0
break
}
retInt=(retInt * base)+(x-1)
}
return retInt
}
private func Hex2Int(_ s:String) -> Int { return Base2Int(s,16) }
private func Oct2Int(_ s:String) -> Int { return Base2Int(s,8) }
private func Bin2Int(_ s:String) -> Int { return Base2Int(s,2) }
private func characterAtIndex(_ index: Int) -> unichar { return (self as NSString).character(at: index)
}
} /* END - String extension */
// --------- End of Framework ---------
/*** FUNCTION Version of all String Properties ***/
/*** these mirror the extension, but are result=function(sourceString,[parameters]) ***/
public func Asc( _ s:String) -> Int { return s.Asc() }
public func BeginsWith( _ s:String, other:String,_ checkcase:Bool=false) -> Bool { return s.BeginsWith(other,checkcase) }
public func Compare( _ s:String, other:String, checkcase:Bool=false) -> Int { return s.Compare(other,checkcase) }
public func Empty( _ s:String) -> Bool { return s.Empty() }
public func EndsWith( _ s:String, other:String,_ checkcase:Bool=false) -> Bool { return s.EndsWith(other,checkcase) }
public func CDbl( _ s:String) -> Double { return s.CDbl() }
public func CLong( _ s:String) -> Int64 { return s.CLong() }
public func Mid( _ s:String,_ start:Int,_ length:Int=(-1)) -> String { return s.Mid(start,length) }
public func Left( _ s:String,_ indx:Int) -> String { return s.Left(indx) }
public func Right( _ s:String,_ indx:Int) -> String { return s.Right(indx) }
public func InStr( _ s:String,_ find:String,_ checkcase:Bool=false) -> Int { return s.InStr(find,checkcase) }
public func InStr(_ start:Int,s:String,_ find:String,_ checkcase:Bool=false) -> Int { return s.InStr(start,find,checkcase) }
public func isNumeric( _ s:String) -> Bool { return s.isNumeric() }
public func Len( _ s:String) -> Int { return s.Len() }
public func Trim( _ s:String) -> String { return s.Trim() }
public func LTrim( _ s:String) -> String { return s.LTrim() }
public func RTrim( _ s:String) -> String { return s.RTrim() }
public func NthField( _ s:String,_ fieldNumber:Int,_ sep_by:String=",") -> String { return s.NthField(fieldNumber,sep_by) }
public func CountFields( _ s:String,_ sep_by:String=",") -> Int { return s.CountFields(sep_by) }
public func Uppercase( _ s:String) -> String { return s.Uppercase() }
public func Lowercase( _ s:String) -> String { return s.Lowercase() }
public func Titlecase( _ s:String) -> String { return s.Titlecase() }
public func Val( _ s:String) -> Double { return s.Val() }
public func intVal( _ s:String) -> Int { return s.intVal() }
public func Split( _ s:String,_ sep_by:String=",") -> [String] { return s.Split(sep_by) }
public func Replace( _ s:String,_ oldStr:String,_ newStr:String,_ checkcase:Bool=false) -> String { return s.Replace(oldStr,newStr,checkcase) }
public func ReplaceAll( _ s:String,_ oldStr:String,_ newStr:String,_ checkcase:Bool=false) -> String { return s.ReplaceAll(oldStr,newStr,checkcase) }
/* ** Other String Related Functions ** */
public func Chr(_ v:Int) -> String { return String(describing: UnicodeScalar(v)) }
public func Chr(_ v:CGFloat) -> String { return Chr(Int(v)) }
public func Join(_ s:[String],_ sep_by:String=",") -> String { return s.joined(separator: sep_by) }
public func Str(_ x:Any,dp:Int=12) -> String {
var retStr:String=""
switch x {
case let mBool as Bool:
if mBool {
retStr = "true"
} else {
retStr = "false"
}
case let mInt as Int : retStr = NSString(format:"%d",mInt) as String
case let mDbl as Double : retStr = NSString(format:"%.\(dp)f" as NSString,mDbl) as String
case let mStr as String : retStr = mStr
default : retStr=""
}
return retStr
}
public func numericLen(_ s:String) -> Int {
let scanner = Scanner(string: s)
scanner.charactersToBeSkipped = nil
var value: Double = 0.0
if scanner.scanDouble(&value) && (scanner.scanLocation > 0) {
return scanner.scanLocation
}
return 0
}