I'm trying to convert an hex string to text.
This is what i have:
// Str to Hex
func strToHex(text: String) -> String {
let hexString = text.data(using: .utf8)!.map{ String(format:"%02x", $0) }.joined()
return "0x" + hexString
}
and I'm trying to reverse the hex string that I've just created back to the original one.
So, for example:
let foo: String = strToHex(text: "K8") //output: "0x4b38"
and i would like to do something like
let bar: String = hexToStr(hex: "0x4b38") //output: "K8"
can someone help me?
Thank you
You probably can use something like this:
func hexToStr(text: String) -> String {
let regex = try! NSRegularExpression(pattern: "(0x)?([0-9A-Fa-f]{2})", options: .caseInsensitive)
let textNS = text as NSString
let matchesArray = regex.matches(in: textNS as String, options: [], range: NSMakeRange(0, textNS.length))
let characters = matchesArray.map {
Character(UnicodeScalar(UInt32(textNS.substring(with: $0.rangeAt(2)), radix: 16)!)!)
}
return String(characters)
}
NSRegularExpression is overkill for the job. You can convert the string to byte array by grabbing two characters at a time:
func hexToString(hex: String) -> String? {
guard hex.characters.count % 2 == 0 else {
return nil
}
var bytes = [CChar]()
var startIndex = hex.index(hex.startIndex, offsetBy: 2)
while startIndex < hex.endIndex {
let endIndex = hex.index(startIndex, offsetBy: 2)
let substr = hex[startIndex..<endIndex]
if let byte = Int8(substr, radix: 16) {
bytes.append(byte)
} else {
return nil
}
startIndex = endIndex
}
bytes.append(0)
return String(cString: bytes)
}
Solution that supports also special chars like emojis etc.
static func hexToPlain(input: String) -> String {
let pairs = toPairsOfChars(pairs: [], string: input)
let bytes = pairs.map { UInt8($0, radix: 16)! }
let data = Data(bytes)
return String(bytes: data, encoding: .utf8)!
}
Related
extension String {
func splitWithRegex(by regexStr: String) -> [String] {
guard let regex = try? NSRegularExpression(pattern: regexStr) else { return [] }
let nsRange = NSRange(startIndex..., in: self)
var index = startIndex
var array = regex.matches(in: self, range: nsRange)
.map { match -> String in
let range = Range(match.range, in: self)!
let result = self[index..<range.lowerBound]
index = range.upperBound
return String(result)
}
array.append(String(self[index...]))
return array
}
}
I use the above code to split a string on Swift using a regrex pattern and return an array of strings
How can I return an object of [(String, Range)] so I will use the split substrings as keys and it's range as the value.
For example:
let string = "This is a string"
let stringRange = string.splitWithRegex(by: "\\s+")
Result:
(This, 0...4)
(is, 5...7)
(a, 8...9)
(string, 10...16)
I guess that should do the trick, but I'm not totally sure:
func splitWithRegex2(by regexStr: String) -> [(String, Range<String.Index>)] {
guard let regex = try? NSRegularExpression(pattern: regexStr) else { return [] }
let nsRange = NSRange(startIndex..., in: self)
var index = startIndex
var array = regex.matches(in: self, range: nsRange)
.compactMap { match -> (String, Range<String.Index>)? in
guard let range = Range(match.range, in: self) else { return nil }
let resultRange = index..<range.lowerBound
let string = String(self[resultRange])
index = range.upperBound
guard !resultRange.isEmpty else { return nil } //This should fix the issue where the first match starts your string, in your case, an empty space
let values = (string, resultRange)
return values
}
//Append last value check: needed?
if index < endIndex {
array.append((String(self[index...]), index..<endIndex))
}
return array
}
To play:
let stringReg1 = "This is a string"
let stringRange1 = stringReg1.splitWithRegex2(by: "\\s+")
print(stringRange1)
stringRange1.forEach { //More userfriendly than Range(Swift.String.Index(_rawBits: 65536)..<Swift.String.Index(_rawBits: 327680)))
let nsRange = NSRange($0.1, in: stringReg1)
print("\($0.0), \(nsRange.location)...\(nsRange.location + nsRange.length)")
}
Output:
$>[("This", Range(Swift.String.Index(_rawBits: 1)..<Swift.String.Index(_rawBits: 262144))), ("is", Range(Swift.String.Index(_rawBits: 393216)..<Swift.String.Index(_rawBits: 524288))), ("a", Range(Swift.String.Index(_rawBits: 589824)..<Swift.String.Index(_rawBits: 655360))), ("string", Range(Swift.String.Index(_rawBits: 720896)..<Swift.String.Index(_rawBits: 1114113)))]
$>This, 0...4
$>is, 6...8
$>a, 9...10
$>string, 11...17
let stringReg2 = " This is a string "
let stringRange2 = stringReg2.splitWithRegex2(by: "\\s+")
print(stringRange2)
stringRange2.forEach { //More userfriendly than Range(Swift.String.Index(_rawBits: 65536)..<Swift.String.Index(_rawBits: 327680)))
let nsRange = NSRange($0.1, in: stringReg2)
print("\($0.0), \(nsRange.location)...\(nsRange.location + nsRange.length)")
}
Output:
$>[("This", Range(Swift.String.Index(_rawBits: 262144)..<Swift.String.Index(_rawBits: 524288))), ("is", Range(Swift.String.Index(_rawBits: 655360)..<Swift.String.Index(_rawBits: 786432))), ("a", Range(Swift.String.Index(_rawBits: 851968)..<Swift.String.Index(_rawBits: 917504))), ("string", Range(Swift.String.Index(_rawBits: 983040)..<Swift.String.Index(_rawBits: 1376256)))]
$>This, 4...8
$>is, 10...12
$>a, 13...14
$>string, 15...21
Basicly I want something like this,
NSString* foobar(NSString *input) {
// say input is "1"
NSString *string = #"0123456789";
NSString *anotherString = #"零一二三四五六七八九";
NSRange range = [string rangeOfString:input];
// return "一" here
return [anotherString substringWithRange:range];
}
I tried the same stuff in Swift,
func foobar(input: String) -> String {
// say input is "1"
let string = "0123456789"
let range = string.range(of: input, options: .anchored)
let result = anotherString[range!]
// return "012" here
return String(result)
}
why?
And how can I achieve this?
String (or generally, collection) indices must only be used with the collection that they were created with. In order to find the same positions in another string, the indices must be converted to (integer) offsets and back to indices of the target string:
func foobar(input: String) -> String? {
let s1 = "0123456789"
let s2 = "😀一二三四五六七八九";
guard let range = s1.range(of: input) else {
return nil
}
let pos = s1.distance(from: s1.startIndex, to: range.lowerBound)
let len = s1.distance(from: range.lowerBound, to: range.upperBound)
guard
let lo = s2.index(s2.startIndex, offsetBy: pos, limitedBy: s2.endIndex),
let hi = s2.index(lo, offsetBy: len, limitedBy: s2.endIndex)
else {
return nil
}
return String(s2[lo..<hi])
}
print(foobar(input: "1") as Any) // Optional("一")
print(foobar(input: "123") as Any) // Optional("一二三")
print(foobar(input: "124") as Any) // nil
Your Objective-C code works as long as all characters in the string consume a single UTF-16 code unit (because that is what NSRange counts). It will not work correctly emojis, flags, and other characters which are represented as UTF-16 surrogate pairs, e.g. with
NSString *anotherString = #"😀一二三四五六七八九";
Another approach is converting strings to array of characters
func find(_ str: Character) {
let firstArr = Array("0123456789")
let secondArr = Array("零一二三四五六七八九")
guard let index = firstArr.firstIndex(of: str) else {
print("Not found")
return
}
print(firstArr[index]) // 2
print(secondArr[index]) // 二
}
find("2")
I have been trying to implement Coinbase API in my application that is built using swift 5 and xcode. Whenever I try to convert this base64 encoded data to Normal string, it returns nil.
Here is the string
fI5GtCKFF+O8j8uJlJVGxIvolVUUrVMT9GBouxlpEOUXmaGbwQvHt0z8kK0fUwQaKa45KUOLWHP/CQaM70bhdQ==
Below are the answers i've tried
extension String {
func base64Encoded() -> String? {
return data(using: .utf8)?.base64EncodedString()
}
func base64Decoded() -> String? {
var st = self;
print(st.count)
let remainder = self.count % 4
if remainder > 0 {
st = self.padding(toLength: self.count + 4 - remainder,
withPad: "=",
startingAt: 0)
}
print(st.count)
guard let d = Data(base64Encoded: st, options: .ignoreUnknownCharacters) else{
return nil
}
return String(data: d, encoding: .utf8)
}
}
THE DIFFERENCE
let base64Decoded = Data(base64Encoded: "fI5GtCKFF+O8j8uJlJVGxIvolVUUrVMT9GBouxlpEOUXmaGbwQvHt0z8kK0fUwQaKa45KUOLWHP/CQaM70bhdQ==")!
let sign = HMAC.sign(data: "1".toData()!, algorithm: .sha256, key: base64Decoded)
let signData = sign.base64EncodedString()
The result i receive: vBZ+Z63rFqNmU61yrd91PGQB8f1iqocWkLlAev5XJOc=
It's different from: https://www.liavaag.org/English/SHA-Generator/HMAC/
Link to result There's is the correct output. What am i missing or doing wrong ?
i've just converted my little app but i've found this error:
'substring(from:)' is deprecated: Please use String slicing subscript with a 'partial range from' operator
my code is:
let dateObj = dateFormatterFrom.date(from: dateStringa)
if dateObj != nil {
cell.detailTextLabel?.text = dateFormatterTo.string(from:(dateObj!))
} else {
let index = thisRecord.pubDate.index(thisRecord.pubDate.startIndex, offsetBy: 5)
cell.detailTextLabel?.text = thisRecord.pubDate.substring(from: index)
}
Follow the below example to fix this warning:
Supporting examples for Swift 3, 4 and 5.
let testStr = “Test Teja”
let finalStr = testStr.substring(to: index) // Swift 3
let finalStr = String(testStr[..<index]) // Swift 4
let finalStr = testStr.substring(from: index) // Swift 3
let finalStr = String(testStr[index...]) // Swift 4
//Swift 3
let finalStr = testStr.substring(from: index(startIndex, offsetBy: 3))
//Swift 4 and 5
let reqIndex = testStr.index(testStr.startIndex, offsetBy: 3)
let finalStr = String(testStr[..<reqIndex])
//**Swift 5.1.3 - usage of index**
let myStr = "Test Teja == iOS"
let startBound1 = String.Index(utf16Offset: 13, in: myStr)
let finalStr1 = String(myStr[startBound1...])// "iOS"
let startBound2 = String.Index(utf16Offset: 5, in: myStr)
let finalStr2 = String(myStr[startBound2..<myStr.endIndex]) //"Teja == iOS"
In place of substring use suffix. Use like below :
cell.detailTextLabel?.text = String(thisRecord.pubDate.suffix(from: index))
It means you should use the new partial range operator as your upperBound:
let str = "Hello World !!!"
if let index = str.range(of: "Hello ")?.upperBound {
let string = String(str[index...]) // "World !!!"
}
In your case
cell.detailTextLabel?.text = String(thisRecord.pubDate[index...]))
In Swift 5, it is:
extension String {
func index(from: Int) -> Index {
return self.index(startIndex, offsetBy: from)
}
func substring(from: Int) -> String {
let fromIndex = index(from: from)
return String(self[fromIndex...])
}
func substring(to: Int) -> String {
let toIndex = index(from: to)
return String(self[..<toIndex])
}
func substring(with r: Range<Int>) -> String {
let startIndex = index(from: r.lowerBound)
let endIndex = index(from: r.upperBound)
return String(self[startIndex..<endIndex])
}
}
Most of my strings have A-Za-z and 0-9 content. No need for difficult
Index handling. This extension of String is based on the familiar LEFT / MID and RIGHT functions.
extension String {
// LEFT
// Returns the specified number of chars from the left of the string
// let str = "Hello"
// print(str.left(3)) // Hel
func left(_ to: Int) -> String {
return "\(self[..<self.index(startIndex, offsetBy: to)])"
}
// RIGHT
// Returns the specified number of chars from the right of the string
// let str = "Hello"
// print(str.left(3)) // llo
func right(_ from: Int) -> String {
return "\(self[self.index(startIndex, offsetBy: self.length-from)...])"
}
// MID
// Returns the specified number of chars from the startpoint of the string
// let str = "Hello"
// print(str.left(2,amount: 2)) // ll
func mid(_ from: Int, amount: Int) -> String {
let x = "\(self[self.index(startIndex, offsetBy: from)...])"
return x.left(amount)
}
}
If you wish to get substring with specific offset without upper bound do the following:
let index = thisRecord.pubDate.index(thisRecord.pubDate.startIndex, offsetBy: 5)
cell.detailTextLabel?.text = String(thisRecord.pubDate[index...]
This way you create a new String object from your existing String thisRecord.pubDate taking anything from specified index to the end index of original String.
str[..<index]
str[index...]
The code above is "partial range from"
Look at this How can I use String slicing subscripts in Swift 4?
How to convert this type of string "8.37" to its decimal value 8.37?
I'm using Swift 4.
I tried:
extension String {
func hexToFloat() -> Float {
var toInt = Int32(truncatingIfNeeded: strtol(self, nil, 16))
var float:Float32!
memcpy(&float, &toInt, MemoryLayout.size(ofValue: float))
return float
}
}
[...]
let myString = "8.37"
let myDecimal = myString.hexToFloat()
print(myDecimal) // prints 0.0
(From here)
There's no straight forward way (at least to my knowledge). But you can do something like below to get the value. Please note that the code below is in Swift 3. You may have to change the syntax.
extension String {
func hexToFloat() -> Float {
let data = myString.data(using: .utf8)
let options: [String: Any] = [NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType]
do {
let attributedString = try NSAttributedString(data: data!, options: options, documentAttributes: nil)
return Float(attributedString.string)!
} catch {
return 0
}
}
}
let myString = "8.37"
let myDecimal = myString.hexToFloat()
print(myDecimal) // prints 8.37