Форматирование предложений в строке с помощью C#

У меня есть строка с несколькими предложениями. Как я Использую для своей выгоды первую букву первого слова в каждом предложении. Что-то как форматирование абзаца в слове.

например, "это - некоторый код. код находится в C#". ouput должен быть, "Это - некоторый код. Код находится в C#".

один путь состоял бы в том, чтобы разделить строку на основе'.' и затем использовать для своей выгоды первую букву и затем возразить.

Существует ли лучшее решение?

6
задан hippietrail 17 September 2014 в 06:39
поделиться

5 ответов

На мой взгляд, когда речь идет о потенциально сложных правилах, основанных на строке и замене и замене - вы не можете получить намного лучше, чем решение на основе 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% медленнее, и мое решение было медленнее, чем Ахмада.

5
ответ дан 8 December 2019 в 13:46
поделиться

У вас есть несколько разных вариантов:

  1. Ваш подход расщепления строки, капитализация, а затем повторное соединение
  2. , используя регулярные выражения для выполнения замены выражений (которые могут быть немного сложными для случая)
  3. Написать итератор C #, который итерации на каждый символ и дает новый IENumerable с первой буквой после периода в верхнем регистре. Может предложить преимущество потокового решения.
  4. Структура на каждом чарном и верхнем регистре тем, которые появляются сразу после периода (пробел, игнорируемый) - пунктБуфтер может сделать это проще.

Ниже приведен код 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();
}
4
ответ дан 8 December 2019 в 13:46
поделиться

Я не знаю почему, но я решил дать возвращение доходности . Просто для удовольствия.

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());
2
ответ дан 8 December 2019 в 13:46
поделиться
  1. Сделайте вашу работу в StringBuffer.
  2. Ниже всего в целом.
  3. петля сквозь и прописные ведущие символы.
  4. Вызов ToString.
1
ответ дан 8 December 2019 в 13:46
поделиться

Вот решение для регулярных выражений, которое использует категорию пунктуации, чтобы не указывать.!? "И т. Д., Хотя вы обязательно должны проверить, соответствует ли она вашим потребностям, или задать их явно. Прочтите категорию" 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 не объясняет, к чему относятся некоторые из категорий пунктуации, поэтому вот разбивка:

  • P : все знаки препинания (включают все категории ниже)
  • Pc : подчеркивание _
  • Pd : тире -
  • Ps : открывающие круглые скобки, квадратные скобки ( [ {
  • Pe : закрывающая скобка, скобки и фигурные скобки ) ] }
  • Pi : начальные одинарные / двойные кавычки (MSDN говорит, что «может вести себя как Ps / Pe в зависимости от использования ")
  • Pf : заключительные одинарные / двойные кавычки (применяется примечание MSDN Pi)
  • Po : другие знаки препинания, такие как запятые, двоеточия, точки с запятой и косые черты , , : , ; , \ , /

Тщательно сравните, как эти группы влияют на результаты. Это должно предоставить вам большую гибкость. Если это нежелательно, вы можете использовать определенные символы в классе символов, как показано ранее.

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 .

5
ответ дан 8 December 2019 в 13:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: