C# 如何将int写入System.Span,即int.Parse(Span)的反向?

C# 如何将int写入System.Span,即int.Parse(Span)的反向?,c#,.net-core,C#,.net Core,我需要编写和读取大型CSV(逗号分隔值)文件,这些文件基本上包含转换为字符串的整数值。为了高效地读取此类文件,.Net Core为类型int引入了一种新的Parse方法: public static int Parse (ReadOnlySpan<char> s, System.Globalization.NumberStyles style = System.Globalization.NumberStyles.Integer, IFormatProvider provi

我需要编写和读取大型CSV(逗号分隔值)文件,这些文件基本上包含转换为字符串的整数值。为了高效地读取此类文件,.Net Core为类型
int
引入了一种新的
Parse
方法:

public static int Parse (ReadOnlySpan<char> s,
  System.Globalization.NumberStyles style = 
  System.Globalization.NumberStyles.Integer, IFormatProvider provider = null);
同样,对于每一个
int
都会创建一个
string
,然后为每一行创建另一个
string
。这将创建数百万个需要进行垃圾收集的字符串

我更喜欢这样的:

char[] charArray = getEmptyCharArray();
var span = new Span<char>(charArray);
int length1 = span.Write(int1);
charArray[length1] = ',';
span = span.Slice(length1 + 1);
int length2 = span.Write(int2);
streamWriter.Write(charArray, 0, length1 + 1 + length2);
char[]charArray=getEmptyCharArray();
var span=新span(charArray);
int length1=span.Write(int1);
charArray[长度1]=',';
span=span.切片(长度1+1);
int length2=span.Write(int2);
streamWriter.Write(字符,0,长度1+1+2);
getEmptyCharray()
提供了一个可重用的字符数组

