我有一个需求,这是相对模糊的,但它感觉像 应该是可能的使用 BCL。
对于上下文,我正在解析 野田时光中的日期/时间字符串。我在输入字符串中为我的位置维护一个逻辑光标。因此,尽管完整的字符串可能是“2013年1月3日”,但逻辑光标可能在“ J”处。
现在,我需要解析月份名称,将其与文化中所有已知的月份名称进行比较:
执行此操作的 当前代码通常使用 CompareInfo.Compare
。实际上就像这样(只针对匹配部分——实际中有更多的代码,但与匹配无关) :
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))
但是:
事实上,我强烈怀疑这不会经常出现... ... 但我真的 喜欢在这里做正确的事情。我也真的希望能够在不成为 Unicode 专家或自己实现它的情况下完成它:)
(如果有人想要得出任何最终结论的话,在《野田时代》(Noda Time)中被称为 窃听器210。)
我喜欢正常化的想法。我需要详细检查一下,看看是否正确,还是性能良好。假设我的 可以能正确地工作,我仍然不确定它是否值得改变——这种事情可能会在现实生活中出现,但可能会损害我所有用户的性能:
我还检查了 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
的成本很高,但并不可怕。在我的开发笔记本电脑上的“ Friday April 12201320:28:42”示例中,它将我在一秒钟内可以执行的解析操作的数量从大约460K 改为大约400K。如果可能的话,我宁愿避免这种减速,但它不是 也是坏。
规范化比我想象的更不可行——因为它在便携式类库中不可用。我可以潜在地将它 只是用于非 PCL 构建,允许 PCL 构建稍微不那么正确。正常化测试(string.IsNormalized
)的性能损失使性能降低到每秒约445K 次调用,这是我可以接受的。我仍然不确定它是否做了我需要它做的所有事情——例如,在许多文化中,包含“ ß”的月份名称应该与“ ss”相匹配,我相信... 而正常化并不能做到这一点。