OC 转换整数到二进制 NSString

最近在我工作的一个项目中,我想用Object-C以二进制字符串的形式显示一个整数。虽然我写出了我认为是相当好的实现,我还是好奇想知道其他开发者是如何着手处理相同问题的。

我问了Nick Lockwood是否有时间编写相似的一些东西。记住几乎没有提供限制性的需求。解决方法可以是C函数、一个方法或者一个范畴,调用的名字并没有定义。

读一下看看四个不同的方法来将一个整数转换为一个二进制的NSString对象。



方法一:将整数作为NSString转换为二进制数(John)

以下展示出的内容只是为了调试方便, 性能并不是最主要的考虑因素。但是,我依旧在寻找是否有其他独一无二并且高效的办法。

作为我的第一次尝试,我决定使用一个基于C语言的数组并且在每个数组元素的最右的bit做上记号,之后 .我把新的值右移一位,然后接着循环,这就是这个方法的代码看起来的样子。

- (NSString *)intToBinary:(int)intValue{
  int byteBlock = 8,            // 8 bits per byte
      totalBits = (sizeof(int)) * byteBlock, // Total bits
      binaryDigit = totalBits; // Which digit are we processing   // C array - storage plus one for null
  char ndigit[totalBits + 1];
   while (binaryDigit-- > 0)
  {
    // Set digit in array based on rightmost bit
    ndigit[binaryDigit] = (intValue & 1) ? '1' : '0';
     // Shift incoming value one to right
    intValue >>= 1;  }   // Append null
  ndigit[totalBits] = 0;
   // Return the binary string
  return [NSString stringWithUTF8String:ndigit];}

按下列方式运行以上代码。

int intValue = 16;
NSLog(@"int: %d", intValue);
NSLog(@"res: %@", [self intToBinary:intValue]);
 
intValue = -1;
NSLog(@"int: %d", intValue);
NSLog(@"res: %@\n\n", [self intToBinary:intValue]);
 
intValue = 2147483647;
NSLog(@"int: %d", intValue);
NSLog(@"res: %@\n\n", [self intToBinary:intValue]);
 
intValue = -2147483648;
NSLog(@"int: %d", intValue);
NSLog(@"bin: %@\n\n", [self intToBinary:intValue]);

现在,那些数据看起来是这样的:

int16
bin: 00000000000000000000000000010000
 
int-1
bin: 11111111111111111111111111111111
 
int2147483647
bin: 01111111111111111111111111111111
 
int-2147483648
bin: 10000000000000000000000000000000

方法二: 用NSString转换整数为二进制字串(作者:John)

不管高效与否,我不能抱着抛出来的没有区分构成整数字节边界的一大堆1或0发晕啊。可以使用C数组的方法输出“漂亮的打印”吗,这是肯定的。这会搞乱代码吗?这是更为肯定的。所以我决定用另一种方式实现:

- (NSString *)intToBinary:(int)intValue{
  int byteBlock = 8,    // 每个字节8位
      totalBits = sizeof(int) * byteBlock, // 总位数(不写死,可以适应变化)
      binaryDigit = 1;  // 当前掩(masked)位
  NSMutableString *binaryStr = [[NSMutableString alloc] init];   // 二进制字串
   do
  {
    // 检出下一位,然后向左移位,附加 0 或 1
    [binaryStr insertString:((intValue & binaryDigit) ? @"1" : @"0" ) atIndex:0];
     // 若还有待处理的位(目的是为避免在最后加上分界符),且正处于字节边界,则加入分界符|
    if (--totalBits && !(totalBits % byteBlock))
      [binaryStr insertString:@"|" atIndex:0];
     // 移到下一位
    binaryDigit <<= 1;
   } while (totalBits);
   // 返回二进制字串
  return binaryStr;}

这种方法稍有不同,首先要提的就是使用了基于C的Objective-C可变字符串的数组。同时,不再对传入的值移位以获得每一位,这一次我用移位从最低到最高位进行检查。我希望创造一个更可读的输出,在代码中判断是否已到一个字节的边界(一个字节8位),若是则输出一个竖杠|,除了最末尾。

我通常都会把代码写得冗长(以避免可能的BUG),对这个小练手也一样。例如,在3–4两行的值可以用硬编码(指定固定的位数)和在do循环内分配。然而,我通常愿意在便携性和/或可读性上做出这样的权衡。

第二种方法的输出如下:

int16
bin: 00000000|00000000|00000000|00010000
 
int-1
bin: 11111111|11111111|11111111|11111111
 
int2147483647
bin: 01111111|11111111|11111111|11111111
 
int-2147483648
bin: 10000000|00000000|00000000|00000000


方法三:用NSString转换整数为二进制字符串(作者:Nick) 

当 John 让我创建一个从整数输出二进制字符串的方法时,我第一个本能反应当然就是去 Stack Overflow 找现成答案。 

虽然我是一个程序员而非抄袭者,而且我喜欢解决编程的麻烦问题,但我一直认为,好钢应该用在刀刃上,辛苦最好用于解决新的问题,而不是那些已被解决的。我期待能够有一个内置的解决方案,比如使用[NSString stringWithFormat:] 方法或者 NSNumberFormatter 就可以做到,但我很惊讶地发现居然没有。 

