2014-11-25 23 views
6

我已經得到了一個看起來像NSAttributedString迭代格式%佔位符逐個

「Somestring 大膽%@模板%F%d等等等等等等」

我希望能夠更換格式模板部分就像[NSString stringWithFormat:...],但保持樣式,以便被替換的字符串與它們周圍的樣式(在我上面的示例中全是粗體)匹配。

有沒有一種方法來遍歷每個格式%佔位符一個接一個,所以我可以使用參數列表填充字符串?

我不想構建自己的%實現,因爲我知道這裏有一百萬種和一種不同的格式。

或者有沒有更容易忽略的解決方案?

編輯: 我會解釋一下完整的解決方案,我解決的:

爲了能夠爲我的團隊屬性本地化的字符串我已經有了一個辦法寫

"key"="test //italic %@// **bold** __underline %d__"; 

如果說明符位於屬性標記之間,我希望將該部分歸爲屬性。目前我可以創建如上所示的屬性字符串,下一部分是照顧剩下的說明符。

我做它的順序解析屬性 - >應用參數 ...,我可以很容易地解決它的其他方式,但我不想格式參數惹屬性

+0

是;請參閱'va_start()','va_next()'和'va_end()'。 – trojanfoe 2014-11-25 07:43:01

+0

@trojanfoe我指的是佔位符百分比令牌,而不是參數本身。如果我可以確定每個百分比標記的位置,那麼我可以迭代參數列表並替換屬性字符串中的標記並保留屬性(粗體,斜體等) – SomeGuy 2014-11-25 08:21:02

+0

查找每個'%'說明符都很簡單,一個'NSScanner',但我不清楚你想做什麼。 「我不想構建我自己的%實現...」是什麼意思? – 2014-11-25 10:16:14

回答

2

非常感謝@Rick指引我朝着正確的方向前進。

他的帖子建議首先檢查參數並在應用格式字符串之前預先轉義任何字符串或字符對象。這讓我想起了另一個問題,我試圖迭代不同類型的參數列表(NSString,int等),就像NSLog一樣。我想我發現它不可能(或者至少真的很難)做到這一點,NSLog可以通過格式說明符(%@,%i等)知道什麼類型。

我知道我實際上可以通過轉義參數獲得相同的效果,而不是通過轉義格式字符串本身的

例子:

format: "test //italic %@//" 
args: "text // more text" 

步驟:

  1. 先用//代替//所有實例 - 標籤 - //
  2. 應用參數
  3. 確定在何處造型之間適用// - TAG - //

Obvio好吧// - TAG - //仍然可以寫入參數來搞亂造型,但是根據你用作替代品的情況,這種情況發生的機率基本爲零。

+0

很高興看到它解決:) – Rick 2014-11-28 15:24:17

1

我在解析的順序做屬性 - >應用參數...,我 可以很容易地解決它的其他方式,但我不想格式參數 惹屬性

爲什麼不簡單地添加一個轉義字符?根據我的理解,如果第一個字符串包含雙斜槓,那麼運行您提供的示例的風險會變得很糟糕?

"key"="test //italic %@// **bold** __underline %d__"; 

如果%@text // more text會搞砸了格式化。

如果是這樣,那麼只需解析NSStringchar類型的每個變量值以確保它們不包含爲您的屬性保留的任何字符。如果他們這樣做,請在解析屬性之前添加一些轉義字符。

上面的例子是這樣的應用參數之後:

"key"="test //italic text \/\/ more text// **bold** __underline 34__"; 

之後,你像以前解析的屬性,同樣的方式,但你忽略\前面的字符,並確保去除\

這是一個努力,但我敢打賭它比實施自己的printf樣式解析器要少得多。

+0

嘿,你的意思是有可能通過然後在應用格式之前預先轉義任何字符,然後在最後使用樣式。我認爲這可能是一個可行的解決方案。我遇到的另一個問題是遍歷不同類型的參數列表,比如說一個NSString後跟一個int,然後是一個浮點數等等,你知道這可以工作嗎? – SomeGuy 2014-11-28 10:49:21

+0

是的,確切地說。然而,由於只有字符串和字符可能會導致格式問題,所以您只需要事先對其進行修改,如果您之後有其他原因需要進行參數替換。關於你的第二個問題,我想說它可能會自行提出一個新問題。 – Rick 2014-11-28 14:19:18

+0

@Rick你是否贊成我的回答? – trojanfoe 2014-11-28 14:22:22

1

這裏是工作代碼:

#import <Foundation/Foundation.h> 

@interface NSAttributedString (AttributedFormat) 
- (instancetype)initWithFormat:(NSAttributedString *)attrFormat, ...; 
- (instancetype)initWithFormat:(NSAttributedString *)attrFormat arguments:(va_list)arguments; 
@end 

@implementation NSAttributedString (AttributedFormat) 

- (instancetype)initWithFormat:(NSAttributedString *)attrFormat, ... { 
    va_list args; 
    va_start(args, attrFormat); 
    self = [self initWithFormat:attrFormat arguments:args]; 
    va_end(args); 
    return self; 
} 

