Regex. Корпус верблюда для подчеркивания. Игнорировать первое появление
Например:
thisIsMySample
должен быть:
this_Is_My_Sample
Мой код:
System.Text.RegularExpressions.Regex.Replace(input, "([A-Z])", "_$0", System.Text.RegularExpressions.RegexOptions.Compiled);
Он отлично работает, но если вход изменяется на:
ThisIsMySample
вывод будет:
_This_Is_My_Sample
Как игнорировать первое появление?
Ответы
Ответ 1
Нерегрессивное решение
string result = string.Concat(input.Select((x,i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString()));
Кажется, тоже довольно быстро: Regex: 2569ms, С#: 1489ms
Stopwatch stp = new Stopwatch();
stp.Start();
for (int i = 0; i < 1000000; i++)
{
string input = "ThisIsMySample";
string result = System.Text.RegularExpressions.Regex.Replace(input, "(?<=.)([A-Z])", "_$0",
System.Text.RegularExpressions.RegexOptions.Compiled);
}
stp.Stop();
MessageBox.Show(stp.ElapsedMilliseconds.ToString());
// Result 2569ms
Stopwatch stp2 = new Stopwatch();
stp2.Start();
for (int i = 0; i < 1000000; i++)
{
string input = "ThisIsMySample";
string result = string.Concat(input.Select((x, j) => j > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString()));
}
stp2.Stop();
MessageBox.Show(stp2.ElapsedMilliseconds.ToString());
// Result: 1489ms
Ответ 2
Вы можете использовать lookbehind, чтобы каждому соответствию предшествовал хотя бы один символ:
System.Text.RegularExpressions.Regex.Replace(input, "(?<=.)([A-Z])", "_$0",
System.Text.RegularExpressions.RegexOptions.Compiled);
lookaheads и lookbehind позволяют вам делать утверждения о тексте, окружающем совпадение, без включения этого текста в соответствие.
Ответ 3
Возможно, нравится:
var str = Regex.Replace(input, "([A-Z])", "_$0", RegexOptions.Compiled);
if(str.StartsWith("_"))
str = str.SubString(1);
Ответ 4
Разрабатывая решение sa_ddam213, моя расширяет это:
public static string GetConstStyleName(this string value)
{
return string.Concat(value.Select((x, i) =>
{
//want to avoid putting underscores between pairs of upper-cases or pairs of numbers, or adding redundant underscores if they already exist.
bool isPrevCharLower = (i == 0) ? false : char.IsLower(value[i - 1]);
bool isPrevCharNumber = (i == 0) ? false : char.IsNumber(value[i - 1]);
return (isPrevCharLower && (char.IsUpper(x) || char.IsNumber(x))) //lower-case followed by upper-case or number needs underscore
|| (isPrevCharNumber && (char.IsUpper(x))) //number followed by upper-case needs underscore
? "_" + x.ToString() : x.ToString();
})).ToUpperInvariant();
}
Ответ 5
// (Preceded by a lowercase character or digit) (a capital) => The character prefixed with an underscore
var result = Regex.Replace(input, "(?<=[a-z0-9])[A-Z]", m => "_" + m.Value);
result = result.ToLowerInvariant();
- Это работает как для
PascalCase
, так и camelCase
.
- Он не создает никаких подчеркиваний подчеркивания.
- Он оставляет в тактике любые последовательности символов, отличных от слова, и подчеркивания в строке, потому что они кажутся преднамеренными, например.
__HiThere_Guys
становится __hi_there_guys
.
- Суффиксы цифр (намеренно) считаются частью слова, например.
NewVersion3
становится new_version3
.
- Префикс для цифр следует за исходным корпусом, например.
3VersionsHere
становится 3_versions_here
, но 3rdVersion
становится 3rd_version
.
- К сожалению, заглавные двухбуквенные аббревиатуры (например, в
IDNumber
, где ID
будут считаться отдельным словом), как предложено в Соглашения Microsoft Capitalization, не поддерживаются, поскольку они противоречат другим случаям. Я рекомендую, в общем, противостоять этому руководству, поскольку это, по-видимому, произвольное исключение из конвенции о том, чтобы не использовать аббревиатуры. Придерживайтесь IDNumber
.
Ответ 6
Вам нужно изменить ваше регулярное выражение, чтобы оно не соответствовало первому char, указав, что вы вообще игнорируете первый char
.([A-Z])
Вышеупомянутое регулярное выражение просто исключает каждый char, который приходит первым, и поскольку он не находится в фигурных скобках, он будет в группе соответствия.
Теперь вам нужно сопоставить вторую группу, как отметил Бибху:
System.Text.RegularExpressions.Regex.Replace(s, "(.)([A-Z])", "$1_$2", System.Text.RegularExpressions.RegexOptions.Compiled);
Ответ 7
Используйте ".([A-Z])"
для вашего регулярного выражения, а затем "_$1"
для замены. Таким образом, вы используете захваченную строку для замены и с ведущим .
вы уверены, что не поймаете первую char вашей строки.
Ответ 8
Вы можете добиться лучшей производительности, используя следующую реализацию вместо Regex и Linq, также он использует тип Span для меньшего выделения:
public ReadOnlySpan<char> ToSnakeCaseBySpan(string name)
{
int upperCaseLength = 0;
for (int i = 0; i < name.Length; i++)
{
if (name[i] >= 'A' && name[i] <= 'Z' && name[i] != name[0])
{
upperCaseLength++;
}
}
int bufferSize = name.Length + upperCaseLength;
Span<char> buffer = new char[bufferSize];
int bufferPosition = 0;
int namePosition = 0;
while (bufferPosition < buffer.Length)
{
if (namePosition > 0 && name[namePosition] >= 'A' && name[namePosition] <= 'Z')
{
buffer[bufferPosition] = '_';
buffer[bufferPosition + 1] = name[namePosition];
bufferPosition += 2;
namePosition++;
continue;
}
buffer[bufferPosition] = name[namePosition];
bufferPosition++;
namePosition++;
}
return buffer;
}
Бенчмарки
| Method | Mean | Error | StdDev | Rank | Gen 0 | Gen 1 | Gen 2 | Allocated |
|-------------------------------- |------------:|-----------:|-----------:|-----:|-------:|------:|------:|----------:|
| ToSnakeCaseBySpan | 27.47 ns | 0.4629 ns | 0.4330 ns | 1 | 0.0153 | - | - | 48 B |
| ToSnakeCaseStringBuilderBySpan | 85.23 ns | 1.6495 ns | 1.3774 ns | 2 | 0.0637 | - | - | 200 B |
| ToSnakeCaseNewtonsoftJson | 85.72 ns | 1.6418 ns | 1.4554 ns | 2 | 0.0484 | - | - | 152 B |
| ToSnakeCaseNewtonsoftJsonBySpan | 86.96 ns | 1.7060 ns | 1.5958 ns | 2 | 0.0484 | - | - | 152 B |
| ToSnakeCaseLinq | 353.42 ns | 3.9670 ns | 3.7108 ns | 3 | 0.1450 | - | - | 456 B |
| ToSnakeCaseRegex | 2,056.69 ns | 29.5694 ns | 26.2125 ns | 4 | 0.1526 | - | - | 496 B |
Readmore