简体   繁体   中英

How to encode extended ASCII/macOS Roman (characters from 128 to 255) on 1 byte in Swift?

I'm trying to write a .txt file with extended ASCII code, but I need to do it on 8-bit characters.

I'd love to get extended ASCII from Codepage 437 , but I can live with Mac OS Roman . But as it's operation on numbers, it shouldn't make any difference.

When using Character(UnicodeScalar(unicodePosition)) , it works well for 0 to 127. Each character is 8-bit. From 128th scalar up, they are not ASCII/macOS Roman and they're encoded on 16 bits.

So I can create an array of UInt8 with specific characters that I want to save to file.

let firstCharacter: UInt8 = 240 // Apple Logo in macOS Roman or "≡" in codepage 437

let secondCharacter: UInt8 = 236 // Infinity symbol on codepage 437 or "I" with two dots in macOS Roman

let listOfCharacters: [UInt8] = [firstCharacter, secondCharacter]

But I have no idea on how to save such a list to a file, and then display it as extendedASCII or macOS Roman encoding.

I need to operate on this numbers because I'm trying to implement Vigenre Cipher for extended ASCII alphabet (or macOS Roman) and I need the 8-bit input to be 8-bit output so the content of the file have exactly the same file size. I have to do it on 256 characters, hence I need extended ascii/macOS Roman.

I'd also need to read this kind of file back, so method for reading a textile encoded with extended ASCII would also be appreciated. I guess that's why there's String.Encoding.nonLossyASCII and not only .ascii ?

Codepage 437 is available as CFStringEncodings.dosLatinUS and can be converted to a String.Encoding as in How to use Big5 encoding in Swift on iOS :

let cfEnc = CFStringEncodings.dosLatinUS
let nsEnc = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(cfEnc.rawValue))
let encoding = String.Encoding(rawValue: nsEnc) // String.Encoding

Now you can convert the bytes to a string and back:

let bytes = Data([240, 236])
// CP437 to string:
if let string = String(data: bytes, encoding: encoding) {
    print(string) // ≡∞
    // String to CP437:
    if let bytes2 = string.data(using: encoding) {
        print(Array(bytes2)) // [240, 236]
    }
}

The simple approach is to start with a String instance, convert it to Data using a specified encoding and then convert it the [UInt8] array:

let text = "The quick brown fox ... éâ..."
let data = text.data(using: .macOSRoman)
let characters [UInt8](data)

Be carefule with your encryption. Most characters in the range between 0 and 31 cannot be represented in text. They might not occur in the original text. But they will appear in the encrypted text. If you don't avoid it, the result will be binary data that can no longer be converted to readable text.

So my final solution looks like this:

class FileManager {

    let encoding: String.Encoding

    init() {
        let cfEnc = CFStringEncodings.dosLatinUS
        let nsEnc = CFStringConvertEncodingToNSStringEncoding(CFStringEncoding(cfEnc.rawValue))
        let encoding = String.Encoding(rawValue: nsEnc)
        self.encoding = encoding
    }

    func loadFromFile(path: String) -> Data {
        return NSData(contentsOfFile: path)! as Data
    }

    func saveToFile(data: Data, path: String) {
        let string = dataToPage437String(data: data)
        let fileURL = URL(fileURLWithPath: path)
        try? string?.write(to: fileURL, atomically: false, encoding: encoding)
    }

    func page437StringToData(string: String) -> Data? {
         return string.data(using: encoding)
    }

    private func dataToPage437String(data: Data) -> String? {
        return String(data: data, encoding: encoding)
    }
}
class EncryptionEngine {

    func encrypt(originalData: Data, keyData: Data) -> Data {
        var encryptedData = [UInt8]()

        for (index, byte) in originalData.enumerated() {
            let indexInCurrentBlock = index % keyData.count
            let row = Int(byte)
            let column = Int(keyData[indexInCurrentBlock])
            //for pure Vigenère cipher modifier should be 0
            let modifier = index + 1
            let encryptedCharacter = UInt8((row + column + modifier) % 256)
            encryptedData.append(encryptedCharacter)
        }

        return Data(encryptedData)
    }
}
        let fileManager = FileManager()
        let encryptionEngine = EncryptionEngine()
        let originalData = fileManager.loadFromFile(path: "/Path/test2.txt")
        guard let keyData = fileManager.page437StringToData(string: "keyToEncryptTakenFromTextField") else { return }
        let encryptedData = encryptionEngine.encrypt(originalData: originalData, keyData: keyData)
        fileManager.saveToFile(data: encryptedData, path: "/Path/test_enc.txt")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM