2015-07-16 5 views
2

Я обволакиваю голову вокруг этого, чтобы найти «изящное» решение, но я не совсем этому доволен.Заменить строки вхождения со списком записей

Возможные входные строки:

foo() bar()

() bar

foo

()()foo() bar

Там может быть "неограниченные" кронштейны и дополнительные, не скобка текст inbe между скобками. Содержимое этих пустых скобок должно заполняться данными, взятыми из List<string> в порядке ввода записей в список. Если нет записей или недостаточно записей, скобки не затронуты.

Возможные строковые замены:

foo() bar() заменен x, y приведет foo (x) bar (y)

foo() bar() заменен x приведет foo (x) bar()

foo() bar() заменен x, y, z приведет к foo (x) bar (y)

Надеюсь, вы поняли эту идею.

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

Я задавался вопросом, есть ли более элегантное решение с, например, регулярным выражением. Может быть, я сейчас слишком близко к задаче и есть простое решение :-)

Вот подход я не очень доволен (читаемость/легко понять):

var guiIdentifierIndex = 0; 
    var guiIdentifierList = new List<string>{"x", "y", "z", "x", "y"}; 

    var sourcePathItem = "foo()"; 
    string targetString = ""; 
    var splittedPath = sourcePathItem.Split(new string[] { BRACKETS }, StringSplitOptions.None); 
    for (int index = 0; index < splittedPath.Length; index++) 
    { 
    var subPath = splittedPath[index]; 
    var guiIdentifier = string.Empty; 
    if (guiIdentifierIndex < guiIdentifierList.Count) 
    { 
     guiIdentifier = guiIdentifierList[guiIdentifierIndex]; 
     guiIdentifierIndex++; 
    } 
    targetString += subPath; 
    if (index < splittedPath.Length - 1) 
     targetString += string.Format("({0})", guiIdentifier); 
    } 

http://volatileread.com/utilitylibrary/snippetcompiler?id=22718

ответ

1

Вы можете использовать регулярные выражения, например,

String source = "foo() bar()"; 

    var guiIdentifierList = new List<String> { 
    "x", "y", "z", "x", "y" }; 

    int guiIdentifierIndex = 0; 

    // result == "foo (x) bar (y)" 
    String result = Regex.Replace(source, @"\(\)", (MatchEvaluator) (
    (match) => "(" + (guiIdentifierIndex < guiIdentifierList.Count 
         ? guiIdentifierList[guiIdentifierIndex++] 
         : "") + ")" 
)); 
+0

Это не будет работать, если не будет достаточно идентификатор в списке (вне диапазона) – derape

+0

@derape: вы правы - я редактировал код - я не обращал внимания на * Если нет записей или не хватает записей, скобки нетронуты * - обычно, если есть два элемента, лучший выбор это исключение. –

+0

Yay =) Могут ли быть какие-либо проблемы с 'guiIdentifierIndex' в закрытии? – derape

0

Разделите заменяющую строку (x, y, z) на запятую, затем проведите через результирующий массив, заменив первое вхождение() на соответствующее значение.

Эта ссылка показывает, как заменить первый экземпляр: Replace first occurrence of pattern in a string

-1
StringBuilder sb = new StringBuilder(); 
String[] parts = sourcePathItem.Split(new String[]{"()"}, StringSplitOptions.None); 

for (Int32 i = 0; i < parts.Length; i++){ 
     sb.Append(parts[i]); 
     if (i < guiIdentifierList.Count && i + 1 < parts.Length) 
      sb.Append("(" + guiIdentifierList[i] + ")"); 
} 

var result = sb.ToString(); 
0

Как об этом:

var template = "foo() bar()"; 
var replacements = new[] {"x", "y", "z"}; 

var components = template.Split(new []{"()"}, StringSplitOptions.RemoveEmptyEntries); 
var sb = new StringBuilder(); 

var replacementCount = replacements.Count(); 

