У меня есть требование , которое является относительно неясным, но он чувствует , как это должно быть возможно с помощью BCL.
Для контекста я анализирую строку даты / времени в Noda Time . Я поддерживаю логический курсор для моей позиции во входной строке. Таким образом, хотя полная строка может быть «3 января 2013 года», логический курсор может быть на «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))
но:
- Для этого мне нужно создать подстроку, чего я бы предпочел избежать. (Я рассматриваю Noda Time как эффективную системную библиотеку; производительность анализа может быть важна для некоторых клиентов.)
- Он не говорит мне, как далеко продвинуть курсор после этого
На самом деле, я сильно подозреваю, что это будет происходить не очень часто ... но мне бы очень хотелось поступить правильно. Я также очень хотел бы иметь возможность делать это, не становясь экспертом по Unicode и не внедряя его самостоятельно :)
(Поднят как ошибка 210 в Noda Time, на случай, если кто-то захочет следовать какому-либо окончательному выводу.)
Мне нравится идея нормализации. Мне нужно подробно проверить это на предмет а) правильности и б) производительности. Предполагая, что я смогу заставить его работать правильно, я все еще не уверен, стоит ли вообще его менять - это такие вещи, которые, вероятно, никогда не появятся в реальной жизни, но могут повредить производительности всех моих пользователей: (
Я также проверил 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 12 2013 20:28:42» на моем портативном компьютере для разработки он изменяет количество операций синтаксического анализа, которые я могу выполнить за секунду, с примерно 460K до примерно 400K. Я бы предпочел по возможности избежать этого замедления, но это не так уж плохо.Нормализация менее осуществима, чем я думал, потому что она недоступна в переносимых библиотеках классов. Я потенциально мог бы использовать его только для сборок без PCL, что позволяет сборкам PCL быть немного менее правильными. Тестирование на normalization (
string.IsNormalized
) снижает производительность примерно до 445 тыс. Вызовов в секунду, с чем я могу жить. Я до сих пор не уверен, что он делает все, что мне нужно - например, название месяца, содержащее "ß", должно соответствовать "ss" во многих культурах, я считаю ... и нормализация этого не делает.
text
не слишком длинный, вы можете сделать это if (compareInfo.IndexOf(text, candidate, position, options) == position)
. msdn.microsoft.com/en-us/library/ms143031.aspx Но если text
это очень долго, это потратит много времени на поиск за пределами того места, где это необходимо.
String
класса вообще в данном случае и использовать Char[]
непосредственно. Вы в конечном итоге напишете больше кода, но это то, что происходит, когда вам нужна высокая производительность ... или, может быть, вам следует программировать на C ++ / CLI ;-)