1

I have a string of binary values e.g. "010010000110010101111001". Is there a simple way to convert this string into its ascii representation to get (in this case) "Hey"?

Only found the other way or things for Integer:

    let binary = "11001"
    if let number = Int(binary, radix: 2) {
        print(number) // Output: 25
    }

Do someone know a good and efficient solution for this case?

2
  • I think your solution is simple too. Commented Mar 8, 2017 at 10:01
  • 1
    thats not my solution, I need one o get ascii values not ints Commented Mar 8, 2017 at 10:10

5 Answers 5

7

A variant of @OOPer's solution would be to use a conditionally binding while loop and index(_:offsetBy:limitedBy:) in order to iterate over the 8 character substrings, taking advantage of the fact that index(_:offsetBy:limitedBy:) returns nil when you try to advance past the limit.

let binaryBits = "010010000110010101111001"

var result = ""
var index = binaryBits.startIndex

while let next = binaryBits.index(index, offsetBy: 8, limitedBy: binaryBits.endIndex) {
    let asciiCode = UInt8(binaryBits[index..<next], radix: 2)!
    result.append(Character(UnicodeScalar(asciiCode)))
    index = next
}

print(result) // Hey

Note that we're going via Character rather than String in the intermediate step – this is simply to take advantage of the fact that Character is specially optimised for cases where the UTF-8 representation fits into 63 bytes, which is the case here. This saves heap-allocating an intermediate buffer for each character.


Purely for the fun of it, another approach could be to use sequence(state:next:) in order to create a sequence of the start and end indices of each substring, and then reduce in order to concatenate the resultant characters together into a string:

let binaryBits = "010010000110010101111001"

// returns a lazily evaluated sequence of the start and end indices for each substring
// of 8 characters.
let indices = sequence(state: binaryBits.startIndex, next: {
    index -> (index: String.Index, nextIndex: String.Index)? in

    let previousIndex = index

    // Advance the current index – if it didn't go past the limit, then return the
    // current index along with the advanced index as a new element of the sequence.
    return binaryBits.characters.formIndex(&index, offsetBy: 8, limitedBy: binaryBits.endIndex) ? (previousIndex, index) : nil
})

// iterate over the indices, concatenating the resultant characters together.
let result = indices.reduce("") {
    $0 + String(UnicodeScalar(UInt8(binaryBits[$1.index..<$1.nextIndex], radix: 2)!))
}

print(result) // Hey

On the face of it, this appears to be much less efficient than the first solution (due to the fact that reduce should copy the string at each iteration) – however it appears the compiler is able to perform some optimisations to make it not much slower than the first solution.

Sign up to request clarification or add additional context in comments.

3 Comments

I'm a real fan of sequence(state:next), even though it usually tend to suffer w.r.t. performance when compared to more "classic" approaches. Incidentally, my vote (or a yet-to-be-updated shortly previous one) on this good answer should push you over 999 Swift tag upvotes. So grats somewhat in advance on the gold badge! :D
@dfri Yeah, I would definitely use it more often if it's performance wasn't an issue and it's syntax was a little less daunting, I usually always prefer the more "classical" approaches :) And thanks for the +1 :D Unfortunately you missed the 1000th upvote by a minute or two! You got the 1001th upvote ;) Looking forward to wielding the mighty mjölnir :)
Yeah, me too :) darn, the 1000th would have been neat! Heh yes my deleted comment had some confusion w.r.t. the fact that only the silver badge was shown at your profile, but I guess the gold one will soon appear there as well :) The first hammer strike will hopefully give much satisfaction!
6

You may need to split the input binary digits into 8-bit chunks, and then convert each chunk to an ASCII character. I cannot think of a super simple way:

var binaryBits = "010010000110010101111001"

var index = binaryBits.startIndex
var result: String = ""
for _ in 0..<binaryBits.characters.count/8 {
    let nextIndex = binaryBits.index(index, offsetBy: 8)
    let charBits = binaryBits[index..<nextIndex]
    result += String(UnicodeScalar(UInt8(charBits, radix: 2)!))
    index = nextIndex
}
print(result) //->Hey

2 Comments

Can you help with reversing this?
@rv7284, Better start a new thread of your own. It may be a little bigger code than to write as a comment.
1

Does basically the same as OOPer's solution, but he/she was faster and has a shorter, more elegant approach :-)

func getASCIIString(from binaryString: String) -> String? {

    guard binaryString.characters.count % 8 == 0 else {
        return nil
    }

    var asciiCharacters = [String]()
    var asciiString = ""

    let startIndex = binaryString.startIndex
    var currentLowerIndex = startIndex

    while currentLowerIndex < binaryString.endIndex {

        let currentUpperIndex = binaryString.index(currentLowerIndex, offsetBy: 8)
        let character = binaryString.substring(with: Range(uncheckedBounds: (lower: currentLowerIndex, upper: currentUpperIndex)))
        asciiCharacters.append(character)
        currentLowerIndex = currentUpperIndex
    }

    for asciiChar in asciiCharacters {
        if let number = UInt8(asciiChar, radix: 2) {
            let character = String(describing: UnicodeScalar(number))
            asciiString.append(character)
        } else {
            return nil
        }
    }

    return asciiString
}

let binaryString = "010010000110010101111001"

if let asciiString = getASCIIString(from: binaryString) {
    print(asciiString) // Hey
}

Comments

1

A different approach

let bytes_string: String = "010010000110010101111001"
var range_count: Int = 0

let characters_array: [String] = Array(bytes_string.characters).map({ String($0)})

var conversion: String = ""

repeat
{
    let sub_range = characters_array[range_count ..< (range_count + 8)]
    let sub_string: String = sub_range.reduce("") { $0 + $1 }

    let character: String = String(UnicodeScalar(UInt8(sub_string, radix: 2)!))

    conversion += character

    range_count += 8

} while range_count < characters_array.count

print(conversion)

Comments

0

You can do this:

extension String {
    var binaryToAscii: String {
        stride(from: 0, through: count - 1, by: 8)
        .map { i in map { String($0)}[i..<(i + 8)].joined() }
        .map { String(UnicodeScalar(UInt8($0, radix: 2)!)) }
        .joined()
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.