carlson
carlson

Reputation: 105

how to split a string with Umlaut like "ä" in swift

I'm programming in swift 5 a search routine and I want to highlight in a string if the search is contained in this string (e.g. if I search for "bcd" in a string like "äbcdef" the result should look like "äbcdef". In doing so I wrote an extension for String to split a String into the substring before the match with the search string (="before") , the match with the search string (="match") and the substring afterwards (="after").

extension String {


func findSubstring(forSearchStr search: String, caseSensitive sensitive: Bool) -> (before: String, match: String, after: String) {
    
        var before = self
        var searchStr = search
        if !sensitive {
            before = before.lowercased()
            searchStr = searchStr.lowercased()
        }
        var match = ""
        var after = ""
        let totalStringlength = before.count
        let searchStringlength = searchStr.count
        var startpos = self.startIndex
        var endpos = self.endIndex
        for id in 0 ... (totalStringlength - searchStringlength) {
            startpos = self.index(self.startIndex, offsetBy: id)
            endpos = self.index(startpos, offsetBy: searchStringlength)
            if searchStr == String(before[startpos ..< endpos]) {
                before = String(self[self.startIndex ..< startpos])
                match = String(self[startpos ..< endpos])
                if id < totalStringlength - searchStringlength - 1 {
                    startpos = self.index(startpos, offsetBy: searchStringlength)
                    after = String(self[startpos ..< self.endIndex])
                }
                break
            }
        }
        return (before, match, after)
    } // end findSubstring()

}

My problem is, that this routine works well for all strings without special characters like the German Umlaute "ä, ö, ü" or "ß". If a string contains one of these characters the returned substrings "match" and "after" are shifted one sign to the right. In the example above the result for the search "bcd" is in this case "äbcdef"

My question is, what do I have to do to handle this characters properly as well?

By the way: is there a simplier solution than mine to split a string as described than what I have programmed (which seems to me to be rather complex :) )

Thanks for your valuable support

Upvotes: 0

Views: 198

Answers (1)

Sweeper
Sweeper

Reputation: 274835

String comparison is a complicated issue, and is something you would not want to handle yourself unless you are studying this.

Just use String.range(of:options:):

extension String {
    func findSubstring(forSearchStr search: String, caseSensitive sensitive: Bool) -> (before: String, match: String, after: String)? {
        if let substringRange = range(of: search, options: sensitive ? [] : [.caseInsensitive], locale: nil) {
            return (String(self[startIndex..<substringRange.lowerBound]),
                    String(self[substringRange]),
                    String(self[substringRange.upperBound..<self.endIndex]))
        } else {
            return nil
        }
    }
}

// (before: "ä", match: "bcd", after: "e")
print("äbcde".findSubstring(forSearchStr: "bcd", caseSensitive: true)!)

Note that this is not a literal comparison. For example:

// prints (before: "", match: "ß", after: "")
print("ß".findSubstring(forSearchStr: "ss", caseSensitive: false)!)

If you want a literal comparison, use the literal option:

range(of: search, options: sensitive ? [.literal] : [.caseInsensitive, .literal])

Upvotes: 3

Related Questions