У меня есть строка с несколькими предложениями. Как я Использую для своей выгоды первую букву первого слова в каждом предложении. Что-то как форматирование абзаца в слове.
например, "это - некоторый код. код находится в C#". ouput должен быть, "Это - некоторый код. Код находится в C#".
один путь состоял бы в том, чтобы разделить строку на основе'.' и затем использовать для своей выгоды первую букву и затем возразить.
Существует ли лучшее решение?
На мой взгляд, когда речь идет о потенциально сложных правилах, основанных на строке и замене и замене - вы не можете получить намного лучше, чем решение на основе REGEOX (несмотря на то, что им так трудно читать!). Это предлагает лучшую эффективность и эффективность производительности и памяти, на мой взгляд, вы будете удивлены, насколько быстро это будет.
Я бы использовал перегрузку Regex.replace, которая принимает входную строку, шаблон Regex и делегат MatchEvaluation . MatchEvaluator - это функция, которая принимает Match
объект в качестве ввода и возвращает замену строки.
Вот код:
public static string Capitalise(string input)
{
//now the first character
return Regex.Replace(input, @"(?<=(^|[.;:])\s*)[a-z]",
(match) => { return match.Value.ToUpper(); });
}
REGEX использует конструкцию («? <=)» (Положительный вид с нулевой шириной), чтобы ограничить фиксацию только к символам A-Z, предшествующие запуск строки, либо знаки препинания, которые вы хотите. В [.;:]
BIT Вы можете добавить дополнительные, которые вы хотите (например, [.;:? ».«]
, чтобы добавить? И «символы».
это означает , Также, что ваш MatchEvaluator не должен делать какие-либо ненужные соединения строки (которые вы хотите избежать по причинам эффективности).
Все остальные вещи, упомянутые одним из других ответов на использование RegexOptions.ru. С точки зрения производительности. Статический метод Regex.replace предлагает очень похожими преимуществами производительности, хотя (есть просто дополнительный поиск по словарю).
Как я говорю - я буду удивлен, если кто-то из другого не регенда Решения здесь будут работать лучше и будут как быстрые.
Редактировать
поставил это решение против Ахмада, как он совершенно справедливо отметил, что внешний вид может быть менее эффективным, чем его путь.
Вот Грубый тест, который я сделал:
public string LowerCaseLipsum
{
get
{
//went to lipsum.com and generated 10 paragraphs of lipsum
//which I then initialised into the backing field with @"[lipsumtext]".ToLower()
return _lowerCaseLipsum;
}
}
[TestMethod]
public void CapitaliseAhmadsWay()
{
List<string> results = new List<string>();
DateTime start = DateTime.Now;
Regex r = new Regex(@"(^|\p{P}\s+)(\w+)", RegexOptions.Compiled);
for (int f = 0; f < 1000; f++)
{
results.Add(r.Replace(LowerCaseLipsum, m => m.Groups[1].Value
+ m.Groups[2].Value.Substring(0, 1).ToUpper()
+ m.Groups[2].Value.Substring(1)));
}
TimeSpan duration = DateTime.Now - start;
Console.WriteLine("Operation took {0} seconds", duration.TotalSeconds);
}
[TestMethod]
public void CapitaliseLookAroundWay()
{
List<string> results = new List<string>();
DateTime start = DateTime.Now;
Regex r = new Regex(@"(?<=(^|[.;:])\s*)[a-z]", RegexOptions.Compiled);
for (int f = 0; f < 1000; f++)
{
results.Add(r.Replace(LowerCaseLipsum, m => m.Value.ToUpper()));
}
TimeSpan duration = DateTime.Now - start;
Console.WriteLine("Operation took {0} seconds", duration.TotalSeconds);
}
в релизате, мое решение было около на 12% быстрее, чем у Ахмада (1,48 секунды в отличие от 1,68 секунды).
Интересно, однако, если это было сделано через статический метод Regex.replace, оба были более 80% медленнее, и мое решение было медленнее, чем Ахмада.
У вас есть несколько разных вариантов:
IENumerable
с первой буквой после периода в верхнем регистре. Может предложить преимущество потокового решения. Ниже приведен код ITERATOR:
public static string ToSentenceCase( string someString )
{
var sb = new StringBuilder( someString.Length );
bool wasPeriodLastSeen = true; // We want first letter to be capitalized
foreach( var c in someString )
{
if( wasPeriodLastSeen && !c.IsWhiteSpace )
{
sb.Append( c.ToUpper() );
wasPeriodLastSeen = false;
}
else
{
if( c == '.' ) // you may want to expand this to other punctuation
wasPeriodLastSeen = true;
sb.Append( c );
}
}
return sb.ToString();
}
Я не знаю почему, но я решил дать возвращение доходности . Просто для удовольствия.
static IEnumerable<char> CapitalLetters(string sentence)
{
//capitalize first letter
bool capitalize = true;
char lastLetter;
for (int i = 0; i < sentence.Length; i++)
{
lastLetter = sentence[i];
yield return (capitalize) ? Char.ToUpper(sentence[i]) : sentence[i];
if (Char.IsWhiteSpace(lastLetter) && capitalize == true)
continue;
capitalize = false;
if (lastLetter == '.' || lastLetter == '!') //etc
capitalize = true;
}
}
использовать его:
string sentence = new String(CapitalLetters("this is some code. the code is in C#.").ToArray());
Вот решение для регулярных выражений, которое использует категорию пунктуации, чтобы не указывать.!? "И т. Д., Хотя вы обязательно должны проверить, соответствует ли она вашим потребностям, или задать их явно. Прочтите категорию" P "в разделе" Поддерживается " Общие категории Unicode », расположенный на странице Классы символов MSDN .
string input = @"this is some code. the code is in C#? it's great! In ""quotes."" after quotes.";
string pattern = @"(^|\p{P}\s+)(\w+)";
// compiled for performance (might want to benchmark it for your loop)
Regex rx = new Regex(pattern, RegexOptions.Compiled);
string result = rx.Replace(input, m => m.Groups[1].Value
+ m.Groups[2].Value.Substring(0, 1).ToUpper()
+ m.Groups[2].Value.Substring(1));
Если вы решите не использовать класс \ p {P}
, вам придется указать символы самостоятельно, аналогично:
string pattern = @"(^|[.?!""]\s+)(\w+)";
РЕДАКТИРОВАТЬ: ниже - обновленный пример, демонстрирующий 3 шаблона. Первый показывает, как все знаки препинания влияют на регистр. Во втором показано, как выбрать и выбрать определенные категории знаков препинания с помощью вычитания классов. В нем используются все знаки пунктуации. при удалении определенных групп пунктуации. Третий вариант аналогичен второму, но использует другие группы.
Ссылка MSDN не объясняет, к чему относятся некоторые из категорий пунктуации, поэтому вот разбивка:
_
-
(
[
{
)
]
}
,
, :
, ;
, \
, /
Тщательно сравните, как эти группы влияют на результаты. Это должно предоставить вам большую гибкость. Если это нежелательно, вы можете использовать определенные символы в классе символов, как показано ранее.
string input = @"foo ( parens ) bar { braces } foo [ brackets ] bar. single ' quote & "" double "" quote.
dash - test. Connector _ test. Comma, test. Semicolon; test. Colon: test. Slash / test. Slash \ test.";
string[] patterns = {
@"(^|\p{P}\s+)(\w+)", // all punctuation chars
@"(^|[\p{P}-[\p{Pc}\p{Pd}\p{Ps}\p{Pe}]]\s+)(\w+)", // all punctuation chars except Pc/Pd/Ps/Pe
@"(^|[\p{P}-[\p{Po}]]\s+)(\w+)" // all punctuation chars except Po
};
// compiled for performance (might want to benchmark it for your loop)
foreach (string pattern in patterns)
{
Console.WriteLine("*** Current pattern: {0}", pattern);
string result = Regex.Replace(input, pattern,
m => m.Groups[1].Value
+ m.Groups[2].Value.Substring(0, 1).ToUpper()
+ m.Groups[2].Value.Substring(1));
Console.WriteLine(result);
Console.WriteLine();
}
Обратите внимание, что «тире» не пишется с заглавной буквы в последнем шаблоне, а находится на новой строке. Один из способов сделать его капитализированным - использовать параметр RegexOptions.Multiline
. Попробуйте использовать приведенный выше фрагмент, чтобы узнать, соответствует ли он желаемому результату.
Кроме того, в качестве примера я не использовал RegexOptions.Compiled в приведенном выше цикле. Чтобы использовать оба параметра ИЛИ их вместе: RegexOptions.Compiled | RegexOptions.Multiline
.