【问题标题】:Swift 3 - How to convert memory of Int32 as four charactersSwift 3 - 如何将 Int32 的内存转换为四个字符
【发布时间】:2017-03-03 14:19:53
【问题描述】:

我想将 Int32 转换为由四个 C 风格、1 字节宽的字符组成的字符串(可能与 this 密切相关,但在 Swift 3 中)。

这样做的用途是 Core Audio 的许多 API 函数返回一个 OSStatus(实际上是一个 Int32),它通常可以解释为由四个 C 样式字符组成的字符串。

fun interpretAsString(possibleMsg: Int32) -> String {
  // Blackbox
}

【问题讨论】:

标签: ios swift swift3 core-audio int32


【解决方案1】:

实际上“四字符代码”通常是 无符号 32 位 价值:

public typealias FourCharCode = UInt32
public typealias OSType = FourCharCode

四个字节(从 MSB 到 LSB)每个定义一个字符。 这是一个简单的 Swift 3 函数,用于将整数转换为字符串, 受到各种 C/Objective-C/Swift 1+2 解决方案的启发 iOS/C: Convert "integer" into four character string:

func fourCCToString(_ value: FourCharCode) -> String {
    let utf16 = [
        UInt16((value >> 24) & 0xFF),
        UInt16((value >> 16) & 0xFF),
        UInt16((value >> 8) & 0xFF),
        UInt16((value & 0xFF)) ]
    return String(utf16CodeUnits: utf16, count: 4)
}

例子:

print(fourCCToString(0x48454C4F)) // HELO

我选择了一个带有 UTF-16 代码点的数组作为中间存储,因为它可以直接用于创建字符串。

如果您真的需要 有符号 32 位整数,那么您可以 打电话

fourCCToString(FourCharCode(bitPattern: i32value)

或使用Int32 参数定义类似的函数。

正如下面 Tim Vermeulen 所建议的,UTF-16 数组也可以是 使用map创建:

let utf16 = stride(from: 24, through: 0, by: -8).map {
    UInt16((value >> $0) & 0xFF)
}

let utf16 = [24, 16, 8, 0].map { UInt16((value >> $0) & 0xFF) }

除非该函数对您的应用程序的性能至关重要, 选择您认为最熟悉的内容(否则衡量和比较)。

【讨论】:

  • 我通常使用UInt16(UInt8.max) 而不是有点神奇的0xFF,尽管我想这是经验问题。我还建议使用map 来创建chars 数组,它会在您上次编辑时为您节省一些时间:)
  • @TimVermeulen:感谢您的反馈。我这样写是因为它易于理解,并且因为我假设它允许编译器更好地优化代码(没有调用回调的方法)。
  • 认为编译器将能够优化掉map,但您必须查看生成的程序集。我对stride 不太有信心,但如果这有问题,您可以随时将其替换为[24, 16, 8, 0]
【解决方案2】:

我不测试这段代码,但试试这个:

func interpretAsString(possibleMsg: Int32) -> String {
    var result = String()
    result.append(Character(UnicodeScalar(UInt32(possibleMsg>>24))!))
    result.append(Character(UnicodeScalar(UInt32((possibleMsg>>16) & UInt32(0xFF)))!))
    result.append(Character(UnicodeScalar(UInt32((possibleMsg>>8) & UInt32(0xFF)))!))
    result.append(Character(UnicodeScalar(UInt32((possibleMsg) & UInt32(0xFF)))!))
    return result
}

【讨论】:

    【解决方案3】:

    这可能是一个老问题,但由于它是在 Core Audio 的背景下提出的,所以我只想分享一个我正在玩的变体。

    对于 Core Audio,其中一些(但不是全部?)OSStatus/Int32 值是使用四个字符定义的,Apple 旧的 Core Audio 实用程序类中的一些代码可以提供灵感(非常类似于 linked question

    来自CAXException.h

    class CAX4CCStringNoQuote {
    public:
        CAX4CCStringNoQuote(OSStatus error) {
            // see if it appears to be a 4-char-code
            UInt32 beErr = CFSwapInt32HostToBig(error);
            char *str = mStr;
            memcpy(str, &beErr, 4);
            if (isprint(str[0]) && isprint(str[1]) && isprint(str[2]) && isprint(str[3])) {
                str[4] = '\0';
            } else if (error > -200000 && error < 200000)
                // no, format it as an integer
                snprintf(str, sizeof(mStr), "%d", (int)error);
            else
                snprintf(str, sizeof(mStr), "0x%x", (int)error);
        }
        const char *get() const { return mStr; }
        operator const char *() const { return mStr; }
    private:
        char mStr[16];
    };
    

    在 Swift 5 中,一种粗略的翻译(没有大值的十六进制表示)可能是:

        private func osStatusToString(_ value: OSStatus) -> String {
            let data = withUnsafeBytes(of: value.bigEndian, { Data($0) })
    
            // If all bytes are printable characters, we treat it like characters of a string
            if data.allSatisfy({ 0x20 <= $0 && $0 <= 0x7e }) {
                return String(data: data, encoding: .ascii)!
            } else {
                return String(value)
            }
        }
    

    请注意,Data 初始化程序正在复制字节,但如果需要,可以避免这种情况。

    当然,对于 Core Audio,我们会遇到 both Int32UInt32 类型的四个字符代码。我之前没有使用 Swift 完成泛型,但是在单个函数中处理它们的一种方法可能是:

        private func stringifyErrorCode<T: FixedWidthInteger>(_ value: T) -> String {
            let data = withUnsafeBytes(of: value.bigEndian, { Data($0) })
    
            // If all bytes are printable characters, we treat it like characters of a string
            if data.allSatisfy({ 0x20 <= $0 && $0 <= 0x7e }) {
                return String(data: data, encoding: .ascii)!
            } else {
                return String(value, radix: 10)
            }
        }
    
    

    这可能不适合四个字符代码的通用处理(我在上面的示例中看到了支持 MacOS 罗马编码与 ASCII 字符的其他答案。可能有一些我不知道的历史) ,但对于 Core Audio 状态/选择器代码可能是合理的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多