【发布时间】:2013-04-05 12:13:09
【问题描述】:
我有一个相对模糊的要求,但感觉它应该可以使用 BCL。
对于上下文,我正在解析Noda Time 中的日期/时间字符串。我在输入字符串中为我的位置维护一个逻辑光标。因此,虽然完整的字符串可能是“2013 年 1 月 3 日”,但逻辑光标可能位于“J”。
现在,我需要解析月份名称,将其与文化的所有已知月份名称进行比较:
- 文化敏感
- 不区分大小写
- 就从光标点开始(不是以后;我想看看光标是否在“查看”候选月份名称)
- 快速
- ...之后我需要知道使用了多少个字符
使用CompareInfo.Compare 通常可以使用current code。它实际上是这样的(仅用于匹配部分 - 实物中有更多代码,但与匹配无关):
internal bool MatchCaseInsensitive(string candidate, CompareInfo compareInfo)
{
return compareInfo.Compare(text, position, candidate.Length,
candidate, 0, candidate.Length,
CompareOptions.IgnoreCase) == 0;
}
但是,这取决于候选对象和我们比较的区域长度相同。大多数时候都很好,但在某些特殊情况下不很好。假设我们有类似的东西:
// U+00E9 is a single code point for e-acute
var text = "x b\u00e9d y";
int position = 2;
// e followed by U+0301 still means e-acute, but from two code points
var candidate = "be\u0301d";
现在我的比较将失败。我可以使用IsPrefix:
if (compareInfo.IsPrefix(text.Substring(position), candidate,
CompareOptions.IgnoreCase))
但是:
- 这需要我创建一个子字符串,我真的宁愿避免这样做。 (我将 Noda Time 视为有效的系统库;解析性能对某些客户来说可能很重要。)
- 它没有告诉我之后光标前进了多远
实际上,我强烈怀疑这不会经常出现......但我真的喜欢在这里做正确的事情。我也非常希望能够在不成为 Unicode 专家或自己实现它的情况下做到这一点:)
(在野田时间以bug 210 提出,以防有人想遵循任何最终结论。)
我喜欢标准化的想法。我需要详细检查 a) 正确性和 b) 性能。假设我可以让它正常工作,我仍然不确定是否值得全面改变 - 这种事情可能永远不会真正出现在现实生活中,但可能会损害我所有用户的性能:(
我还检查了 BCL - 它似乎也不能正确处理这个问题。示例代码:
using System;
using System.Globalization;
class Test
{
static void Main()
{
var culture = (CultureInfo) CultureInfo.InvariantCulture.Clone();
var months = culture.DateTimeFormat.AbbreviatedMonthNames;
months[10] = "be\u0301d";
culture.DateTimeFormat.AbbreviatedMonthNames = months;
var text = "25 b\u00e9d 2013";
var pattern = "dd MMM yyyy";
DateTime result;
if (DateTime.TryParseExact(text, pattern, culture,
DateTimeStyles.None, out result))
{
Console.WriteLine("Parsed! Result={0}", result);
}
else
{
Console.WriteLine("Didn't parse");
}
}
}
将自定义月份名称更改为仅带有“bEd”文本值的“bed”可以正常解析。
好的,再来几个数据点:
使用
Substring和IsPrefix的成本很高,但并不可怕。在我的开发笔记本电脑上的“2013 年 4 月 12 日星期五 20:28:42”示例中,它将我在一秒钟内可以执行的解析操作的数量从大约 460K 更改为大约 400K。如果可能的话,我宁愿避免这种减速,但这并不是太糟糕。-
规范化不像我想象的那样可行 - 因为它在可移植类库中不可用。我可能会将它只是用于非 PCL 构建,从而允许 PCL 构建不太正确。标准化测试的性能损失 (
string.IsNormalized) 将性能降低到每秒大约 445K 调用,这是我可以忍受的。我仍然不确定它是否能满足我的所有需求 - 例如,在许多文化中,包含“ß”的月份名称应该与“ss”匹配,我相信......而规范化并不能做到这一点。
【问题讨论】:
-
虽然我理解您希望避免创建子字符串对性能造成的影响,但最好这样做,但在游戏的早期,通过将所有内容转换为选择的 unicode 规范化形式 FIRST 然后了解您可以“逐点”行走。可能是 D 型。
-
@IDisposable:是的,我确实想知道这一点。显然,我可以事先规范月份名称本身。至少我可以只做一次标准化。我想知道规范化程序是否检查是否需要先做任何事情。我在规范化方面没有太多经验 - 绝对是一个值得研究的途径。
-
如果您的
text不是太长,您可以使用if (compareInfo.IndexOf(text, candidate, position, options) == position)。 msdn.microsoft.com/en-us/library/ms143031.aspx 但如果text很长,那将浪费大量时间搜索超出需要的位置。 -
在这种情况下,at all 直接绕过
String类,直接使用Char[]。您最终会编写更多代码,但这就是您想要高性能时发生的事情......或者您应该使用 C++/CLI 进行编程;-) -
CompareOptions.IgnoreNonSpace 不会自动为您处理这个问题吗?在我看来(来自 docco,抱歉,无法通过此 iPad 进行测试!)好像这可能是该选项的 (the?) 用例。 "表示字符串比较必须忽略非空格组合字符,例如变音符号。"