我从这个问题帖(http://stackoverflow.com/questions/111928/is-there-a-printf-converter-to-print-in-binary-format)获悉,一些C编译器在它们对printf函数的实现中包括了一个 %b 选项,用于打印二进制字符串,但它不是一个标准特性,并没有包括在Mac或iOS的实现中。 



在解决这个实际生成字符的串问题之前,我先思考代码接口该是什么样子。当把数字作为字符串打印时,我通常使用 stringWithFormat: 或 NSNumberFormatter,所以我的想法是把我的二进制格式化程序绑定到这些机制之一。

NSNumberFormatter似乎不支持任何手段来扩展其格式选项。当然,我可以使用一个类别(category)来添加额外的格式属性,但是为了使用它们我不得不滥用 stringFromNumber: 方法,这过于粗暴,非我所爱之调调。

stringWithFormat: 方法同样不能扩展。你可以通过重写Objective-C类的描述方法来创建它们的自定义描述,但滥用NSNumber的描述方法来返回二进制字符串显然是个坏主意(它将破坏那些希望它返回一个十进制数字字符串的代码),而创建一个自定义的类来只是封装一个整数以便打印它,有点像一个荒谬的过度设计。


我找到了一些内容,提及glib里有个很有前途的特性叫 register_printf_function(),它允许你指定自定义的 printf 格式字串。它的一个使用例子在 http://codingrelic.geekhold.com/2008/12/printf-acular.html。不幸的是,register_printf_function()似乎没有在Mac或iOS上实现–大概是被认为有安全风险,或只是并非需要优先考虑实现的事情。

我放弃了,看来没有容易的方式把这个二进制字符串格式化整合到现有的Objective-C格式化机制中,于是回到问题本身:

我之前在Stack Overflow上发现有许多实际生成二进制字符串的解决方案,但感觉它们太像是“C”的解决方案。我决定从无到有写点东西,更多使用Cocoa方法和类型以提高可读性。它牺牲了处理中的一些未加工的性能,但如果对性能不作要求,我更喜欢把代码优化得更清晰。


这就是我的解决方案: 

- (NSString *)binaryStringWithInteger:(NSInteger)value{
  NSMutableString *string = [NSMutableString string];  while (value)
  {
    [string insertString:(value & 1)? @"1": @"0" atIndex:0];
    value /= 2;  }
  return string;}

我想这是我见过的最短的解决方案,而且相当简单易懂。其基本原理和我碰到的大多数其他解决方案一样:在一个循环中,我让这个值与1进行AND运算以得到其最后一位的内容,然后通过除以2来移动字节的位。使它如此简洁的原因是,它只是简单地对一个 NSMutableString 预置了位的值,而不是更复杂的东西。 

我可以使用 >> 移位算子代替除号 /,但我个人发现整数除法的概念更容易掌握,我永远都无法记得那些>或<都代表数据的位发生怎样的变化!(随着时间,你的记忆和理解可能发生变化。而且对编译器来说,移位和乘除法没什么区别) 


这个方法的输出与 John 的解决方案有所不同。明显的是,我没有用零填充空位,所以如果输入4会生成“100″,输入1就会生成“1″,输入0则生成一个空字串。

接下来的问题是如何使用这些代码,它可以作为 NSString 的类别(category), 也可以作为公共的工具类。我可能更愿意把它实现为 NSNumber 的 category,因为它能够使接口更简洁,而只需要付出把输出包装为 NSNumber 这样一个很小的代价。看起来类似这样:

@interface NSNumber (Binary) - (NSString *)binaryString;
 @end @implementation NSNumber (Binary) - (NSString *)binaryString{
  NSMutableString *string = [NSMutableString string];
  NSInteger value = [self integerValue];  while (value)
  {
    [string insertString:(value & 1)? @"1": @"0" atIndex:0];
    value /= 2;  }
  return string;} @end

然后你就可以这样来调用它:

NSInteger input = 99;NSString *output = [@(input) binaryString]; //1100011

方法4:采用NSString将整数转换成二进制(Nick)

独立于John的解决方法,在此我写下我的解决方法,没有输出格式的指导,也并不比他的解法优越。看完John的解法之后,我好奇我是否需要以固定宽度和8比特分割的格式输出。John的方法是没有牺牲解决方法的简洁性的。下面是我想出来的方法用于填充比特:

- (NSString *)paddedBinaryString{
  NSMutableString *string = [NSMutableString string];
  NSInteger value = [self integerValue];  for (NSInteger i = 0; i < sizeof(value) * 8; i++)
  {
    [string insertString:(value & 1)? @"1": @"0" atIndex:0];
    value /= 2;  }
  return string;}

方法很简单---我仅仅采用for循环来代替while循环,目的是计算出比特的个数(这个值在当代的系统中应该是64)。如果采用对每个字节添加分割的方法,又会如何呢?

- (NSString *)delimitedBinaryString{
  NSMutableString *string = [NSMutableString string];
  NSInteger value = [self integerValue];  for (NSInteger i = 0; i < sizeof(value) * 8; i++)
  {
    if (i && i % 8 == 0) [string insertString:@"|" atIndex:0];    [string insertString:(value & 1)? @"1": @"0" atIndex:0];
    value /= 2;  }
  return string;}

在此我们添加了额外的一行,去简单地说明如果循环计数器不是0,但是已经可以被8整除,然后如何插入管道符。

我很高兴去谈论这些,但是当我仔细去查看的时候才意识到如果把幻数放入变量中,然后把for循环和do/while循环交换,这样的解决方法基本上是一样的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值