如何将字符串拆分为有效的方式C#

时间:2019-03-02 20:23:28

标签: c# string

我有一个像这样的字符串:

 -82.9494547,36.2913021,0
 -83.0784938,36.2347521,0
 -82.9537782,36.079235,0

我需要这样的输出:

 -82.9494547 36.2913021, -83.0784938 36.2347521, -82.9537782,36.079235

我已经尝试了以下代码来实现所需的输出:

string[] coordinatesVal = coordinateTxt.Trim().Split(new string[] { ",0" }, StringSplitOptions.None);

        for (int i = 0; i < coordinatesVal.Length - 1; i++)
        {
            coordinatesVal[i] = coordinatesVal[i].Trim();
            coordinatesVal[i] = coordinatesVal[i].Replace(',', ' ');

            numbers.Append(coordinatesVal[i]);

            if (i != coordinatesVal.Length - 1)
            {
                coordinatesVal.Append(", ");
            }

        } 

但是在我看来,这个过程并不是专业的解决方案。任何人都可以建议更有效的方法吗?

5 个答案:

答案 0 :(得分:4)

您的代码还可以。您可以取消临时结果和链式方法调用

var numbers = new StringBuilder();
string[] coordinatesVal = coordinateTxt
    .Trim()
    .Split(new string[] { ",0" }, StringSplitOptions.None);
for (int i = 0; i < coordinatesVal.Length - 1; i++) {
    numbers
        .Append(coordinatesVal[i].Trim().Replace(',', ' '))
        .Append(", ");
}
numbers.Length -= 2;

请注意,最后一条语句假定至少有一个坐标对可用。如果坐标可以为空,则必须将循环和最后一条语句放在if (coordinatesVal.Length > 0 ) { ... }中。这比在循环中放入if更为有效。

答案 1 :(得分:1)

您询问效率,但未指定是指代码效率(执行速度)还是程序员效率(您需要花多少时间)。 专业编程的一个关键部分是判断在任何给定情况下哪一个更重要。

其他答案在覆盖程序员的效率方面做得很好,因此我在尝试提高代码效率。我在家里这样做很有趣,但是对于专业工作,在投入时间甚至比较其他答案中给出的方法的速度之前,我需要一个很好的理由,更不用说对它们进行改进了。 / p>

话虽如此,等待程序完成数百万个坐标对的转换会给我这样的理由。

C#字符串处理的一个速度陷阱是String.Replace()String.Trim()返回字符串的全新副本的方式。这涉及分配内存,复制字符并最终清除生成的垃圾。这样做几百万次,它开始累加起来。考虑到这一点,我试图避免尽可能多的分配和副本。

    enum CurrentField
    {
        FirstNum,
        SecondNum,
        UnwantedZero
    };

    static string ConvertStateMachine(string input)
    {
        // Pre-allocate enough space in the string builder.
        var numbers = new StringBuilder(input.Length);

        var state = CurrentField.FirstNum;
        int i = 0;
        while (i < input.Length)
        {
            char c = input[i++];

            switch (state)
            {
                // Copying the first number to the output, next will be another number
                case CurrentField.FirstNum:
                    if (c == ',')
                    {
                        // Separate the two numbers by space instead of comma, then move on
                        numbers.Append(' ');
                        state = CurrentField.SecondNum;
                    }
                    else if (!(c == ' ' || c == '\n'))
                    {
                        // Ignore whitespace, output anything else
                        numbers.Append(c);
                    }
                    break;

                // Copying the second number to the output, next will be the ,0\n that we don't need
                case CurrentField.SecondNum:
                    if (c == ',')
                    {
                        numbers.Append(", ");
                        state = CurrentField.UnwantedZero;
                    }
                    else if (!(c == ' ' || c == '\n'))
                    {
                        // Ignore whitespace, output anything else
                        numbers.Append(c);
                    }
                    break;
                case CurrentField.UnwantedZero:
                    // Output nothing, just track when the line is finished and we start all over again.
                    if (c == '\n')
                    {
                        state = CurrentField.FirstNum;
                    }
                    break;
            }
        }
        return numbers.ToString();
    }

这使用状态机根据输入字符是第一个数字,第二个数字还是行的其余部分来区别对待输入的字符,并相应地输出字符。每个字符仅复制一次到输出中,然后我相信在输出最后转换为字符串时再复制一次。通过为输出使用char[]可以避免第二次转换。

此代码中的瓶颈似乎是StringBuilder.Append()的调用次数。如果需要更高的速度,我首先尝试跟踪要直接复制到输出中的字符数,然后使用.Append(string value, int startIndex, int count)在一个呼叫中发送整个数字。

我将一些示例解决方案放入测试工具,并在包含300,000条坐标对线的字符串上运行它们,平均运行50多次。我的电脑上的结果是:

String Split, Replace each line (see Olivier's answer, though I pre-allocated the space in the StringBuilder):
    6542 ms / 13493147 ticks, 130.84ms / 269862.9 ticks per conversion
Replace & Trim entire string (see Heriberto's second version):
    3352 ms / 6914604 ticks, 67.04 ms / 138292.1 ticks per conversion
    - Note: Original test was done with 900000 coord pairs, but this entire-string version suffered an out of memory exception so I had to rein it in a bit.
Split and Join (see Łukasz's answer):
    8780 ms / 18110672 ticks, 175.6 ms / 362213.4 ticks per conversion
Character state machine (see above):
    1685 ms / 3475506 ticks, 33.7 ms / 69510.12 ticks per conversion

因此,哪个版本最有效的问题归结为:您的要求是什么?

答案 2 :(得分:0)

您的解决方案很好。也许您可以这样写一些更优雅:

string[] coordinatesVal = coordinateTxt.Trim().Split(new string[] { ",0" }, 
StringSplitOptions.RemoveEmptyEntries);
string result = string.Empty;
foreach (string line in coordinatesVal)
{
    string[] numbers = line.Trim().Split(',');
    result += numbers[0] + " " + numbers[1] + ", ";
}
result = result.Remove(result.Count()-2, 2);

请注意StringSplitOptions.RemoveEmptyEntries方法的Split参数,因此您不必在 foreach 块中处理空行。

答案 3 :(得分:0)

或者您可以做一个非常短的单线。调试起来比较困难,但是在简单的情况下就可以了。

string result =
  string.Join(", ",
    coordinateTxt.Trim().Split(new string[] { ",0" }, StringSplitOptions.RemoveEmptyEntries).
      Select(i => i.Replace(",", " ")));

答案 4 :(得分:0)

这是另一种无需定义自己的循环和替换方法或使用LINQ的方法。

 string coordinateTxt = @" -82.9494547,36.2913021,0
 -83.0784938,36.2347521,0
 -82.9537782,36.079235,0";

            string[] coordinatesVal = coordinateTxt.Replace(",", "*").Trim().Split(new string[] { "*0", Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
            string result = string.Join(",", coordinatesVal).Replace("*", " ");
            Console.WriteLine(result);

甚至

            string coordinateTxt = @" -82.9494540,36.2913021,0
-83.0784938,36.2347521,0
-82.9537782,36.079235,0";

            string result = coordinateTxt.Replace(Environment.NewLine, "").Replace($",", " ").Replace(" 0", ", ").Trim(new char[]{ ',',' ' });
            Console.WriteLine(result);