我有一个像这样的字符串:
-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(", ");
}
}
但是在我看来,这个过程并不是专业的解决方案。任何人都可以建议更有效的方法吗?
答案 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);