【问题标题】:iOS/C: Convert "integer" into four character stringiOS/C:将“整数”转换为四个字符串
【发布时间】:2012-12-22 17:53:46
【问题描述】:

很多与音频会话编程相关的常量实际上是四个字符的字符串 (Audio Session Services Reference)。这同样适用于从 AudioSessionGetProperty 等函数返回的 OSStatus code

问题是,当我尝试开箱即用地打印这些东西时,它们看起来像 1919902568。我可以将其插入计算器并打开 ASCII 输出,它会告诉我“roch”,但必须有一个以编程方式执行此操作。

我在我的一个 C 函数中取得了有限的成功,其中包含以下块:

char str[20];
// see if it appears to be a four-character code
*(UInt32 *) (str + 1) = CFSwapInt32HostToBig(error);
if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4])) {
    str[0] = str[5] = '\'';
    str[6] = '\0';
} else {
    // no, format as integer
    sprintf(str, "%d", (int)error);
}

我想做的是从它当前的功能中抽象出这个特性,以便在其他地方使用它。我试过做

char * fourCharCode(UInt32 code) {
    // block
}
void someOtherFunction(UInt32 foo){
    printf("%s\n",fourCharCode(foo));
}

但这给了我“à*€/3íT:ê*€/+€/”,而不是“roch”。我的 C fu 不是很强大,但我的直觉是上面的代码试图将内存地址解释为字符串。或者可能存在编码问题?有什么想法吗?

【问题讨论】:

    标签: ios c


    【解决方案1】:

    您所说的类型是FourCharCode,在CFBase.h 中定义。它相当于OSType。在OSTypeNSString 之间进行转换的最简单方法是使用NSFileTypeForHFSTypeCode()NSHFSTypeCodeFromFileType()。遗憾的是,这些功能在 iOS 上不可用。

    对于 iOS 和 Cocoa 可移植代码,我喜欢他的 NCCommon.h 中的 Joachim Bengtsson's FourCC2Str()(加上一些更容易使用的转换清理):

    #include <TargetConditionals.h>
    #if TARGET_RT_BIG_ENDIAN
    #   define FourCC2Str(fourcc) (const char[]){*((char*)&fourcc), *(((char*)&fourcc)+1), *(((char*)&fourcc)+2), *(((char*)&fourcc)+3),0}
    #else
    #   define FourCC2Str(fourcc) (const char[]){*(((char*)&fourcc)+3), *(((char*)&fourcc)+2), *(((char*)&fourcc)+1), *(((char*)&fourcc)+0),0}
    #endif
    
    FourCharCode code = 'APPL';
    NSLog(@"%s", FourCC2Str(code));
    NSLog(@"%@", @(FourCC2Str(code));
    

    您当然可以将@() 放入宏中以便于使用。

    【讨论】:

    • 哦,这听起来真的很有用。它给了我一个开箱即用的错误“预期表达式”,所以我必须回到这个。
    • 已清理以使其更易于使用。它返回一个 char[] 而不是 char*,现在这似乎使事情变得混乱。
    • NSFileTypeForHFSTypeCode for 1634039412 烦人地返回一个包含单引号的 6 长 "\'aevt\'" :/
    【解决方案2】:

    在 Swift 中你会使用这个函数:

    func str4 (n: Int) -> String
    {
        var s: String = ""
        var i: Int = n
    
        for var j: Int = 0; j < 4; ++j
        {
            s = String(UnicodeScalar(i & 255)) + s
            i = i / 256
        }
    
        return (s)
    }
    

    这个函数将在三分之一的时间内完成与上面相同的操作:

    func str4 (n: Int) -> String
    {
        var s: String = String (UnicodeScalar((n >> 24) & 255))
        s.append(UnicodeScalar((n >> 16) & 255))
        s.append(UnicodeScalar((n >> 8) & 255))
        s.append(UnicodeScalar(n & 255))
        return (s)
    }
    

    相反的方式是:

    func val4 (s: String) -> Int
    {
        var n: Int = 0
        var r: String = ""
        if (countElements(s) > 4)
        {
            r = s.substringToIndex(advance(s.startIndex, 4))
        }
        else
        {
            r = s + "    "
            r = r.substringToIndex(advance(r.startIndex, 4))
        }
        for UniCodeChar in r.unicodeScalars
        {
            n = (n << 8) + (Int(UniCodeChar.value) & 255)
        }
    
        return (n)
    }
    

    【讨论】:

    • 只是为了简化你的一个函数 [code]func stringValue(unicodeValue: Int) -> String { var stringValue = "" var value = unicodeValue for _ in 0..
    • 另一种方式:extension Int { var unicodeStringValue: String { get { var stringValue = "" var value = self for _ in 0..
    【解决方案3】:
    char str[5];
    str[4] = '\0';
    long *code = (long *)str;
    *code = 1919902568;
    printf("%s\n", str);
    

    【讨论】:

    • 在找出一个 reverseStr 函数后,这行得通。我真的很想得到一个 DRYer 解决方案(比如将它包装在一个方法中),但它现在可以使用。
    【解决方案4】:

    整数 = 4 个字节 = 4 个字符。因此,要将整数转换为 char *,您可以简单地编写:

    char st[5] = {0};
    st[0] = yourInt & 0xff;
    st[1] = (yourInt >> 8) & 0xff;
    st[2] = (yourInt >> 16) & 0xff;
    st[3] = (yourInt >> 24) & 0xff;
    

    将其转换回来:

    yourInt = st[0] | (st[1] << 8) | (st[2] << 16) | (st[3] << 24);
    

    【讨论】:

    • 如果有人想打印字符串,添加st[4] = '\0';不是很好吗?
    • 可能看起来很简单,但方法是错误的。第一个字符是最高字节。 #define FourCC2Str(code) (char[5]){(code &gt;&gt; 24) &amp; 0xFF, (code &gt;&gt; 16) &amp; 0xFF, (code &gt;&gt; 8) &amp; 0xFF, code &amp; 0xFF, 0}
    • 这并没有错,因为现在大多数 CPU,包括 Intel 的 CPU,都是 little-endian。
    • 这是不可取的,因为你不知道 st[4] 是你要改变的。它位于存储 FourBytesCode 的内存之外。请记住,这些几乎总是简单的 Int32 变量,而不是“内存块”,您要归零的这个字节很容易成为另一个变量的一部分,或者只是立即使您的程序崩溃。
    • @JanRüegg st[4] 由于 = {0} 初始化,已经是 \0。 @MottiShneor 为什么 st[4] 一旦在堆栈上就不能是你的了?
    【解决方案5】:

    我为我的音频代码编写了这个 C 函数……它可能有点幼稚,但它对我来说已经足够好:

    NSString* fourCharNSStringForFourCharCode(FourCharCode aCode){
    
    char fourChar[5] = {(aCode >> 24) & 0xFF, (aCode >> 16) & 0xFF, (aCode >> 8) & 0xFF, aCode & 0xFF, 0};
    
    NSString *fourCharString = [NSString stringWithCString:fourChar encoding:NSUTF8StringEncoding];
    
    return fourCharString; }
    

    【讨论】:

      【解决方案6】:

      如果您正在为 macOS 而不是 iOS 进行开发,那么 Launch Services 中已经为此提供了一个内置函数。获取 4cc 作为 NSString:

      (__bridge_transfer NSString *)UTCreateStringForOSType(fourccInt)
      

      【讨论】:

      • 与查尔斯主义的现有答案相同。
      • 没有那个名字的人回答,也没有提到UTCreateStringForOSType/()的回答
      • 他在 10 小时前删除了它。尽管如此,它仍然适用于 macOS,并且对我编写 macOS 应用程序有很大帮助。然而,正如他正确回答的那样,最初的问题是关于 iOS 的,其中 UTCreateStringForOSType - 所以你的答案也受到这个问题的影响。
      • 这是迄今为止最好的答案。原因:它是由 Apple 使用、创建和维护的预期功能,因此 - 未来更改安全。另外,我相信它应该是有效的,它的名字是描述性的。到处找这个
      【解决方案7】:

      这是我在测试目标中使用的辅助函数:

      斯威夫特 5:

      extension FourCharCode {
          private static let bytesSize = MemoryLayout<Self>.size
          var codeString: String {
              get {
                  withUnsafePointer(to: bigEndian) { pointer in
                      pointer.withMemoryRebound(to: UInt8.self, capacity: Self.bytesSize) { bytes in
                          String(bytes: UnsafeBufferPointer(start: bytes,
                                                            count: Self.bytesSize),
                                 encoding: .macOSRoman)!
                      }
                  }
              }
          }
      }
      
      extension OSStatus {
          var codeString: String {
              FourCharCode(bitPattern: self).codeString
          }
      }
      
      private func fourChars(_ string: String) -> String? {
          string.count == MemoryLayout<FourCharCode>.size ? string : nil
      }
      private func fourBytes(_ string: String) -> Data? {
          fourChars(string)?.data(using: .macOSRoman, allowLossyConversion: false)
      }
      func stringCode(_ string: String) -> FourCharCode {
          fourBytes(string)?.withUnsafeBytes { $0.load(as: FourCharCode.self).byteSwapped } ?? 0
      }
      

      它在 macOS 和 iOS 上都适用于我,并且与内置 macOS NSFileTypeForHFSTypeCodeNSHFSTypeCodeFromFileType 的行为非常匹配

      几个问题:

      1. 使用bigEndianbyteSwapped
      2. 使用macOSRoman编码
      3. 通过返回0来处理长字符串,如NSHFSTypeCodeFromFileType

      上述实现与标准库方法的区别:

      1. NSFileTypeForHFSTypeCode 在字符串周围添加额外的单引号:NSFileTypeForHFSTypeCode(OSType(kAudioCodecBadDataError))) == "'bada'"。我选择不返回bada
      2. 它无法生成中间有\0 的字符串,比如NSFileTypeForHFSTypeCode(kAudioFormatMicrosoftGSM) 应该是ms\01 但返回'ms

      【讨论】:

        【解决方案8】:

        我建议使用这样的函数:

        static NSString * NSStringFromCode(UInt32 code)
        {
            UInt8 chars[4];
            *(UInt32 *)chars = code;
            for(UInt32 i = 0; i < 4; ++i)
            {
                if(!isprint(chars[i]))
                {
                    return [NSString stringWithFormat:@"%u", code];
                }
            }
            return [NSString stringWithFormat:@"%c%c%c%c", chars[3], chars[2], chars[1], chars[0]];
        }
        

        这将确保您最终不会得到某些 FourCharCodes 的随机结果,这些结果是实际数字,例如 kCMPixelFormat_32ARGB = 32

        【讨论】:

          【解决方案9】:

          更 Swifty 的实现,适用于 Swift 4:

          extension String {
              init(fourCharCode: FourCharCode) {
                  let n = Int(fourCharCode)
                  var s: String = ""
          
                  let unicodes = [UnicodeScalar((n >> 24) & 255), UnicodeScalar((n >> 16) & 255), UnicodeScalar((n >> 8) & 255), UnicodeScalar(n & 255)]
                  unicodes.flatMap { (unicode) -> String? in
                      guard let unicode = unicode else { return nil }
                      return String(unicode)
                  }.forEach { (unicode) in
                      s.append(unicode)
                  }
          
                  self = s.trimmingCharacters(in: CharacterSet.whitespaces)
              }
          }
          

          可以如下使用:

          String(fourCharCode: CMFormatDescriptionGetMediaSubType(charCode))
          

          【讨论】:

            【解决方案10】:
            • 处理不同的字节序架构
            • iOS 和 MacOS()
            • 简单易懂
            • 结果字符串(应该)与 OSType 相同,即使是 8 位字符
            #import <Cocoa/Cocoa.h>
            #import <CoreServices/CoreServices.h> // for EndianU32_NtoB
            @implementation NSString (FourCC)
            
            + (NSString *) stringFromFourCC: (OSType) cccc
            {
                NSString * result = nil;
                cccc = EndianU32_NtoB(cccc); // convert to network byte order, if needed
                NSData * data = [NSData dataWithBytes: &cccc length: sizeof(OSType)];
                result = [[NSString alloc] initWithData: data encoding: NSWindowsCP1252StringEncoding]; // lossless 8-bit encoding, could also use NSMacOSRomanStringEncoding
                return result;
            }
            

            【讨论】:

            • 参见前面的文本框和积分 cmets。如果这还不够,请提出具体问题。
            • 我认为你可以避免创建 NSData 包装器对象,并直接从“字节”构建 NSString 使用: - (nullable instancetype)initWithBytes:(const void *)bytes length:(NSUInteger)len encoding:( NSStringEncoding)编码;
            【解决方案11】:

            将 Swift 2.2 版本用作两个扩展:

            extension String {
                public var fourCharCode:FourCharCode {
                    var string = self
                    if unicodeScalars.count < 4 {
                        string = self + "    "
                    }
                    string = string.substringToIndex(string.startIndex.advancedBy(4))
            
                    var res:FourCharCode = 0
                    for unicodeScalar in string.unicodeScalars {
                        res = (res << 8) + (FourCharCode(unicodeScalar) & 255)
                    }
            
                    return res
                }
            }
            
            extension FourCharCode {
                public var string:String {
                    var res = String (UnicodeScalar((self >> 24) & 255))
                    res.append(UnicodeScalar((self >> 16) & 255))
                    res.append(UnicodeScalar((self >> 8) & 255))
                    res.append(UnicodeScalar(self & 255))
                    return res
                }
            }
            

            【讨论】:

              【解决方案12】:

              这是 Swift 3 版本的 j.s.com 代码,已清理以消除重复:

              func debugPrintFourCcCode(_ value: Int) {
                var res = ""
              
                if value <= 0x28 {
                  res = "\(value)"
                } else {
                  func add(_ pos: Int) {
                    res.append(String(UnicodeScalar((value >> pos) & 255)!))
                  }
              
                  add(24)
                  add(16)
                  add(8)
                  add(0)
                }
              
                NSLog("Code: \(res)")
              }
              

              【讨论】:

                【解决方案13】:

                Tricky Swift 版本:

                func convert(fileType: OSType) -> String {
                    let chars = [24, 16, 8, 0].map { Character(UnicodeScalar(UInt8(fileType >> $0 & 0xFF)))}
                    return String(chars)
                }
                

                我已经在 Swift 5 上进行了测试,但应该可以在以前的 Swift 版本上运行。

                【讨论】:

                  【解决方案14】:

                  适用于 Mac 的版本,但不适用于 iOS:

                  extension String {
                  
                      init(_ fourCharCode: FourCharCode) { // or `OSType`, or `UInt32`
                          self = NSFileTypeForHFSTypeCode(fourCharCode).trimmingCharacters(in: CharacterSet(charactersIn: "'"))
                      }
                  
                  }
                  

                  【讨论】:

                    【解决方案15】:
                    public extension UInt32 {
                        var string: String {
                            String([
                                Character(Unicode.Scalar(self >> 24 & 0xFF) ?? "?"),
                                Character(Unicode.Scalar(self >> 16 & 0xFF) ?? "?"),
                                Character(Unicode.Scalar(self >> 8 & 0xFF) ?? "?"),
                                Character(Unicode.Scalar(self & 0xFF) ?? "?")
                            ])
                        }
                    }
                    

                    【讨论】:

                      【解决方案16】:

                      这是我上面首选答案的变体(感谢 pawneebill) 我使用不同的 NSString 初始化程序保存了 NSData 中间对象的创建和释放。

                      #import <Cocoa/Cocoa.h>
                      #import <CoreServices/CoreServices.h> // for EndianU32_NtoB
                      @implementation NSString (FourCC)
                      
                      + (NSString *) stringFromFourCC: (OSType) cccc
                      {
                          NSString * result = nil;
                          cccc = EndianU32_NtoB(cccc); // convert to network byte order if needed
                          result = [[NSString alloc] initWithBytes: &cccc length: sizeof(cccc) encoding: NSMacOSRomanStringEncoding]; // lossless 8-bit encoding
                          return result;
                      }
                      

                      或者,更紧凑但解释更少......

                      + (NSString *) stringFromFourCC: (OSType) cccc
                      {
                          cccc = EndianU32_NtoB(cccc); // convert to network byte order if needed
                          return [[NSString alloc] initWithBytes: &cccc length: sizeof(cccc) encoding: NSMacOSRomanStringEncoding]; // lossless 8-bit encoding
                      }
                      

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2014-03-06
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        相关资源
                        最近更新 更多