17

许多与音频会话编程相关的常量实际上是四字符字符串(音频会话服务参考)。这同样适用于从函数返回的OSStatus 代码AudioSessionGetProperty,例如.

问题是,当我尝试开箱即用地打印这些东西时,它们看起来像 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 不是很强大,但我的直觉是上面的代码试图将内存地址解释为字符串。或者可能存在编码问题?有任何想法吗?

4

16 回答 16

19

您正在谈论的类型是 a FourCharCode,定义在CFBase.h. 它相当于一个OSType. OSType在and之间转换的最简单方法NSString是使用NSFileTypeForHFSTypeCode()and NSHFSTypeCodeFromFileType()。不幸的是,这些功能在 iOS 上不可用。

对于 iOS 和 Cocoa-portable 代码,我喜欢Joachim Bengtsson 的 FourCC2Str()代码NCCommon.h(加上一些铸造清理以便于使用):

#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));

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

于 2013-01-08T19:41:00.370 回答
7

在 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)
}
于 2014-09-02T14:22:15.477 回答
4
char str[5];
str[4] = '\0';
long *code = (long *)str;
*code = 1919902568;
printf("%s\n", str);
于 2013-01-08T19:25:01.687 回答
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);
于 2013-01-08T19:42:56.020 回答
3

我为我的音频代码编写了这个 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; }
于 2013-11-23T02:16:15.333 回答
3

我建议使用这样的函数:

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.

于 2014-06-30T07:00:00.413 回答
3

如果您正在为 macOS 而不是 iOS 进行开发,那么 Launch Services 中已经有一个内置功能。将 4cc 作为 NSString 获取:

(__bridge_transfer NSString *)UTCreateStringForOSType(fourccInt)
于 2015-10-15T11:47:38.093 回答
3

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

斯威夫特 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 上都适用于我,并且与内置 macOSNSFileTypeForHFSTypeCodeNSHFSTypeCodeFromFileType

几个陷阱:

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

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

  1. NSFileTypeForHFSTypeCode在 sting 周围添加额外的单引号:NSFileTypeForHFSTypeCode(OSType(kAudioCodecBadDataError))) == "'bada'". 我已经选择退出bada
  2. 它无法生成\0中间的字符串,就像 in NSFileTypeForHFSTypeCode(kAudioFormatMicrosoftGSM)should bems\01但返回'ms
于 2020-02-23T22:56:56.047 回答
2

一个更 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))
于 2017-11-27T14:26:26.843 回答
2
  • 处理不同的字节序架构
  • 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;
}
于 2019-07-08T19:59:41.140 回答
0

将 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
    }
}
于 2016-08-03T11:22:54.617 回答
0

这是 jscom 代码的 Swift 3 版本,经过清理以消除重复:

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)")
}
于 2017-07-18T06:24:26.037 回答
0

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

extension String {

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

}
于 2019-08-20T21:20:06.913 回答
0

棘手的斯威夫特版本:

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

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

于 2020-01-30T09:53:14.230 回答
0
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) ?? "?")
        ])
    }
}
于 2020-12-20T16:00:17.047 回答
0

这是我上面首选答案的变体(感谢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
}
于 2021-12-06T11:26:08.530 回答