使用 NSPredicate 分析字符串
你好!这个问题很长,所以一定要先坐下来:)
在我的代码中的某个时刻,我从用户那里得到一个字符串,然后我分析该字符串。我所说的分析是指遍历每个字符并根据用户在 NSPredicateEditor 中设置的谓词对其进行数学计算。谓词是这样以编程方式设置的:
NSArray* keyPaths = [NSArray arrayWithObjects:
[NSExpression expressionForKeyPath: @"Character"],
[NSExpression expressionForKeyPath: @"Character Before"],
[NSExpression expressionForKeyPath: @"Character After"],
nil];
// -----------------------------------------
NSArray* constants = [NSArray arrayWithObjects:
[NSExpression expressionForConstantValue: @"Any letter"],
[NSExpression expressionForConstantValue: @"Letter (uppercase)"],
[NSExpression expressionForConstantValue: @"Letter (lowercase)"],
[NSExpression expressionForConstantValue: @"Number"],
[NSExpression expressionForConstantValue: @"Alphanumerical"],
[NSExpression expressionForConstantValue: @"Ponctuation Mark"],
nil];
// -----------------------------------------
NSArray* compoundTypes = [NSArray arrayWithObjects:
[NSNumber numberWithInteger: NSNotPredicateType],
[NSNumber numberWithInteger: NSAndPredicateType],
[NSNumber numberWithInteger: NSOrPredicateType],
nil];
// -----------------------------------------
NSArray* operatorsA = [NSArray arrayWithObjects:
[NSNumber numberWithInteger: NSEqualToPredicateOperatorType],
[NSNumber numberWithInteger: NSNotEqualToPredicateOperatorType],
nil];
NSArray* operatorsB = [NSArray arrayWithObjects:
[NSNumber numberWithInteger: NSInPredicateOperatorType],
nil];
// -----------------------------------------
NSPredicateEditorRowTemplate* template1 = [[NSPredicateEditorRowTemplate alloc] initWithLeftExpressions: keyPaths
rightExpressions: constants
modifier: NSDirectPredicateModifier
operators: operatorsA
options: 0];
NSPredicateEditorRowTemplate* template2 = [[NSPredicateEditorRowTemplate alloc] initWithLeftExpressions: keyPaths
rightExpressionAttributeType: NSStringAttributeType
modifier: NSDirectPredicateModifier
operators: operatorsB
options: 0];
NSPredicateEditorRowTemplate* compound = [[NSPredicateEditorRowTemplate alloc] initWithCompoundTypes: compoundTypes];
// -----------------------------------------
NSArray* rowTemplates = [NSArray arrayWithObjects: template1, template2, compound, nil];
[myPredicateEditor setRowTemplates: rowTemplates];
所以你可以看到我有三个关键路径和一些可以与它们进行比较的常量。 在分析字符串时,我基本上想用伪代码执行此操作:
originalString = [string from NSTextView]
for (char in originalString)
bChar = [character before char]
aChar = [character after char]
predicate = [predicate from myPredicateEditor]
// using predicate - problem!
result = [evaluate predicate with:
bChar somehow 'linked' to keypath 'Character Before'
char 'linked' to 'Character'
aChar 'linked' to 'Character After' // These values change, of course
and also:
constant "All letters" as "abc...WXYZ"
constant "Numbers" as "0123456789"
etc for all other constants set up // These don't
]
if (result) then do something with char, bChar and aChar
您可以看到我的问题基本上出在哪里:
-
'Character Before/After' 由于空格而不能成为键路径,但我想保持这种方式对于用户来说更漂亮(想象一下有一些像“characterBefore”的东西......)
- 常量实际上代表像“0123456789”这样的字符串,但我也无法向用户显示
我能够找到解决此问题的方法,但我现在它不适用于每个字符,而且它也非常效率低下(至少在我看来)。我所做的就是从谓词中获取谓词格式,替换我必须替换的内容,然后评估新格式。现在,一些真实的代码可以解释这一点:
#define kPredicateSubstitutionsDict [NSDictionary dictionaryWithObjectsAndKeys: \
@"IN 'abcdefghijklmnopqrstuvwxyz'", @"== \"Letter (lowercase)\"", \
@"IN 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'", @"== \"Letter (uppercase)\"", \
@"IN 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'", @"== \"Any letter\"", \
@"IN '1234567890'", @"== \"Number\"", \
@"IN 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'", @"== \"Alphanumerical\"", \
@"IN ',.;:!?'", @"== \"Ponctuation Mark\"", \
\
@"MATCHES '[^a-z]'" , @"!= \"Letter (lowercase)\"", \
@"MATCHES '[^A-Z]'" , @"!= \"Letter (uppercase)\"", \
@"MATCHES '[^a-zA-Z]'" , @"!= \"Any letter\"", \
@"MATCHES '[^0-9]'" , @"!= \"Number\"", \
@"MATCHES '[^a-zA-Z0-9]'" , @"!= \"Alphanumerical\"", \
@"MATCHES '[^,\.;:!\?]'" , @"!= \"Ponctuation Mark\"", \
\
nil]
// NSPredicate* predicate is setup in another place
// NSString* originalString is also setup in another place
NSString* predFormat = [predicate predicateFormat];
for (NSString* key in [kPredicateSubstitutionsDict allKeys]) {
prefFormat = [predFormat stringByReplacingOccurrencesOfString: key withString: [kPredicateSubstitutionsDict objectForKey: key]];
}
for (NSInteger i = 0; i < [originalString length]; i++) {
NSString* charString = [originalString substringWithRange: NSMakeRange(i, 1)];
NSString* bfString;
NSString* afString;
if (i == 0) {
bfString = @"";
}
else {
bfString = [originalString substringWithRange: NSMakeRange(i - 1, 1)];
}
if (i == [originalString length] - 1) {
afString = @"";
}
else {
afString = [originalString substringWithRange: NSMakeRange(i + 1, 1)];
}
predFormat = [predFormat stringByReplacingOccurrencesOfString: @"Character Before" withString: [NSString stringWithFormat: @"\"%@\"", bfString]];
predFormat = [predFormat stringByReplacingOccurrencesOfString: @"Character After" withString: [NSString stringWithFormat: @"\"%@\"", afString]];
predFormat = [predFormat stringByReplacingOccurrencesOfString: @"Character" withString: [NSString stringWithFormat: @"\"%@\"", charString]];
NSPredicate* newPred = [NSPredicate predicateWithFormat: predFormat];
if ([newPred evaluateWithObject: self]) { // self just so I give it something (nothing is actually gotten from self)
// if predicate evaluates to true, then do something exciting!
}
}
所以,给你,这是我正在做的事情的简化版本。如果您看到任何拼写错误,很可能它们不在我的代码中,因为我对此进行了相当多的编辑,因此它会更简单。
总结一下:
- 我需要评估用户对许多字符所做的谓词,对其进行大量修改,同时尝试尽可能高效
我的方法发现的问题是:
- 我认为它一点也不干净
- 我不能保证它适用于每种情况(当其中一个字符是换行符/输入字符时,谓词会引发错误,指出它无法理解格式)
这就是大家!感谢您阅读到目前为止。愿你的神与你同在,解决这个烂摊子!
编辑:只是为了澄清相关问题,我想补充一点,似乎触发此问题的事实是,当我设置谓词编辑器时,我不能在一开始就定义一个带有名称的常量(显示给用户) ) 和一个表示该常量并以谓词格式插入的值。键路径也是如此:如果我可以有一个显示名称,然后有一个值,即谓词的 var 字符串($VAR 或其他任何东西),那么所有问题都将得到解决。如果可能的话,请告诉我如何做。如果不可能,那么请关注我描述的其他问题。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
所以主要问题是:
答:是的。您过去可以在 Interface Builder 中进行设置,但我在 Xcode 4 中尝试这样做并不成功。该功能可能已在 IB => 中删除。 Xcode 4 合并。
幸运的是,您可以用代码来完成。不幸的是,它需要一个 NSPredicateEditorRowTemplate 子类。我还没有找到没有这样的子类的方法。
要点是覆盖行模板中的
-templateViews
,并执行如下操作:瞧!谓词编辑器中的弹出窗口,其中标题与键路径不匹配。
编辑
解决此问题的另一种方法(无需子类化!)是将自定义文本放入 .strings 文件中,并将编辑器“本地化”为英语(或您想要的任何语言)。
NSPredicateEditor
NSPredicateEditor
的后续工作如果您要在谓词中表达这些比较,您可能不会使用
!=
和==
。喜欢:还有一个of:
为此,我能想到的方法是使用两个单独的行模板,以第一种格式识别并显示谓词(
MATCHES '[0-9]'
)。 ),第二个将识别并显示否定,您开始进入 NSPredicateEditorRowTemplates 的奇怪领域,我认为我仍然不确定您想要完成什么。为什么你想将字符串的每个字符与谓词进行匹配,但无论如何,有关如何创建自定义行模板的更多信息,请查看这两篇博客文章:
NSPredicateEditorRowTemplate
NSPredicateEditorRowTemplate
So the main question:
Answer: yes. You used to be able to set this up in Interface Builder, but my attempts to do so in Xcode 4 have been unsuccessful. It's possible that the capability was removed in the IB => Xcode 4 merge.
Fortunately, you can do it in code. Unfortunately, it requires an
NSPredicateEditorRowTemplate
subclass. I haven't found a way to do it without such a subclass.The gist is to override
-templateViews
in your row template, and do something like this:And voilá! A popup in your predicate editor where the title does not match the keypath.
edit
The other way to solve this (without subclassing!) would be to put your custom text in a .strings file and "localize" your editor to English (or whatever language you want).
NSPredicateEditor
NSPredicateEditor
If you were to express these comparisons in a predicate, you probably wouldn't use
!=
and==
. Likely, you'd want to do something like:And one of:
For this, the way that I can think of to do this would be to have two separate row templates. One would recognize and display predicates in the first format (
MATCHES '[0-9]'
), and the second would recognize and display the negation. You're starting to get into the weird realm ofNSPredicateEditorRowTemplates
, I think. I'm still not really sure what you're trying to accomplish and why you want to be matching every character of a string against a predicate, but whatever.For more information on how to create custom row templates, check out these two blog posts:
NSPredicateEditorRowTemplate
NSPredicateEditorRowTemplate
为什么设置视觉文本需要子类? 会不会更便宜,如下所示:
简单地在类中创建一个方法(例如:然后在 setRowTemplates: 之前调用它)
Why is the subclass necessary for setting the visual text? Wouldn't it be less expensive to simply create a method in your class like:
And then call it before setRowTemplates: like so: