将子字符串解析为直接加倍

时间:2016-01-07 02:26:23

标签: c# .net f#

如果我有一个字符串,例如1 2 3,并且我确定了包含double的子字符串的位置,那么如何直接从子字符串解析它而不创建临时字符串?

例如,我可以System.Double.Parse(str.Substring(0, 1)),但这会创建一个缓慢且不必要的临时字符串。是否可以直接从原始字符串的一部分解析double?

修改

Eric Lippert在这里质疑我的动机,说“小弦很便宜”。这样做的动机来自于我对int的解析和看到大量的性能改进做同样的事情,因为显然,小字符串不是那么便宜。

这是一个通过临时字符串来表示一系列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)

这需要2.4s才能从字符串中获得15,625,000英镑的罚款。

这是一个避免临时字符串的版本:

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)

这需要0.255秒,比使用临时字符串的解决方案快9倍!

我认为没有理由为什么lexing浮标应该有所不同。因此,通过不提供从子字符串解析浮点数的能力,.NET在表上保留了一个数量级的性能。我做了大量的科学计算,经常需要大量的数据,尤其是在启动时,所以我真的不想像这样把性能提升到风中。

5 个答案:

答案 0 :(得分:2)

是的,我认为这完全可行。您可以编写自己的函数来进行解析,甚至可以将其基于Double.Parse() .state('main', { url: '/', templateUrl: '/views/main.html', controller: 'MainCtrl as vm', resolve: { userAccount: userAccountMain } }) 源代码。这段代码看起来并不大而且我觉得你可以根据自己的需要进行更优化。

答案 1 :(得分:1)

您可以逐位解析字符串,如下所示:

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

您可以对此进行扩展以考虑小数点等。

但我真的怀疑正常的方式是否会成为性能瓶颈,而且我很想知道你为什么想要解决这个问题。如果那段代码确实对性能至关重要,那么最好的方法是用另一种语言编写它?

答案 2 :(得分:0)

如果您只是寻找个位数,那就很容易了:

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

此F#实现使用string实现char seq,并且char值可以转换为byte值。

我怀疑它比使用Double.Parse(str.Substring(0, 1))更快。

答案 3 :(得分:-1)

for (int x = 0; x < input.Length; x++)
{
    if(input[x] != ' ')
        Console.WriteLine(Double.Parse(input[x].ToString()));
}

不创建任何额外的Enumerable对象,但Double.Parse仅排除字符串,因此需要toString。

答案 4 :(得分:-2)

这是你能做的最好的事情

static void Main(string[] args)
{
    string input = "1 2 3";
    double[] output = input.Split(new char[] {' '},StringSplitOptions.RemoveEmptyEntries).Select(x => double.Parse(x)).ToArray();
}