5

I found something strange when trying to use width specifiers with %@. They work fine in NSLog but not with NSString stringWithFormat:.

Example:

NSString *rightAligned = @"foo";
NSString *leftAligned = @"1";

NSLog(@"| %15@ | %-15@ |", rightAligned, leftAligned);

And you get the expected output of:

|             foo | 1               |

But replace the NSLog with stringWithFormat::

NSString *test = [NSString stringWithFormat:@"| %15@ | %-15@ |", rightAligned, leftAligned];

And the value of test is incorrectly:

| foo | 1 |

If I change this to use %s and cStringUsingEncoding: then it works:

NSString *test2 = [NSString stringWithFormat:@"| %15s | %-15s |", [rightAligned cStringUsingEncoding:NSUTF8StringEncoding], [leftAligned cStringUsingEncoding:NSUTF8StringEncoding]];

The result is the same as with NSLog.

What makes this really strange is that NSLog is basically just a wrapper around NSString stringWithFormat:.

So why the different results? Why aren't format specifiers honored for %@ in stringWithFormat but they are with NSLog?

As a side note, the Swift String init(format:) initializer has the same problem with %@ and width specifiers.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • "NSLog is now just a shim to os_log in most circumstances." source: [Foundation Release Notes for macOS 10.12 and iOS 10](https://developer.apple.com/library/archive/releasenotes/Miscellaneous/RN-Foundation-OSX10.12/index.html) – Willeke May 01 '19 at 22:16

1 Answers1

1

The mystery is why %15@ would ever work. It should not.

Format specifiers come from sprintf which has no %@ (it is just a special extension for Objective-C). As far as stringWithFormat goes, %15s has always been the way to say this; I can cite Stack Overflow examples such as NSString stringwithformat: Padding 2 strings with unknown length.

I'm guessing it only "works" because it now uses os_log under the hood; unfortunately, the os_log syntax is almost completely undocumented.

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
matt
  • 515,959
  • 87
  • 875
  • 1,141
  • I suspect that if you try this with a sufficiently old version of Xcode, you'll find that NSLog matches `stringWithFormat` behavior. – matt May 08 '19 at 13:29
  • I understand why `%15s` works. The question is asking why `%15@` works in `NSLog` but not `NSString stringWithFormat:`. – rmaddy May 08 '19 at 15:35
  • "The question is asking why %15@ works in NSLog" Yes, and I'm saying "I don't know because it shouldn't." :) I'm guessing it's because it now uses `os_log` under the hood; unfortunate the `os_log` syntax is almost completely undocumented. – matt May 08 '19 at 15:46