Regular Expression issue for Hexadecimal and Binary strings

I have a button that is supposed auto-detect encoded strings. I have a UISpinner module that allows the user to select the decoding method they want to use. Currently there are three decoding methods: "-- Select decoding --" -> Hexadecimal, Binary, and Base64.

Say the string is in hexadecimal, when the button (for auto-detection) is pressed it calls the hexadecimal string check boolean method and the spinner is supposed to go to the index of 1 (hexadecimal) and when the string is in Binary the binary string check is called and the spinner is supposed to be at index of 2 (binary).

So far, I have two boolean methods that are supposed to check the string using regular expression and return true if the string matches. The problem is, when I give the sub-program a binary string and press the button, it goes to hexadecimal and not binary. Here are the methods I have:

Code Block
private func isStringHex(source: String) -> Bool {
        let reg = try! NSRegularExpression(pattern: "(0x)?([0-9a-f]{16})")
        let ran = NSRange(location: 0, length: source.count)
        if (reg.firstMatch(in: source, options: [], range: ran) != nil) {
            return true
        }
        return false
    }
    
    private func isStringBinary(source: String) -> Bool {
        let reg = try! NSRegularExpression(pattern: "([01]{2})")
        let ran = NSRange(location: 0, length: source.count)
        if (reg.firstMatch(in: source, options: [], range: ran) != nil) {
            return true
        }
        return false
    }


Answered by OOPer in 640452022

I have already decided on removing that button until I get things working. I think I will convert it to 16 byte Data value later on...

Possibly a good decision.

So, are there anything else we can help?

The problem is, when I give the sub-program a binary string and press the button, it goes to hexadecimal and not binary. 

Can use show an example of the binary string, which should be classified as binary?


Currently the method isStringBinary(source:) returns true when the source contains two character sequence 0 or 1.
In other words, isStringBinary(source:) searches any one of 00, 01, 10 or 11 in the source. Is that what you really want to check?


One more, can you show the whole code of the action method for the button?
I tested the code :

Code Block
let digits = "0xffffffffffffffff"
let digits2 = "010111"
let hexa = isStringHex(source: digits)
let bin = isStringBinary(source: digits)
let hexa2 = isStringHex(source: digits2)
let bin2 = isStringBinary(source: digits2)

This gives the correct results (true, false, false, true).

Binary is recognized as binary.
So maybe the error is in the button code.

But several problems:
  • hexa must be exactly 16 digits. Is it what you want ?

  • must be lower case and not uppercase as FFFF: should change pattern to

Code Block
    let reg = try! NSRegularExpression(pattern: "(0x)?([0-9a-fA-F]{16})")
  • Spaces are not allowed ; but it is common to write hexa as FFFF FFFF FFFF FFFF ; could change to

Code Block
    let reg = try! NSRegularExpression(pattern: "(0x)?([0-9a-fA-F ]{16})")
  • 01A is recognized as binary. This is because you use first match.

  • Or do you want to have at least 2 digits in binary ?

You have simpler solution than regex here:
Code Block
func isStringBinary(source: String) -> Bool {
let chars : Set<Character> = Set(source)
let isValidBin = chars.isSubset(of: ["0", "1"]) && chars.count >= 1
return isValidBin
}

Here is the whole code for the button action method
Code Block
@IBAction func btnAutoCheckString(_ sender: Any) {
        let source = sourceString.text!
        if isStringBinary(source: source) {
            decodings.selectRow(2, inComponent: 0, animated: true)
            decodeBinaryString(source: source)
        }
        if isStringHex(source: source) {
            decodings.selectRow(1, inComponent: 0, animated: true)
            decodeHexString(source: source)
        }
    }
// Applying Claude31's methods
private func isStringHex(source: String) -> Bool {
        let reg = try! NSRegularExpression(pattern: "(0x)?([0-9a-fA-F]{16})")
        let ran = NSRange(location: 0, length: source.count)
        if (reg.firstMatch(in: source, options: [], range: ran) != nil) {
            return true
        }
        return false
    }
    private func isStringBinary(source: String) -> Bool {
        /*
        let reg = try! NSRegularExpression(pattern: "([01]{2})")
        let ran = NSRange(location: 0, length: source.count)
        if (reg.firstMatch(in: source, options: [], range: ran) != nil) {
            return true
        }
        return false
        */
        let chars : Set<Character> = Set(source)
        let isValidBin = chars.isSubset(of: ["0", "1"]) && chars.count >= 1
        return isValidBin
    }


Here is the whole code for the button action method

Thanks for showing the code.

