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这里的想法是根本不创建任何字符串。解析浮点所需的所有内容都在原始字符串中,因此在解析之前将其复制到另一个字符串中是没有意义的。我在哪里复制了一个字符串?我只是添加了测试代码,不是我。但是,该代码将非常慢!:-)