На данный момент это лучшее решение, но вам нужно использовать \\ B для компиляции. В противном случае компилятор пытается обработать \ B как escape-последовательность.
Ферруччо,
Хорошее решение. Может ли кто-нибудь придумать причину, по которой это не должно быть общепринятым ответом? Он менее эффективен или менее эффективен?
Дрю Ноукс
8
Этот трактует последовательные заглавные буквы как отдельные слова (например, ANZAC - это 5 слов), тогда как ответ MizardX рассматривает его (правильно IMHO) как одно слово.
Ray
2
@Ray, я бы сказал, что «ANZAC» следует писать как «Anzac», чтобы его можно было считать словом паскальского падежа, поскольку это не английский регистр.
Sam
1
@Neaox, по-английски это должно быть, но это не аббревиатура или нормальный английский регистр; он разделен заглавными буквами. Если исходный текст должен быть написан с заглавной буквы так же, как и в обычном английском, то другие буквы также не должны быть заглавными. Например, почему «i» в «is» должно быть написано с заглавной буквы, чтобы соответствовать формату с разделителями заглавными буквами, а не «NZAC» в «ANZAC»? Строго говоря, если вы интерпретируете «АНЗАК» как разделенное заглавными буквами, то это 5 слов, по одному на каждую букву.
Сэм
19
Отличный ответ, MizardX! Я немного изменил его, чтобы рассматривать цифры как отдельные слова, чтобы «AddressLine1» превратился в «Address Line 1» вместо «Address Line1»:
Отличное дополнение! Я подозреваю, что немало людей будут удивлены принятым ответом на обработку чисел в строках. :)
Jordan Gray
Я знаю, что прошло почти 8 лет с тех пор, как вы опубликовали это, но и у меня это сработало отлично. :) Цифры меня сначала сбили с толку.
Майкл Армс,
Единственный ответ, который проходит мои 2 теста на выбросы: «Take5» -> «Take 5», «PublisherID» -> «Publisher ID». Я хочу проголосовать за это дважды
PandaWood
18
Просто для небольшого разнообразия ... Вот метод расширения, который не использует регулярное выражение.
publicstaticclassCamelSpaceExtensions{publicstaticstringSpaceCamelCase(thisString input){returnnewstring(Enumerable.Concat(
input.Take(1),// No space before initial capInsertSpacesBeforeCaps(input.Skip(1))).ToArray());}privatestaticIEnumerable<char>InsertSpacesBeforeCaps(IEnumerable<char> input){foreach(char c in input){if(char.IsUpper(c)){yieldreturn' ';}yieldreturn c;}}}
Чтобы избежать использования Trim (), перед foreach я поставил: int counter = -1. внутри добавьте счетчик ++. измените проверку на: if (char.IsUpper (c) && counter> 0)
Outside the Box Developer
Это вставляет пробел перед первым символом.
Зар
Я взял на себя смелость исправить проблему, указанную @ZarShardan. Если вам не нравится изменение, откатитесь назад или измените свое исправление.
jpmc26
Можно ли это улучшить для обработки сокращений, например, добавив пробел перед последним заглавным регистром в серии заглавных букв, например, BOEForecast => BOE Forecast
Непалуз
11
Замечательный комментарий Гранта Вагнера в сторону:
Dim s AsString=RegularExpressions.Regex.Replace("ThisIsMyCapsDelimitedString","([A-Z])"," $1")
Хороший замечание ... Не стесняйтесь вставлять .substring (), .trimstart (), .trim (), .remove () и т. Д. По вашему выбору. :)
Псевдо мазохист
9
Мне нужно было решение, поддерживающее сокращения и числа. Это решение на основе Regex обрабатывает следующие шаблоны как отдельные «слова»:
Заглавная буква, за которой следуют строчные буквы
Последовательность последовательных чисел
Последовательные заглавные буквы (интерпретируются как аббревиатуры) - новое слово может начинаться с последней заглавной буквы, например, HTMLGuide => «HTML Guide», «TheATeam» => «The A Team»
usingSystem.Text.RegularExpressions;namespaceDemo{publicclassIntercappedStringHelper{privatestaticreadonlyRegexSeparatorRegex;staticIntercappedStringHelper(){conststring pattern =@"
(?<!^) # Not start
(
# Digit, not preceded by another digit
(?<!\d)\d
|
# Upper-case letter, followed by lower-case letter if
# preceded by another upper-case letter, e.g. 'G' in HTMLGuide
(?(?<=[A-Z])[A-Z](?=[a-z])|[A-Z])
)";var options =RegexOptions.IgnorePatternWhitespace|RegexOptions.Compiled;SeparatorRegex=newRegex(pattern, options);}publicstaticstringSeparateWords(stringvalue,string separator =" "){returnSeparatorRegex.Replace(value, separator +"$1");}}}
+1 за объяснение регулярного выражения и его удобочитаемость. И я узнал кое-что новое. В .NET Regex есть режим свободного интервала и комментариев. Спасибо!
Феликс Кейл
4
Для большего разнообразия при использовании простых старых объектов C # следующий результат дает тот же результат, что и отличное регулярное выражение @ MizardX.
publicstringFromCamelCase(string camel){// omitted checking camel for nullStringBuilder sb =newStringBuilder();int upperCaseRun =0;foreach(char c in camel){// append a space only if we're not at the start// and we're not already in an all caps string.if(char.IsUpper(c)){if(upperCaseRun ==0&& sb.Length!=0){
sb.Append(' ');}
upperCaseRun++;}elseif(char.IsLower(c)){if(upperCaseRun >1)//The first new word will also be capitalized.{
sb.Insert(sb.Length-1,' ');}
upperCaseRun =0;}else{
upperCaseRun =0;}
sb.Append(c);}return sb.ToString();}
Я знал, что будет простой способ RegEx ... Мне нужно больше его использовать.
Макс Шмелинг,
1
Не гуру регулярных выражений, но что происходит с "HeresAWTFString"?
Ник
1
Вы получаете «Heres AWTF String», но это именно то, о чем просил Матиас Нино в своем вопросе.
Макс Шмелинг,
Да, ему нужно добавить, что «несколько соседних столиц не трогают». Что довольно очевидно требуется во многих случаях, например, "PublisherID" здесь идет к "Publisher I D", что ужасно
PandaWood
2
Regex примерно в 10-12 раз медленнее простого цикла:
publicstaticstringCamelCaseToSpaceSeparated(thisstring str){if(string.IsNullOrEmpty(str)){return str;}var res =newStringBuilder();
res.Append(str[0]);for(var i =1; i < str.Length; i++){if(char.IsUpper(str[i])){
res.Append(' ');}
res.Append(str[i]);}return res.ToString();}
Я модифицировал вас, но люди обычно лучше переносят удар, если он не начинается с «наивного».
MusiGenesis
Не думаю, что это был удар. В этом контексте «наивный» обычно означает очевидное или простое (то есть не обязательно лучшее решение). Нет намерения оскорбить.
Ферруччо,
0
Вероятно, есть более элегантное решение, но вот что я придумал сразу же:
string myString ="ThisIsMyCapsDelimitedString";for(int i =1; i < myString.Length; i++){if(myString[i].ToString().ToUpper()== myString[i].ToString()){
myString = myString.Insert(i," ");
i++;}}
privatestaticStringBuilder camelCaseToRegular(string i_String){StringBuilder output =newStringBuilder();int i =0;foreach(char character in i_String){if(character <='Z'&& character >='A'&& i >0){
output.Append(" ");}
output.Append(character);
i++;}return output;}
/// <summary>/// Get the words in a code <paramref name="identifier"/>./// </summary>/// <param name="identifier">The code <paramref name="identifier"/></param> to extract words from.publicstaticstring[]GetWords(thisstring identifier){Contract.Ensures(Contract.Result<string[]>()!=null,"returned array of string is not null but can be empty");if(identifier ==null){returnnewstring[0];}if(identifier.Length==0){returnnewstring[0];}constint MIN_WORD_LENGTH =2;// Ignore one letter or one digit wordsvar length = identifier.Length;var list =newList<string>(1+ length/2);// Set capacity, not possible more words since we discard one char wordsvar sb =newStringBuilder();CharKind cKindCurrent =GetCharKind(identifier[0]);// length is not zero hereCharKind cKindNext = length ==1?CharKind.End:GetCharKind(identifier[1]);for(var i =0; i < length; i++){var c = identifier[i];CharKind cKindNextNext =(i >= length -2)?CharKind.End:GetCharKind(identifier[i +2]);// Process cKindCurrentswitch(cKindCurrent){caseCharKind.Digit:caseCharKind.LowerCaseLetter:
sb.Append(c);// Append digit or lowerCaseLetter to sbif(cKindNext ==CharKind.UpperCaseLetter){goto TURN_SB_INTO_WORD;// Finish word if next char is upper}goto CHAR_PROCESSED;caseCharKind.Other:goto TURN_SB_INTO_WORD;default:// charCurrent is never Start or EndDebug.Assert(cKindCurrent ==CharKind.UpperCaseLetter);break;}// Here cKindCurrent is UpperCaseLetter// Append UpperCaseLetter to sb anyway
sb.Append(c);switch(cKindNext){default:goto CHAR_PROCESSED;caseCharKind.UpperCaseLetter:// "SimpleHTTPServer" when we are at 'P' we need to see that NextNext is 'e' to get the word!if(cKindNextNext ==CharKind.LowerCaseLetter){goto TURN_SB_INTO_WORD;}goto CHAR_PROCESSED;caseCharKind.End:caseCharKind.Other:break;// goto TURN_SB_INTO_WORD;}//------------------------------------------------
TURN_SB_INTO_WORD:string word = sb.ToString();
sb.Length=0;if(word.Length>= MIN_WORD_LENGTH){
list.Add(word);}
CHAR_PROCESSED:// Shift left for next iteration!
cKindCurrent = cKindNext;
cKindNext = cKindNextNext;}string lastWord = sb.ToString();if(lastWord.Length>= MIN_WORD_LENGTH){
list.Add(lastWord);}return list.ToArray();}privatestaticCharKindGetCharKind(char c){if(char.IsDigit(c)){returnCharKind.Digit;}if(char.IsLetter(c)){if(char.IsUpper(c)){returnCharKind.UpperCaseLetter;}Debug.Assert(char.IsLower(c));returnCharKind.LowerCaseLetter;}returnCharKind.Other;}enumCharKind{End,// For end of stringDigit,UpperCaseLetter,LowerCaseLetter,Other}
Тесты:
[TestCase((string)null,"")][TestCase("","")]// Ignore one letter or one digit words[TestCase("A","")][TestCase("4","")][TestCase("_","")][TestCase("Word_m_Field","Word Field")][TestCase("Word_4_Field","Word Field")][TestCase("a4","a4")][TestCase("ABC","ABC")][TestCase("abc","abc")][TestCase("AbCd","Ab Cd")][TestCase("AbcCde","Abc Cde")][TestCase("ABCCde","ABC Cde")][TestCase("Abc42Cde","Abc42 Cde")][TestCase("Abc42cde","Abc42cde")][TestCase("ABC42Cde","ABC42 Cde")][TestCase("42ABC","42 ABC")][TestCase("42abc","42abc")][TestCase("abc_cde","abc cde")][TestCase("Abc_Cde","Abc Cde")][TestCase("_Abc__Cde_","Abc Cde")][TestCase("ABC_CDE_FGH","ABC CDE FGH")][TestCase("ABC CDE FGH","ABC CDE FGH")]// Should not happend (white char) anything that is not a letter/digit/'_' is considered as a separator[TestCase("ABC,CDE;FGH","ABC CDE FGH")]// Should not happend (,;) anything that is not a letter/digit/'_' is considered as a separator[TestCase("abc<cde","abc cde")][TestCase("abc<>cde","abc cde")][TestCase("abc<D>cde","abc cde")]// Ignore one letter or one digit words[TestCase("abc<Da>cde","abc Da cde")][TestCase("abc<cde>","abc cde")][TestCase("SimpleHTTPServer","Simple HTTP Server")][TestCase("SimpleHTTPS2erver","Simple HTTPS2erver")][TestCase("camelCase","camel Case")][TestCase("m_Field","Field")][TestCase("mm_Field","mm Field")]publicvoidTest_GetWords(string identifier,string expectedWordsStr){var expectedWords = expectedWordsStr.Split(' ');if(identifier ==null|| identifier.Length<=1){
expectedWords =newstring[0];}var words = identifier.GetWords();Assert.IsTrue(words.SequenceEqual(expectedWords));}
Простое решение, которое должно быть на порядок (а) быстрее, чем решение с регулярным выражением (на основе тестов, которые я провел с лучшими решениями в этом потоке), особенно при увеличении размера входной строки:
string s1 ="ThisIsATestStringAbcDefGhiJklMnoPqrStuVwxYz";string s2;StringBuilder sb =newStringBuilder();foreach(char c in s1)
sb.Append(char.IsUpper(c)?" "+ c.ToString(): c.ToString());
s2 = sb.ToString();
Regex.Replace(s, "([A-Z0-9]+)", " $1").Trim()
. А если вы хотите разделить каждую заглавную букву, просто уберите плюс.Ответы:
Я сделал это некоторое время назад. Он соответствует каждому компоненту имени CamelCase.
Например:
Чтобы преобразовать это, просто вставьте пробелы между словами:
Если вам нужно обрабатывать цифры:
источник
источник
Отличный ответ, MizardX! Я немного изменил его, чтобы рассматривать цифры как отдельные слова, чтобы «AddressLine1» превратился в «Address Line 1» вместо «Address Line1»:
источник
Просто для небольшого разнообразия ... Вот метод расширения, который не использует регулярное выражение.
источник
Замечательный комментарий Гранта Вагнера в сторону:
источник
Мне нужно было решение, поддерживающее сокращения и числа. Это решение на основе Regex обрабатывает следующие шаблоны как отдельные «слова»:
Вы можете сделать это как однострочник:
Может быть лучше более читаемый подход:
Вот выдержка из тестов (XUnit):
источник
Для большего разнообразия при использовании простых старых объектов C # следующий результат дает тот же результат, что и отличное регулярное выражение @ MizardX.
источник
Ниже представлен прототип, который преобразует следующее в регистр заголовка:
Очевидно, вам понадобится только метод "ToTitleCase".
Консоль будет выглядеть следующим образом:
Ссылка на сообщение в блоге
источник
источник
Regex примерно в 10-12 раз медленнее простого цикла:
источник
Решение наивного регулярного выражения. Не обрабатывает О'Коннера, а также добавляет пробел в начале строки.
источник
Вероятно, есть более элегантное решение, но вот что я придумал сразу же:
источник
Попробуй использовать
Результат подойдет для смеси алфавита с цифрами.
источник
Реализация кода psudo из: https://stackoverflow.com/a/5796394/4279201
источник
Чтобы сопоставить категорию Unicode с непрописными буквами и заглавными буквами :
(?<=\P{Lu})(?=\p{Lu})
источник
Процедурное и быстрое выполнение:
Тесты:
источник
Простое решение, которое должно быть на порядок (а) быстрее, чем решение с регулярным выражением (на основе тестов, которые я провел с лучшими решениями в этом потоке), особенно при увеличении размера входной строки:
источник