C # проблема манипуляции со строками - PullRequest
5 голосов
/ 25 марта 2009

Вот простая проблема. У меня есть приложение, которое берет номер телефона, например, «13335557777», и мне нужно повернуть его и вставить точку между номерами, например:

"7.7.7.7.5.5.5.3.3.3.1."

Я знаю, что могу сделать это с помощью StringBuilder и цикла for, чтобы перевернуть строку и вставить точки, но есть ли умный способ сделать это в LINQ (или каким-либо другим способом)?

Примечание: для этого меня не особо беспокоит производительность, распределение памяти или что-то еще, просто интересно посмотреть, как это будет сделано в LINQ.

Ответы [ 7 ]

8 голосов
/ 25 марта 2009

Попробуйте это

var source = GetTheString();
var reversed = source.Reverse().Select(x => x.ToString()).Aggregate((x,y) => x + "." + y);

EDIT

Это решение определенно нацелено на "умный" конец. Вероятно, гораздо эффективнее использовать StringBuilder для построения строки. Это решение создает много промежуточных строк.

EDIT2

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

  • Обычный агрегат (100 элементов): 00: 00: 00.0418640
  • WithStringBuilder (100 элементов): 00: 00: 00.0040099
  • Обычный агрегат (1000 элементов): 00: 00: 00.3062040
  • WithStringBuilder (1000 элементов): 00: 00: 00.0405955
  • Обычный агрегат (10000 элементов): 00: 00: 03.0270392
  • WithStringBuilder (10000 элементов): 00: 00: 00.4149977

Однако значимость разницы в скорости сильно зависит от того, где она фактически используется в вашем приложении.

Код для эталона.

public static class AggregateUnchanged {
    public static string Run(string input) {
        return input
            .Reverse()
            .Select(x => x.ToString())
            .Aggregate((x, y) => x + "." + y);
    }
}

public static class WithStringBuilder {
    public static string Run(string input) {
        var builder = new StringBuilder();
        foreach (var cur in input.Reverse()) {
            builder.Append(cur);
            builder.Append('.');
        }

        if (builder.Length > 0) {
            builder.Length = builder.Length - 1;
        }

        return builder.ToString();
    }
}

class Program {
    public static void RunAndPrint(string name, List<string> inputs, Func<string, string> worker) {

        // Test case. JIT the code and verify it actually works 
        var test = worker("123456");
        if (test != "6.5.4.3.2.1") {
            throw new InvalidOperationException("Bad algorithm");
        }

        var watch = new Stopwatch();
        watch.Start();
        foreach (var cur in inputs) {
            var result = worker(cur);
        }
        watch.Stop();
        Console.WriteLine("{0} ({2} elements): {1}", name, watch.Elapsed, inputs.Count);
    }

    public static string NextInput(Random r) {
        var len = r.Next(1, 1000);
        var builder = new StringBuilder();
        for (int i = 0; i < len; i++) {
            builder.Append(r.Next(0, 9));
        }
        return builder.ToString();
    }

    public static void RunAll(List<string> input) {
        RunAndPrint("Normal Aggregate", input, AggregateUnchanged.Run);
        RunAndPrint("WithStringBuilder", input, WithStringBuilder.Run);
    }

    static void Main(string[] args) {
        var random = new Random((int)DateTime.Now.Ticks);
        RunAll(Enumerable.Range(0, 100).Select(_ => NextInput(random)).ToList());
        RunAll(Enumerable.Range(0, 1000).Select(_ => NextInput(random)).ToList());
        RunAll(Enumerable.Range(0, 10000).Select(_ => NextInput(random)).ToList());
    }
}
2 голосов
/ 25 марта 2009

Преимущество этого метода в том, что String.Join будет дешевле, чем ".Aggregate ((x, y) => x +". "+ Y)".

var target = string.Join(".", source.Reverse().Select(c => c.ToString()).ToArray());
1 голос
/ 25 марта 2009
        string x = "123456";
        StringBuilder y = new StringBuilder(x.Length * 2);

        for (int i = x.Length - 1; i >= 0; i--)
        {
            y.Append(x[i]);
            y.Append(".");
        }
0 голосов
/ 25 марта 2009

Это действительно проблема linq? Просто работайте в цикле назад на символ:

string s = "";
string content = "35557777";
for (int i = content.Length -1; i > 0; i--)
{
    s += content[i] + ".";
}
Console.WriteLine(s);
Console.ReadLine();

Если это строка длиннее 4k, используйте StringBuilder. Использование LINQ для «7.7.7.7.5.5.5.3.3.3.1.» это не то, для чего нужен LINQ, отметьте мне -99, если хотите, и Марку тоже.

0 голосов
/ 25 марта 2009
string aString = "13335557777";
string reversed = (from c in aString.Reverse()
                 select c + ".").Aggregate((a, b) => a + b);
0 голосов
/ 25 марта 2009

Пока вы уже просматриваете массив, будет проще использовать string.Join:

string[] source = GetTheStringAsArray();
string reversed = string.Join(".", source.Reverse());
0 голосов
/ 25 марта 2009

(удален ответ с использованием списка char)

Что касается комментария к другому сообщению, я считаю, что, хотя LINQ и т. Д. Могут быть "умными", они не обязательно эффективны. Например, будет создано много промежуточных строк, которые нужно собрать.

Я бы придерживался StringBuilder и т. Д., Если у вас нет веских оснований для его изменения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...