Создание новой строки, подобной пути, из существующей

#c# #.net #string

#c# #.net #строка

Вопрос:

Я хотел бы изменить исходную строку, которая выглядит как

 "one.two.three" 
  

и перенесите ее в строку с косыми чертами, чтобы использовать ее как строку папки, которая имеет следующую структуру:

 "oneone.twoone.two.three"
  

Знаете ли вы более элегантные способы реализовать это, чем мое решение ниже? Я не очень доволен своими циклами for.

 var folder = "one.two.three";
var folderParts = folder.Split('.');
var newFolder = new StringBuilder();
for (int i = 0; i < folderParts.Length; i  )
{
    for (int j = 0; j < i; j  )
    {
       if (j == 0)
       {
          newFolder.Append("\");
       }

       newFolder.Append($"{folderParts[j]}.");
    }

    newFolder.Append(folderParts[i]);
}
  

Комментарии:

1. Должен быть способ сделать это с помощью Aggregate, но я не могу точно указать на это.

Ответ №1:

Вы можете сделать это довольно кратко с помощью Regex

 var newFolder = Regex.Replace(folder, @".", @"$`.");
  

Это соответствует каждому периоду. Каждый раз, когда он находит точку, он вставляет обратную косую черту, а затем всю входную строку перед совпадением ( $` ). Мы должны снова добавить точку в конце.

Итак, шаги (< и > указывают текст, вставленный заменой на этом шаге):

  1. Совпадение в 1-м периоде. one<one>.two.three
  2. Совпадение во 2-м периоде. oneone.two<one.two>.three
  3. Результат: oneone.twoone.two.three

Для получения бонусных баллов используйте Path.DirectorySeparatorChar за кроссплатформенную корректность.

 var newFolder = Regex.Replace(folder, @".", $"{Path.DirectorySeparatorChar}$`.")
  

Вот еще один способ linqy:

 var a = "";
var newFolder = Path.Combine(folder.Split('.')
    .Select(x => a  = (a == "" ? "" : ".")   x).ToArray());
  

Комментарии:

1. Отлично! Просто замените, чтобы получить результат

2. Это, конечно, самое короткое решение, но его довольно сложно прочитать, если вы не любитель регулярных выражений. Спасибо за подробное объяснение! 🙂

3. Также благодарю вас за подсказку относительно кроссплатформенной совместимости.

4. @Christoph согласился. Я сказал, что это кратко, а не то, что это обязательно читаемо! Честно говоря, я бы написал комментарий против любого из этих решений, в котором говорилось бы, что оно сделало. Вы можете добавить несколько дополнительных строк комментариев к этой, объясняющих, как это работает, прежде чем она станет такой же длинной, как следующий самый краткий ответ.

Ответ №2:

Вы можете попробовать Linq:

   string folder = "one.two.three";
  string[] parts = folder.Split('.');

  string result = Path.Combine(Enumerable
    .Range(1, parts.Length)
    .Select(i => string.Join(".", parts.Take(i)))
    .ToArray());

  Console.Write(newFolder);
  

Результат:

  oneone.twoone.two.three 
  

Комментарии:

1. Возможно, использовать Path.Combine поверх этой первой string.Join для кроссплатформенности?

2. @canton7: Спасибо! Path.Combine намного лучше в контексте

Ответ №3:

Вы можете двигаться вперед — только в одном цикле, подобном этому:

     var folder = "one.two.three";
    var newFolder = new StringBuilder();

    int index = -1;
    while (index   1 < folder.Length) {
        index = folder.IndexOf('.', index   1);
        if (index < 0) {
            newFolder.Append(folder);
            break;
        }
        else {
            newFolder.Append(folder, 0, index);
            newFolder.Append(Path.DirectorySeparatorChar);
        }
    }
  

Вы можете попробовать это здесь.

Ответ №4:

Вместо того, чтобы сначала разбивать строку, я нахожу более элегантным начать с того, что у вас есть, и сократить его:

 var folder = "one.two.three";
var newFolder = string.Empty;
for (var f = folder; f.Any(); f = f.Remove(Math.Max(f.LastIndexOf('.'), 0)))
    newFolder = Path.Combine(f, newFolder);

Console.WriteLine(newFolder);
  

Вывод:

 oneone.twoone.two.three