Отдельная строка заголовка без пробелов в словах

Я хочу найти и отделить слова в названии, в котором нет пробелов.

До:

ThisIsAnExampleTitleHELLO-WORLD2019T.EST(Test) "Тест" 'Test' [Тест]

После:

Это пример названия HELLO-WORLD 2019 TEST (Тест) [Тест] "Тест" "Тест"


Я ищу правило регулярного выражения, которое может сделать следующее.

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

Но также сохраните все заглавные слова, чтобы не ALLUPPERCASE их в ALLUPPERCASE.

Дополнительные правила:

  • Пробел, если он касается числа: Hello2019World Hello 2019 World
  • Игнорировать интервал инициалов, которые содержат, дефисы, или подчеркивает TEST
  • Игнорировать интервал, если между скобками, скобками или кавычками [Test] (Test) "Test" 'Test'
  • Сохранить дефис Hello-World

С#

https://rextester.com/GAZJS38767

// Title without spaces
string title = "ThisIsAnExampleTitleHELLO-WORLD2019T.E.S.T.(Test)[Test]\"Test\"'Test'";

// Detect where to space words
string[] split =  Regex.Split(title, "(?<!^)(?=(?<![.\\-'\"([{])[A-Z][\\d+]?)");

// Trim each word of extra spaces before joining
split = (from e in split
         select e.Trim()).ToArray();

// Join into new title
string newtitle = string.Join(" ", split);

// Display
Console.WriteLine(newtitle);

Регулярное выражение

У меня проблемы с пробелами перед числами, скобками, скобками и кавычками.

https://regex101.com/r/9IIYGX/1

(?<!^)(?=(?<![.\-'"([{])(?<![A-Z])[A-Z][\d+?]?)

(?<!^)          // Negative look behind

(?=             // Positive look ahead

(?<![.\-'"([{]) // Ignore if starts with punctuation
(?<![A-Z])      // Ignore if starts with double Uppercase letter
[A-Z]           // Space after each Uppercase letter
[\d+]?          // Space after number

)

Решение

Спасибо за все ваши совлокальные усилия в ответах. Вот пример Regex. Я применяю это к именам файлов и исключаю специальные символы \/:*?"<>|.

https://rextester.com/FYEVE73725

https://regex101.com/r/xi8L4z/1

Ответ 1

Первые несколько частей похожи на @revo answer: (?<!^|[AZ\p{P}])[AZ]|(?<=\p{P})\p{P}, дополнительно добавляю следующее регулярное выражение между числом и буквой: (?<=[az])(?=\d)|(?<=\d)(?=[az])|(?<=[AZ])(?=\d)|(?<=\d)(?=[AZ]) и для обнаружения OTPIsADevice затем замените на предвкушение и просмотр сзади, чтобы найти заглавные буквы строчными: (((?<!^)[AZ](?=[az]))|((?<=[az])[AZ]))

Обратите внимание, что | is или оператор, который позволил выполнить все регулярные выражения.

Регулярное выражение: (?<!^|[AZ\p{P}])[AZ]|(?<=\p{P})\p{P}|(?<=[az])(?=\d)|(?<=\d)(?=[az])|(?<=[AZ])(?=\d)|(?<=\d)(?=[AZ])|(((?<!^)[AZ](?=[az]))|((?<=[az])[AZ]))

демонстрация

Обновить

Импровизированный немного:

От: (?<!^|[AZ\p{P}])[AZ]|(?<=\p{P})\p{P}|(?<=[az])(?=\d)|(?<=\d)(?=[az])|(?<=[AZ])(?=\d)|(?<=\d)(?=[AZ])

в: (?<!^|[AZ\p{P}])[AZ]|(?<=\p{P})\p{P}|(?<=\p{L})\d которые сделать то же самое.

(((?<!^)(?<!\p{P})[AZ](?=[az]))|((?<=[az])[AZ]))|(?<!^)(?=[[({&])|(?<=[)\]}!&}]) импровизированный из комментария OP, который добавляет исключение к некоторой пунктуации: (((?<!^)(?<!['([{])[AZ](?=[az]))|((?<=[az])[AZ]))|(?<!^)(?=[[({&])|(?<=[)\\]}!&}])

Конечное регулярное выражение: (?<!^|[AZ\p{P}])[AZ]|(?<=\p{P})\p{P}|(?<=\p{L})\d|(((?<!^)(?<!\p{P})[AZ](?=[az]))|((?<=[az])[AZ]))|(?<!^)(?=[[({&])|(?<=[)\]}!&}])

демонстрация

Ответ 2

Вот регулярное выражение, которое, кажется, работает хорошо, по крайней мере, для вашего примера ввода:

(?<=[a-z])(?=[A-Z])|(?<=[0-9])(?=[A-Za-z])|(?<=[A-Za-z])(?=[0-9])|(?<=\W)(?=\W)

Этот паттен говорит сделать раскол на границе одного из следующих условий:

  • то, что предшествует строчная буква, а то, что предшествует строчная буква (или наоборот)
  • что предшествует цифре, а что следует букве (или наоборот)
  • то, что предшествует, а что следует, - это не слово


string title = "ThisIsAnExampleTitleHELLO-WORLD2019T.E.S.T.(Test)[Test]\"Test\"'Test'";
string[] split =  Regex.Split(title, "(?<=[a-z])(?=[A-Z])|(?<=[0-9])(?=[A-Za-z])|(?<=[A-Za-z])(?=[0-9])|(?<=\\W)(?=\\W)"); 
split = (from e in split select e.Trim()).ToArray();
string newtitle = string.Join(" ", split);

This Is An Example Title HELLO-WORLD 2019 T.E.S.T. (Test) [Test] "Test" 'Test'

Примечание: Вы также можете добавить это утверждение в чередование регулярных выражений:

(?<=\W)(?=\w)|(?<=\w)(?=\W)

Нам здесь это сошло с рук, потому что такого граничного условия никогда не было. Но вам может понадобиться это с другими входами.

Ответ 3

Стремясь к простоте, а не к огромному регулярному выражению, я бы порекомендовал этот код с небольшими простыми шаблонами (комментарии с пояснениями находятся в коде):

string str = "ThisIsAnExampleTitleHELLO-WORLD2019T.E.S.T.(Test)\"Test\"'Test'[Test]";
// insert space when there is small letter followed by upercase letter
str = Regex.Replace(str, "(?<=[a-z])(?=[A-Z])", " ");
// insert space whenever there digit followed by a ltter
str = Regex.Replace(str, @"(?<=\d)(?=[A-Za-z])", " ");
// insert space when there letter followed by digit
str = Regex.Replace(str, @"(?<=[A-Za-z])(?=\d)", " ");
// insert space when there one of characters ("'[ followed by letter or digit
str = Regex.Replace(str, @"(?=[(\[""'][a-zA-Z0-9])", " ");
// insert space when what preceeds is on of characters ])"'
str = Regex.Replace(str, @"(?<=[)\]""'])", " ");

Ответ 4

Вы можете уменьшить требования к сокращению шагов регулярного выражения, используя другую их интерпретацию. Например, первое требование будет таким же, как сказать, сохранить заглавные буквы, если им не предшествуют знаки препинания или заглавные буквы.

Следующее регулярное выражение работает почти для всех упомянутых требований и может быть расширено, чтобы включить или исключить другие ситуации:

(?<!^|[A-Z\p{P}])[A-Z]|(?<=\p{P})\p{P}

Вы должны использовать метод Replace() и использовать $0 качестве строки подстановки.

Смотрите демо здесь

.NET (увидеть это в действии):

string input = @"ThisIsAnExample.TitleHELLO-WORLD2019T.E.S.T.(Test)""Test""'Test'[Test]";
Regex regex = new Regex(@"(?<!^|[A-Z\p{P}])[A-Z]|(?<=\p{P})\p{P}", RegexOptions.Multiline);
Console.WriteLine(regex.Replace(input, @" $0"));