不幸的是,
Span
没有
Write()
函数:-(

因此,问题是:如何在不生成任何垃圾收集对象(字符串)的情况下,将
int
(或
DateTime
Decimal
或…)写入
Span


请注意,2018年之前给出的任何答案可能都不是本文所需要的,因为
System.Span
仅在.NET Core 2.1中介绍。还要注意,这里的问题是关于
System.Span
的,而不是HTML Span或任何其他Span。

如何尝试将
int
直接解析为
char
数组通过传递所有数字,将其转换为
char
-s,并将其直接存储到目标

public static ReadOnlySpan<char> ToSpan(int src)
        {
            int len = GetLength(src);
            Span<char> chars = new char[len];
            for (int i = 0; i < chars.Length; i++)
            {
                chars[i]= (char)((Math.Floor(src / Math.Pow(10, (chars.Length - i - 1))) % 10) + 48);
            }
            return chars;

            static int GetLength(int src)
            {
                int len = 0;
                while (src > 0)
                {
                    src = src / 10;
                    len++;
                }
                return len;
            }

        }
        static void Main(string[] args)
        {
            int original = 3334;
            var data = ToSpan(original);
            var copy= int.Parse(data);
            Console.WriteLine(copy);
        }
publicstaticreadonlyspan-ToSpan(int-src)
{
int len=GetLength(src);
Span字符=新字符[len];
for(int i=0;i0)
{
src=src/10;
len++;
}
回程透镜;
}
}
静态void Main(字符串[]参数)
{
int-original=3334;
var数据=ToSpan(原始);
var copy=int.Parse(数据);
控制台写入线(副本);
}
p.S

  • 糟糕的是,您需要首先在
    int
    上迭代,以获得目标的长度
  • 您当然可以对我将数字转换为字符的方式进行一些优化,也可以对我分隔数字的方式进行优化

  • 多亏了伊恩·肯特的评论,我继续问,他们知道答案。答案非常简单:

    var i = 1;
    Span<char> span = new char[100];
    var ok = i.TryFormat(span, out var charsWritten);
    
    var i=1;
    Span=新字符[100];
    var ok=i.TryFormat(范围,写出var字符);
    
    由于我有几天没有找到这个答案,并且我想继续编写代码,所以我编写了自己的方法,但使用char[]而不是Span。我使用BenchmarkRunner测量了不同方法编写50兆字节的CSV文件(包含7'000'000整数)的速度:

    60毫秒:写入相同的常量字符串。这给出了DotNet只需写入文件所需的基线时间

    public void WriteTo4() {
      var PathFileName = directoryInfo.FullName + @"\Test1.csv";
      using (var fileStream = new FileStream(PathFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, bufferSize, FileOptions.SequentialScan)) {
        using (var streamWriter = new StreamWriter(fileStream)) {
          var lineBuffer = new char[100];
          Span<char> span = lineBuffer;
          for (int i = 0; i < iterations; i++) {
            var ok = i.TryFormat(span, out var charsWritten);
            lineBuffer[charsWritten++] = ';';
            var span1 = span[charsWritten..];
            ok = (i+1).TryFormat(span1, out charsWritten);
            span1[charsWritten++] = ';';
            span1 = span1[charsWritten..];
            ok = (i+2).TryFormat(span1, out charsWritten);
            span1[charsWritten++] = ';';
            span1 = span1[charsWritten..];
            ok = (i+3).TryFormat(span1, out charsWritten);
            span1[charsWritten++] = ';';
            span1 = span1[charsWritten..];
            ok = (i+4).TryFormat(span1, out charsWritten);
            span1[charsWritten++] = ';';
            span1 = span1[charsWritten..];
            ok = (i+5).TryFormat(span1, out charsWritten);
            span1[charsWritten++] = ';';
            span1 = span1[charsWritten..];
            ok = (i+6).TryFormat(span1, out charsWritten);
            span1[charsWritten++] = ';';
    
            var ca = lineBuffer[..(lineBuffer.Length - span1.Length + charsWritten)];
            streamWriter.WriteLine(lineBuffer, 0, lineBuffer.Length - span1.Length + charsWritten);
          }
        }
      }
    }
    
    对于(int i=0;i 610毫秒:使用ToString()

    对于(int i=0;i 308毫秒:使用TryFormat(Span)

    185毫秒:使用我自己的方法和字符[]

    public void WriteTo3() {
      var PathFileName = directoryInfo.FullName + @"\Test1.csv";
      using (var fileStream = new FileStream(PathFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, bufferSize, FileOptions.SequentialScan)) {
        using (var streamWriter = new StreamWriter(fileStream)) {
          var lineBuffer = new char[100];
          for (int i = 0; i < iterations; i++) {
            var index = 0;
            lineBuffer.Write3(i, ref index);
            lineBuffer[index++] = ';';
            lineBuffer.Write3(i+1, ref index);
            lineBuffer[index++] = ';';
            lineBuffer.Write3(i+2, ref index);
            lineBuffer[index++] = ';';
            lineBuffer.Write3(i+3, ref index);
            lineBuffer[index++] = ';';
            lineBuffer.Write3(i+4, ref index);
            lineBuffer[index++] = ';';
            lineBuffer.Write3(i+5, ref index);
            lineBuffer[index++] = ';';
            lineBuffer.Write3(i+6, ref index);
            lineBuffer[index++] = ';';
            streamWriter.WriteLine(lineBuffer, 0, index);
          }
        }
      }
    }
    
    
    public static void Write3(this char[] charArray, int i, ref int index) {
      if (i<0) {
        charArray[index++] = '-';
        i = -i;
      }
      int start = index;
    
      while (i>9) {
        charArray[index++] = (char)((i % 10) + '0');
        i /= 10;
      }
      charArray[index++] = (char)(i + '0');
      var end = index-1;
      while (end>start) {
        var temp = charArray[end];
        charArray[end--] = charArray[start];
        charArray[start++] = temp;
      }
    }
    
    令人惊讶的是,字符串对话所花费的时间是实际文件编写时间的10倍。我原以为硬盘比任何软件都慢

    我们被告知,Span将解决许多性能问题。不会太多。如果他们使用char[],似乎会更好

    跨度测试代码

    public void WriteTo4() {
      var PathFileName = directoryInfo.FullName + @"\Test1.csv";
      using (var fileStream = new FileStream(PathFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, bufferSize, FileOptions.SequentialScan)) {
        using (var streamWriter = new StreamWriter(fileStream)) {
          var lineBuffer = new char[100];
          Span<char> span = lineBuffer;
          for (int i = 0; i < iterations; i++) {
            var ok = i.TryFormat(span, out var charsWritten);
            lineBuffer[charsWritten++] = ';';
            var span1 = span[charsWritten..];
            ok = (i+1).TryFormat(span1, out charsWritten);
            span1[charsWritten++] = ';';
            span1 = span1[charsWritten..];
            ok = (i+2).TryFormat(span1, out charsWritten);
            span1[charsWritten++] = ';';
            span1 = span1[charsWritten..];
            ok = (i+3).TryFormat(span1, out charsWritten);
            span1[charsWritten++] = ';';
            span1 = span1[charsWritten..];
            ok = (i+4).TryFormat(span1, out charsWritten);
            span1[charsWritten++] = ';';
            span1 = span1[charsWritten..];
            ok = (i+5).TryFormat(span1, out charsWritten);
            span1[charsWritten++] = ';';
            span1 = span1[charsWritten..];
            ok = (i+6).TryFormat(span1, out charsWritten);
            span1[charsWritten++] = ';';
    
            var ca = lineBuffer[..(lineBuffer.Length - span1.Length + charsWritten)];
            streamWriter.WriteLine(lineBuffer, 0, lineBuffer.Length - span1.Length + charsWritten);
          }
        }
      }
    }
    
    public void WriteTo4(){
    var PathFileName=directoryInfo.FullName+@“\Test1.csv”;
    使用(var fileStream=newfilestream(路径文件名、FileMode.OpenOrCreate、FileAccess.ReadWrite、FileShare.None、bufferSize、FileOptions.SequentialScan)){
    使用(var streamWriter=newstreamwriter(fileStream)){
    var lineBuffer=新字符[100];
    Span=行缓冲区;
    对于(int i=0;i
    使用char[]测试代码

    public void WriteTo3() {
      var PathFileName = directoryInfo.FullName + @"\Test1.csv";
      using (var fileStream = new FileStream(PathFileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None, bufferSize, FileOptions.SequentialScan)) {
        using (var streamWriter = new StreamWriter(fileStream)) {
          var lineBuffer = new char[100];
          for (int i = 0; i < iterations; i++) {
            var index = 0;
            lineBuffer.Write3(i, ref index);
            lineBuffer[index++] = ';';
            lineBuffer.Write3(i+1, ref index);
            lineBuffer[index++] = ';';
            lineBuffer.Write3(i+2, ref index);
            lineBuffer[index++] = ';';
            lineBuffer.Write3(i+3, ref index);
            lineBuffer[index++] = ';';
            lineBuffer.Write3(i+4, ref index);
            lineBuffer[index++] = ';';
            lineBuffer.Write3(i+5, ref index);
            lineBuffer[index++] = ';';
            lineBuffer.Write3(i+6, ref index);
            lineBuffer[index++] = ';';
            streamWriter.WriteLine(lineBuffer, 0, index);
          }
        }
      }
    }
    
    
    public static void Write3(this char[] charArray, int i, ref int index) {
      if (i<0) {
        charArray[index++] = '-';
        i = -i;
      }
      int start = index;
    
      while (i>9) {
        charArray[index++] = (char)((i % 10) + '0');
        i /= 10;
      }
      charArray[index++] = (char)(i + '0');
      var end = index-1;
      while (end>start) {
        var temp = charArray[end];
        charArray[end--] = charArray[start];
        charArray[start++] = temp;
      }
    }
    
    public void WriteTo3(){
    var PathFileName=directoryInfo.FullName+@“\Test1.csv”;
    使用(var fileStream=newfilestream(路径文件名,FileMode.OpenOrCreate,FileAccess.ReadWrite,FileShare.None,bufferSize,FileOptions.S