for (int i = 0; i < components.Count(); i++) 
{ 
    var value = i >= replacementCount ? String.Empty : replacements[i]; 
    sb.AppendFormat("{0}({1})", components[i], value); 
} 

var substitutedTemplate = sb.ToString(); 
0
var numbers = new List<string>(new[] { "1", "2", "3" }); 
string original = "() test()()"; 

String[] tokens = original.Split(new [] {"()"}, StringSplitOptions.None); 

if (tokens.Count() >= numbers.Count()) 
    return original; 

return string.Concat(tokens.Take(tokens.Count() - 1) 
          .Select((t, i) => t + "(" + numbers[i] + ")")); 
+0

Ограничение, введенное вами условием if, не соответствует моим требованиям. больше токенов, то числа должны поддерживаться (скобки остаются пустыми) – derape

0

Вы могли бы попытаться построить целевую строку в одной петле FOOR, как это («Copy и Substitute»)

EDIT: Теперь с «опережающего просмотра», чтобы не заменять, если закрывающая скобка ,

var guiIdentifierIndex = 0; 
var guiIdentifierList = new List<string> { "x", "y", "z" }; 

var sourcePathItem = "foo() bar() func()"; 
string targetString = ""; 
int srcIndex = 0; 

foreach(char c in sourcePathItem) 
{ 
    targetString += c; 
    char lookahead = srcIndex < sourcePathItem.Length - 1 ? sourcePathItem[++srcIndex] : ' '; 

    if (c == '(' && lookahead == ')' 
     && guiIdentifierIndex < guiIdentifierList.Count) 
    { 
     targetString += guiIdentifierList[guiIdentifierIndex++]; 
    } 
} 
Console.WriteLine("Target: " + targetString); 

Это заменяет foo() bar() с x, y к foo (x) bar (y).

Как правило, я думаю, что с помощью Stringbuilder для целевой строки было бы лучше, когда sourcePathItem и guidIdentifierList становится все больше. например меньше ресурсов использование

Привет

+0

Я не хочу 'foo (' заменяется на 'foo (x' – derape

+0

I'v сделал небольшие изменения, чтобы иметь какой-то« lookahead ». –

0

Я хотел бы сделать так:

List<string> guiIdentifiers = new List<string>{"x", "y", "z", "x", "y"}; 

string input = "foo()()()()()()"; 

string[] splitPath = Regex.Split(input, @"(\(\))"); 

for (int i = 0; i < splitPath.Length; i++) 
{ 
    if (splitPath[i] == "()" && guiIdentifiers.Count > 0) 
    { 
    splitPath[i] = string.Format("({0})", guiIdentifiers.First()); 
    guiIdentifiers.Remove(guiIdentifiers.First()); 
    } 
} 
string result = string.Join("", splitPath); 

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

+0

Мне не нужно придерживаться строкового разделения, вот почему я спросил ;-) – derape

+0

И он не использует строковое разделение, он использует Regex.Split. Разница в том, что регулярное выражение хранит разделители в массиве Или вы имеете в виду, что вы все равно не разделили строку? – 3Ducker

+0

Ну, я думал о том, чтобы не публиковать какое-либо решение вообще, чтобы не повлиять на вас, ребята, потому что я был открыт для всех видов решений, но я думаю, что это не как я должен задавать вопросы: -D – derape

0

Привет, вы можете использовать группы Regex. https://msdn.microsoft.com/en-us/library/ewy2t5e0(v=vs.110).aspx

ниже код совпадает с (и создать регулярное выражение группу, можно Использованный заменить.

var sourcePathItem = "()()foo() bar"; 
     var q = new Queue<string>(new string[]{ "x", "y", "z", "x", "y" }); 
     var replaced = Regex.Replace(sourcePathItem, @"\(", m => 

      (m.Groups[1].Value + "(" + q.Dequeue()) 
     ); 
Смежные вопросы