With seeing your code, if the source matches both for isStringBinary(source:) and isStringHex(source:), then both decodeBinaryString(source:) and decodeHexString(source:) are called.
Is that what you intend?


Some other points:
The updated isStringHex(source:) returns true for XYX0x0123456789ABCDEFGHIJK, is this what you want?
No, I need it to detect one or the other...Like if I enter a binary string of "01110100011001010111001101110100" which is "test" in string format, I need for the isStringBinary() boolean method run and if the string is hexadecimal, say, "74657374" which is "test" in hexadecimal, then I need the isStringHex() boolean method to run...I don't know why it is stopping at "Hexadecimal" when I insert a binary string though...I was thinking about adding a return after each detect...

I don't know why it is stopping at "Hexadecimal" when I insert a binary string

Sorry, but you have not shown any code including "Hexadecimal", so stopping at "Hexadecimal" does not make sense for readers.

But some points get clearer.

You want "74657374" to be judged as Hexadecimal. But as Claude31 has already mentioned, your isStringHex(source:) matches only when 16 hexadecimal digits are found.
Your updated  isStringHex(source:) returns false for "74657374".
I guess your isStringHex(source:) should be something like this:
Code Block
private func isStringHex(source: String) -> Bool {
let reg = try! NSRegularExpression(pattern: "^(?:0x)?[0-9a-f]+", options: .caseInsensitive)
return reg.firstMatch(in: source, range: NSRange(0..<source.utf16.count)) != nil
}


Your isStringBinary(source:) can be written using Regex as well:
Code Block
private func isStringBinary(source: String) -> Bool {
let reg = try! NSRegularExpression(pattern: "^[01]+$")
return reg.firstMatch(in: source, range: NSRange(0..<source.utf16.count)) != nil
}



And you need to clarify the last one thing.
The source  "01110100011001010111001101110100" can be interpreted as a binary string of 32-bits, it can also be considered to be a hexadecimal string representing 16-byte data.

I was thinking about adding a return after each detect...

If you put high priority to take it as binary in such a case, adding a return after each detect would be a possible solution.
And don't you want to accept spaces between each "word" (as FFFF) in Hexa ?
Here is the code to decode hexadecimal and binary:
Code Block
private func decodeHexString(source: String) -> Void {
        let data = Data(fromHexEncodedString: source)
        let altData = Data(fromHexEncodedString: "74647374")
        let string = String(data: ((data) ?? altData)!, encoding: .utf8)
        // Print the result
        resultOutput.text = string
    }
    private func decodeBinaryString(source: String) -> Void {
        // Split the binary string into 8'ths
        let newString = source.separate(every: 8, with: "\n")
        let byteArray = newString.components(separatedBy: .whitespacesAndNewlines).compactMap {UInt8($0, radix: 2)}
        if let string = String(bytes: byteArray, encoding: .utf8) {
            resultOutput.text = string
        } else {
            displayErrorMessage(title: "Encoding", message: "Bad Binary Encoding..."); return
        }
    }

Also, there is this extension to Data:
Code Block
extension Data {
    // From http://stackoverflow.com/a/40278391:
    init?(fromHexEncodedString string: String) {
        // Convert 0 ... 9, a ... f, A ...F to their decimal value,
        // return nil for all other input characters
        func decodeNibble(u: UInt16) -> UInt8? {
            switch(u) {
            case 0x30 ... 0x39:
                return UInt8(u - 0x30)
            case 0x41 ... 0x46:
                return UInt8(u - 0x41 + 10)
            case 0x61 ... 0x66:
                return UInt8(u - 0x61 + 10)
            default:
                return nil
            }
        }
        self.init(capacity: string.utf16.count/2)
        var even = true
        var byte: UInt8 = 0
        for c in string.utf16 {
            guard let val = decodeNibble(u: c) else { return nil }
            if even {
                byte = val << 4
            } else {
                byte += val
                self.append(byte)
            }
            even = !even
        }
        guard even else { return nil }
    }
}

Here is the code to decode hexadecimal and binary:

Seems you want to convert the binary/hexadecimal String to Data(or byte array) and decode it as UTF-8 String.

But the problem matches both still exists.
You need to decide how you treat "01110100011001010111001101110100".
It can be interpreted as a binary string containing 4-bytes, or you can convert it to 16-byte Data as a hexadecimal string.

Which do you want? Please decide.
I have already decided on removing that button until I get things working. I think I will convert it to 16 byte Data value later on...
Accepted Answer

I have already decided on removing that button until I get things working. I think I will convert it to 16 byte Data value later on...

Possibly a good decision.

So, are there anything else we can help?
Regular Expression issue for Hexadecimal and Binary strings
 
 
Q