You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#中字符串转数值数组的高效实现问询(超大规模数据场景)

高效C#字符串转数值数组实现方案(针对超大规模数据)

字符串示例:

0 0 1 2.33 4
2.1 2 11 2

需要将这类字符串转换为数值数组,但数据量可能高达10亿个元素,因此需要最快的实现方案。另外,字符串中数字之间的空格数量不固定。

当前尝试的实现代码

static void Main()
{
    string str = "\n\n\n 1 2 3   \r  2322.2 3 4 \n  0 0 ";

    byte[] byteArray = Encoding.ASCII.GetBytes(str);
    MemoryStream stream = new MemoryStream(byteArray);

    var values = ReadNumbers(stream);
    
}

public static IEnumerable<object> ReadNumbers(Stream st)
{
    var buffer = new StringBuilder();
    using (var sr = new StreamReader(st))
    {
        while (!sr.EndOfStream)
        {
            char digit = (char)sr.Read();
            if (!char.IsDigit(digit) && digit != '.')
            {
                if (buffer.Length == 0) continue;
                double ret = double.Parse(buffer.ToString(), culture);
                buffer.Clear();
                yield return ret;
            }
            else
            {
                buffer.Append(digit);
            }
        }

        if (buffer.Length != 0)
        {
            double ret = double.Parse(buffer.ToString(), culture);
            buffer.Clear();
            yield return ret;
        }
    }
}

高效优化方案

核心优化思路

针对10亿级别的超大规模数据,优化重点在于消除中间转换开销、减少内存分配、避免装箱操作:

  • 直接处理ASCII字节数组,跳过StreamReader的字符编码转换步骤(ASCII字节与字符一一对应,无需额外转换);
  • 定位数字的起始/结束字节索引,用内存切片直接解析,避免StringBuilder的频繁拼接与字符串创建;
  • 返回强类型IEnumerable<double>,消除object装箱带来的性能损耗;
  • 兼容所有空白分隔符(空格、换行、回车、制表符等),支持任意数量的分隔空格。

优化代码(基础版)

using System;
using System.Collections.Generic;
using System.Text;

class Program
{
    static void Main()
    {
        string str = "\n\n\n 1 2 3   \r  2322.2 3 4 \n  0 0 ";
        byte[] byteArray = Encoding.ASCII.GetBytes(str);
        
        var values = ReadNumbers(byteArray);
        // 10亿级数据建议分批处理,避免一次性加载到内存导致溢出
    }

    public static IEnumerable<double> ReadNumbers(byte[] byteArray)
    {
        int startIndex = -1;
        int length = byteArray.Length;
        
        // 判断是否为分隔符:空格(32)、换行(10)、回车(13)、制表符(9)
        bool IsSeparator(byte b) => b is 32 or 10 or 13 or 9;
        // 判断是否为数字或小数点
        bool IsDigitOrDot(byte b) => (b >= 48 && b <= 57) || b == 46;

        for (int i = 0; i < length; i++)
        {
            byte current = byteArray[i];
            if (IsDigitOrDot(current))
            {
                if (startIndex == -1)
                {
                    startIndex = i;
                }
            }
            else
            {
                if (startIndex != -1)
                {
                    // 将字节切片转为字符Span,直接解析为double
                    ReadOnlySpan<char> charSpan = Encoding.ASCII.GetString(byteArray, startIndex, i - startIndex).AsSpan();
                    if (double.TryParse(charSpan, out double value))
                    {
                        yield return value;
                    }
                    startIndex = -1;
                }
            }
        }

        // 处理字符串末尾的最后一个数字
        if (startIndex != -1)
        {
            ReadOnlySpan<char> charSpan = Encoding.ASCII.GetString(byteArray, startIndex, length - startIndex).AsSpan();
            if (double.TryParse(charSpan, out double value))
            {
                yield return value;
            }
        }
    }
}

极致优化版(针对10亿级数据)

如果追求极限性能,可以跳过框架的double.Parse,手动实现ASCII浮点数解析(仅支持正数,需支持负数可扩展),进一步消除框架解析的额外逻辑开销:

using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        string str = "\n\n\n 1 2 3   \r  2322.2 3 4 \n  0 0 ";
        byte[] byteArray = Encoding.ASCII.GetBytes(str);
        
        var values = ReadNumbers(byteArray);
    }

    public static IEnumerable<double> ReadNumbers(byte[] byteArray)
    {
        int startIndex = -1;
        int length = byteArray.Length;
        
        bool IsSeparator(byte b) => b is 32 or 10 or 13 or 9;
        bool IsDigitOrDot(byte b) => (b >= 48 && b <= 57) || b == 46;

        for (int i = 0; i < length; i++)
        {
            byte current = byteArray[i];
            if (IsDigitOrDot(current))
            {
                if (startIndex == -1)
                {
                    startIndex = i;
                }
            }
            else
            {
                if (startIndex != -1)
                {
                    var byteSpan = byteArray.AsSpan(startIndex, i - startIndex);
                    if (TryParseAsciiDouble(byteSpan, out double value))
                    {
                        yield return value;
                    }
                    startIndex = -1;
                }
            }
        }

        if (startIndex != -1)
        {
            var byteSpan = byteArray.AsSpan(startIndex, length - startIndex);
            if (TryParseAsciiDouble(byteSpan, out double value))
            {
                yield return value;
            }
        }
    }

    // 手动解析ASCII格式的正浮点数
    private static bool TryParseAsciiDouble(ReadOnlySpan<byte> bytes, out double result)
    {
        result = 0;
        double fractionMultiplier = 0.1;
        bool isFractionPart = false;

        foreach (byte b in bytes)
        {
            if (b == 46) // 小数点
            {
                if (isFractionPart)
                {
                    // 存在多个小数点,解析失败
                    result = 0;
                    return false;
                }
                isFractionPart = true;
                continue;
            }

            if (b < 48 || b > 57)
            {
                // 非数字字符,解析失败
                result = 0;
                return false;
            }

            int digit = b - 48;
            if (!isFractionPart)
            {
                result = result * 10 + digit;
            }
            else
            {
                result += digit * fractionMultiplier;
                fractionMultiplier *= 0.1;
            }
        }

        return true;
    }
}

性能提升关键点

  1. 字节级直接处理:跳过StreamReader的字符转换,直接操作ASCII字节数组,减少中间环节开销;
  2. Span内存切片:避免创建临时字符串,所有解析操作基于内存切片,减少GC压力;
  3. 手动解析优化:跳过框架解析的文化信息、负数、指数等额外逻辑,仅处理需求范围内的格式,速度提升明显;
  4. 强类型返回:返回IEnumerable<double>,消除object装箱带来的性能损耗。

内容的提问来源于stack exchange,提问作者artem_tmm

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 12:35:22