- (instancetype)initWithFormat:(NSAttributedString *)attrFormat arguments:(va_list)arguments { 
    NSRegularExpression *regex; 
    regex = [[NSRegularExpression alloc] initWithPattern: @"(%.*?[@%dDuUxXoOfeEgGccsSpaAF])" 
               options: 0 
                error: nil]; 
    NSString *format = attrFormat.string; 
    format = [regex stringByReplacingMatchesInString: format 
              options: 0 
               range: NSMakeRange(0, format.length) 
             withTemplate: @"\0$1\0"]; 
    NSString *result = [[NSString alloc] initWithFormat:format arguments:arguments]; 
    NSMutableArray *f_comps = [format componentsSeparatedByString:@"\0"].mutableCopy; 
    NSMutableArray *r_comps = [result componentsSeparatedByString:@"\0"].mutableCopy; 

    NSMutableAttributedString *output = [[NSMutableAttributedString alloc] init]; 
    __block int consumed_length = 0; 

    [attrFormat enumerateAttributesInRange:NSMakeRange(0, attrFormat.length) options:0 usingBlock:^(NSDictionary *attrs, NSRange range, BOOL *stop) { 
     NSMutableString *substr = [NSMutableString string]; 
     while(f_comps.count > 0 && NSMaxRange(range) >= consumed_length + [(NSString *)f_comps[0] length]){ 
      NSString *f_str = f_comps[0]; 
      NSString *r_str = r_comps[0]; 
      [substr appendString:r_str]; 
      [f_comps removeObjectAtIndex:0]; 
      [r_comps removeObjectAtIndex:0]; 
      consumed_length += f_str.length; 
     } 

     NSUInteger idx = NSMaxRange(range) - consumed_length; 
     if(f_comps.count > 0 && idx > 0) { 
      NSString *f_str = f_comps[0]; 

      NSString *leading = [f_str substringToIndex:idx]; 
      [substr appendString:leading]; 

      NSString *trailing = [f_str substringFromIndex:idx]; 
      [f_comps replaceObjectAtIndex:0 withObject:trailing]; 
      [r_comps replaceObjectAtIndex:0 withObject:trailing]; 
      consumed_length += idx; 
     } 
     [output appendAttributedString:[[NSAttributedString alloc] initWithString:substr attributes:attrs]]; 
    }]; 

    return [self initWithAttributedString:output]; 
} 
@end 

用例:

NSMutableAttributedString *fmt = [[NSMutableAttributedString alloc] initWithString:@"test: "]; 
[fmt appendAttributedString: [[NSAttributedString alloc] initWithString: @"Some%%string" 
                  attributes: @{ 
                      NSFontAttributeName: [UIFont systemFontOfSize:17] 
                      }]]; 
[fmt appendAttributedString: [[NSAttributedString alloc] initWithString: @"bold %@ template %.3f %d" 
                  attributes: @{ 
                      NSFontAttributeName: [UIFont boldSystemFontOfSize:20], 
                      NSForegroundColorAttributeName: [UIColor redColor] 
                      }]]; 
[fmt appendAttributedString: [[NSAttributedString alloc] initWithString: @"%@ blah blah blah" 
                  attributes: @{ 
                      NSFontAttributeName: [UIFont systemFontOfSize:16], 
                      NSForegroundColorAttributeName: [UIColor blueColor] 
                      }]]; 

NSAttributedString *result = [[NSAttributedString alloc] initWithFormat:fmt, @"[foo]", 1.23, 56, @"[[bar]]"]; 

結果:

screenshot

也許這還是有一些缺陷,但它應該在大多數工作案例。


(%.*?[@%dDuUxXoOfeEgGccsSpaAF]) 

此正則表達式匹配"Format Specifiers"。說明符以%開頭並以列出的字符結尾。並可能在它們之間有一些修飾符。這不是完美的,例如這種非法格式"%__s"應該被忽略,但我的正則表達式匹配這整個字符串。但只要說明符是合法的,它就應該有效。

我的代碼與它匹配,並插入周圍的符分隔符:

I'm %s. 
I'm <delimiter>%s<delimiter>. 

我用\0作爲分隔符。

I'm \0%s\0. 

然後插入它。

I'm \0rintaro\0. 

然後分裂的格式和與所述分隔符的結果:

f_comps: ["I'm ", "%s", "."] 
r_comps: ["I'm ", "rintaro", "."] 

這裏,f_comps總串長度是精確的相同原始歸因格式。然後,用enumerateAttributesInRange迭代屬性,我們可以將這些屬性應用到結果中。

我很抱歉,但它是很難解釋清楚的作業裏面enumerateAttributesInRange,用我的英語不好:)

+0

你能解釋正則表達式和componentsSeparatedByString更深入?從我看到它在分解符的位置分裂,不知道那裏發生了什麼。此外,我仍然擔心自己的指定執行,但對其他人可能有用。 – SomeGuy 2014-11-28 23:47:24

+0

加入回答:) – rintaro 2014-11-29 05:03:36