C# 直接将子字符串解析为double
如果我有一个字符串,例如C# 直接将子字符串解析为double,c#,.net,f#,C#,.net,F#,如果我有一个字符串,例如1 2 3,并且我识别了包含双精度的子字符串的位置,那么我如何直接从子字符串解析它而不创建临时字符串 例如,我可以执行System.Double.Parse(str.Substring(0,1)),但这将创建一个缓慢且不必要的临时字符串。可以直接从原始字符串的一部分解析double吗 编辑 埃里克·利珀特(Eric Lippert)质疑我在这里的动机,称“小弦很便宜”。这样做的动机来自于我对int的解析做了同样的事情,并看到了巨大的性能改进,因为很显然,小字符串并不便宜
1 2 3
,并且我识别了包含双精度
的子字符串的位置,那么我如何直接从子字符串解析它而不创建临时字符串
例如,我可以执行System.Double.Parse(str.Substring(0,1))
,但这将创建一个缓慢且不必要的临时字符串。可以直接从原始字符串的一部分解析double吗
编辑
埃里克·利珀特(Eric Lippert)质疑我在这里的动机,称“小弦很便宜”。这样做的动机来自于我对int的解析做了同样的事情,并看到了巨大的性能改进,因为很显然,小字符串并不便宜
下面是一个函数,该函数通过临时字符串来解析整数序列:
let lex f (s: string) =
let rec inside i0 (s: string, i) =
if i = s.Length then
f (s.Substring(i0, i-i0) |> System.Int32.Parse)
else
let c = s.[i]
if '0'<=c && c<='9' then
inside i0 (s, i+1)
else
f (s.Substring(i0, i-i0) |> System.Int32.Parse)
outside (s, i)
and outside (s: string, i) =
if i < s.Length then
let c = s.[i]
if '0'<=c && c<='9' then
inside i (s, i)
else
outside (s, i+1)
outside (s, 0)
let lex f (s: string) =
let rec inside n (s: string, i) =
if i = s.Length then f n else
let c = s.[i]
if '0'<=c && c<='9' then
inside (10*n + int c - int '0') (s, i+1)
else
f n
outside (s, i)
and outside (s: string, i) =
if i < s.Length then
let c = s.[i]
if '0'<=c && c<='9' then
inside 0 (s, i)
else
outside (s, i+1)
outside (s, 0)
让lex f(s:string)=
让rec在i0内(s:string,i)=
如果i=s.长度,则
f(s.Substring(i0,i-i0)|>System.Int32.Parse)
其他的
设c=s[i]
如果“0”这是您所能做的最好的
static void Main(string[] args)
{
string input = "1 2 3";
double[] output = input.Split(new char[] {' '},StringSplitOptions.RemoveEmptyEntries).Select(x => double.Parse(x)).ToArray();
}
是的,我认为这完全可行。您可以编写自己的函数来进行解析,甚至可以基于Double.Parse()
的源代码。这段代码看起来并不庞大和可怕,我认为您可以根据自己的需要对其进行进一步优化。您可以逐位解析字符串,如下所示:
static double CustomConvertToDouble(string input, int startIndex, int length)
{
double result = 0d;
int lastDigitIndex = startIndex + length - 1;
int power = 0;
for (int i = lastDigitIndex; i >= startIndex; i--)
{
int digit = (input[i] - '0');
result += (Math.Pow(10, power++)) * digit;
}
return result;
}
用法:
string tmp = "1 2 3";
double result = CustomConvertToDouble(tmp, 0, 1);
Console.WriteLine(result); // 1
你可以把小数点等因素考虑进去
但我真的怀疑正常的方式是否会成为性能瓶颈,我想知道你为什么要惹麻烦。如果这段代码确实对性能至关重要,那么最好的方法可能是用另一种语言编写它?for(int x=0;xfor (int x = 0; x < input.Length; x++)
{
if(input[x] != ' ')
Console.WriteLine(Double.Parse(input[x].ToString()));
}
{
如果(输入[x]!='')
Console.WriteLine(Double.Parse(输入[x].ToString());
}
不创建任何额外的可枚举对象,而是Double。仅解析例外字符串,因此需要toString 如果您只查找个位数,则很容易:
let readDigit s i =
let getDigit x =
if '0' <= x && x <= '9'
then byte x - 48uy // byte value of '0'
else failwith "Not a digit"
s |> Seq.item i |> getDigit |> double
让我们读数字s i=
让我们得到数字x=
如果“0”获取数字|>双精度
此F#实现使用字符串实现字符序列,并且字符值可以转换为字节值
不过,我怀疑它是否比使用Double.Parse(str.Substring(0,1))
更快。对我来说似乎是一种极端的微观优化。您需要一个库,或者编写一个完全成熟的双解析器,这不是一个简单的任务?细绳很便宜。这就是说,你当然可以编写一个只复制单个字符的词法分析器。@EricLippert:我已经用解析整数的基准代码更新了这个问题,而不创建临时变量,速度快了9倍多。我假设解析浮动将看到类似的巨大性能提升。可以说,我不会说“小弦是便宜的”。好吧,有便宜的,然后有足够便宜的;有些东西可能很便宜,但仍然太贵。如果创建小字符串的内存和时间负担不够便宜,那么就编写自己的代码,这已经足够便宜了。问题解决;这个系统可以工作,是的。令人沮丧的是,我所需要的一切都已经在.NET中了,但却隐藏在一个API后面,该API将输入限制为一个完整的字符串,而不允许子字符串输入,从而削弱了性能。理想情况下,.NET将为接受字符串、起始索引和长度的Parse
函数提供重载。毕竟,我想几乎所有实际应用的Parse
函数实际上都作用于一个子串……我想这比System.Double.Parse(str.substring(0,1))
抱歉,我没有看到答案的这一部分,我已经从评论中删除了稠密上的200行,具有深度嵌套循环和分支的不安全C#。除了辅助助手的类型/方法之外。我不会这么随便地否定这一点。(更不用说/unsafe
的要求意味着一个人的程序集不再是可验证的,并且具有各种其他潜在的不可接受的限制)对于某些类型的任务,它是值得的。我认为,没有子字符串不是一个很大的优化,但是解析函数本身有很大的优化潜力。嗯??他们用不安全的代码把它的性能搞得一团糟,然后他们把它包装在一个API中,迫使你复制子字符串,所以速度非常慢。坚果这增加了3个数组的额外分配,一个IEnumerable
,一个Func
。考虑到OP甚至不能容忍单个字符串的分配,我认为这不符合要求。更糟糕的是,您将所有临时变量都保存在一个数组中,这样它们就可以存活两代,每次都需要支付标记、转移和指针更新的费用,所以解决方案可能比需要慢40倍。我知道,但请求是“直接解析”。避免临时变量必然会提高优化编译器的速度。去linq并不是最有效的方法。如果列是固定宽度的,子字符串可能比Split()方法更有效,但是如果列不是固定宽度的,Split()方法会更有效。@jdweng这里的想法是根本不创建任何字符串。解析浮点所需的所有内容都在原始字符串中,因此在解析之前将其复制到另一个字符串中是没有意义的。我在哪里复制了一个字符串?我只是添加了测试代码,不是我。但是,该代码将非常慢!:-)