一种快速生成CSV的方法

2024-09-08 07:28
文章标签 方法 快速 生成 csv 一种

本文主要是介绍一种快速生成CSV的方法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

事情是这个样子的

在QQ群在聊把如何100万数据导出成CSV文件?会不会很慢?

俺回了一句“现在的机器性能好,没啥问题”。

然后大家开始谈论机器的配置了。哎,俺的机器配置有点差。

然后俺就进行了一个测试。

测试数据

数据定义

        public struct Rec
        {
            public int v1;
            public int v2;
        }
        private Rec[] Data;

        这里为啥不用class呢,因为这个场景struct 更简单。

测试数据生成

            Data = new Rec[200*10000];
            Random r = new Random(DateTime.Now.Millisecond);
            for(int i= 0;i < Data.Length;i++)
            {
                Data[i].v1 = r.Next();
                Data[i].v2 = r.Next();
            }

测试数据200万行,每行2个数字。

测试结果

测试程序为 .NET Framework 4.8 64 Release 版本

如果用.Net 8 版本进行测试,除了方法3之外,会快一些。方法3速度基本不变。

 开始写代码

方法一

这是最最普通的生成CSV的方法,就是一个一个数据的写入到CSV中。没啥可说的。就是用来当做参照物的,耗时0.536秒

            string fn = @"1.csv";  FileStream fs = new FileStream(fn, FileMode.Create);DateTime t1 = DateTime.Now;byte[] buff;byte[] buff_rn = ASCIIEncoding.ASCII.GetBytes("\r\n");byte[] buff_split = ASCIIEncoding.ASCII.GetBytes(",");string s ="v1,v2";buff = ASCIIEncoding.ASCII.GetBytes(s);fs.Write(buff,0,buff.Length);foreach (Rec i in Data){fs.Write(buff_rn, 0, buff_rn.Length); s = i.v1.ToString();buff = ASCIIEncoding.ASCII.GetBytes(s);fs.Write(buff, 0, buff.Length);fs.Write(buff_split, 0, buff_split.Length);s = i.v2.ToString();buff = ASCIIEncoding.ASCII.GetBytes(s);fs.Write(buff, 0, buff.Length);  }fs.Flush();fs.Close();fs.Dispose();DateTime t2 = DateTime.Now;label1.Text=("输出"+Data.Length.ToString()+"行CSV文件,耗时" +((t2 - t1).TotalMilliseconds/1000).ToString("0.000")+"秒");

方法二

在方法1上加了一个8M的缓存。其实这种写法在其他语言(c++、delphi 等)还有效果,C#效果不大,因为C#的文件流本身有优惠。耗时0.503秒

            string fn = @"2.csv"; int ms_size = 8 * 1024 * 1024;byte[] ms=new byte[ms_size + 1024];int ms_position = 0;FileStream fs = new FileStream(fn, FileMode.Create);DateTime t1 = DateTime.Now;byte[] buff;byte[] buff_rn = ASCIIEncoding.ASCII.GetBytes("\r\n");byte[] buff_split = ASCIIEncoding.ASCII.GetBytes(",");string s = "v1,v2";buff = ASCIIEncoding.ASCII.GetBytes(s);Array.Copy(buff, 0, ms, ms_position, buff.Length);ms_position+= buff.Length;foreach (Rec i in Data){Array.Copy(buff_rn, 0, ms, ms_position, buff_rn.Length);ms_position += buff_rn.Length; s = i.v1.ToString();buff = ASCIIEncoding.ASCII.GetBytes(s); Array.Copy(buff, 0, ms, ms_position, buff.Length);ms_position += buff.Length;Array.Copy(buff_split, 0, ms, ms_position, buff_split.Length);ms_position += buff_split.Length;s = i.v2.ToString();buff = ASCIIEncoding.ASCII.GetBytes(s);Array.Copy(buff, 0, ms, ms_position, buff.Length);ms_position += buff.Length;if (ms_position > ms_size){ fs.Write(ms,0, ms_position); ms_position = 0;}}if (ms_position > 0){fs.Write(ms, 0, ms_position);}fs.Flush();fs.Close();fs.Dispose();DateTime t2 = DateTime.Now;label2.Text = ("输出" + Data.Length.ToString() + "行CSV文件,耗时" + ((t2 - t1).TotalMilliseconds / 1000).ToString("0.000") + "秒");

优化

我们先分析一下,生成CSV中,主要的计算是啥?在这个测试上主要计算就是,整型转字符串。执行了400万次(200万行,每行2个数字)整型转字符串。那么我们就先测试一下执行了400万次整型转字符串多少时间。

            string s = "";DateTime t1 = DateTime.Now;foreach (Rec i in Data){s = i.v1.ToString();s = i.v2.ToString();}DateTime t2 = DateTime.Now;label4.Text = ( (Data.Length*2).ToString() + "次ToString,耗时" + ((t2 - t1).TotalMilliseconds / 1000).ToString("0.000") + "秒");

耗时是0.265秒

那么我们就像从 整型转字符串 开始优化。

方法三

  使用了 IntToString 函数,速度优化到了 耗时0.176秒。优化到这里出现了一个有趣的数据:

 只执行 整型转字符串 耗时是0.265秒,方法三的速度比只 整型转字符串 还快。为什么呢?

因为在 方法三 中,其实没有使用字符串,取消中间商赚差价。

           string fn = @"3.csv";int ms_size = 8 * 1024 * 1024;byte[] ms = new byte[ms_size + 1024];int ms_position = 0;FileStream fs = new FileStream(fn, FileMode.Create);DateTime t1 = DateTime.Now;byte[] buff;byte[] buff_rn = ASCIIEncoding.ASCII.GetBytes("\r\n");byte[] buff_split = ASCIIEncoding.ASCII.GetBytes(",");string s = "v1,v2";buff = ASCIIEncoding.ASCII.GetBytes(s);Array.Copy(buff, 0, ms, ms_position, buff.Length);ms_position += buff.Length;foreach (Rec i in Data){Array.Copy(buff_rn, 0, ms, ms_position, buff_rn.Length);ms_position += buff_rn.Length;IntToString(i.v1,ref ms, ref ms_position); Array.Copy(buff_split, 0, ms, ms_position, buff_split.Length);ms_position += buff_split.Length;IntToString(i.v2, ref ms, ref ms_position);if (ms_position > ms_size){fs.Write(ms, 0, ms_position);ms_position = 0;}}if (ms_position > 0){fs.Write(ms, 0, ms_position);}fs.Flush();fs.Close();fs.Dispose();DateTime t2 = DateTime.Now;label3.Text = ("输出" + Data.Length.ToString() + "行CSV文件,耗时" + ((t2 - t1).TotalMilliseconds / 1000).ToString("0.000") + "秒");

这个代码的功能  其实就个 int.ToString。 看上去下面这个代码有点拉胯,像是那种故意搞笑的代码,是的确实拉胯,但是它确实快。

        public static void IntToString(int n,ref byte[] buff,ref int position){int len = 0;int len_inc = 16;int p = position+ len_inc;do{buff[p] = (byte)((n % 10)+48);p += 1;len += 1;}while ((n /= 10) != 0);p -= 1;for (int i = 0; i < len;i++){buff[position]= buff[p];position += 1;p -= 1;} }

其他

Release 版本 和 Debug版本差别很大,例如 方法三 的Debug版 速度并不快,但是用Release 版本,方法3就明显快。

使用.Net 8 版本进行测试,结果如下

.NET Framework 4.8.Net 8对比
方法10.5380.257.Net 8 快很多
方法20.5030.190.Net 8 快很多
方法30.1760.178两者差不多
整型转字符串0.2650.075.Net 8 快很多

上面的数据进行时测试,不是很准确。但是可以看出 :

.Net 8 在 整型转字符串 上要快很多。方法三因为没有用 .ToString() 所以 速度在.NET Framework 4.8  和  .Net 8 下差不多。

这篇关于一种快速生成CSV的方法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1147491

相关文章

Java中读取YAML文件配置信息常见问题及解决方法

《Java中读取YAML文件配置信息常见问题及解决方法》:本文主要介绍Java中读取YAML文件配置信息常见问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录1 使用Spring Boot的@ConfigurationProperties2. 使用@Valu

Java 方法重载Overload常见误区及注意事项

《Java方法重载Overload常见误区及注意事项》Java方法重载允许同一类中同名方法通过参数类型、数量、顺序差异实现功能扩展,提升代码灵活性,核心条件为参数列表不同,不涉及返回类型、访问修饰符... 目录Java 方法重载(Overload)详解一、方法重载的核心条件二、构成方法重载的具体情况三、不构

SQL中如何添加数据(常见方法及示例)

《SQL中如何添加数据(常见方法及示例)》SQL全称为StructuredQueryLanguage,是一种用于管理关系数据库的标准编程语言,下面给大家介绍SQL中如何添加数据,感兴趣的朋友一起看看吧... 目录在mysql中,有多种方法可以添加数据。以下是一些常见的方法及其示例。1. 使用INSERT I

Python中反转字符串的常见方法小结

《Python中反转字符串的常见方法小结》在Python中,字符串对象没有内置的反转方法,然而,在实际开发中,我们经常会遇到需要反转字符串的场景,比如处理回文字符串、文本加密等,因此,掌握如何在Pyt... 目录python中反转字符串的方法技术背景实现步骤1. 使用切片2. 使用 reversed() 函

Python中将嵌套列表扁平化的多种实现方法

《Python中将嵌套列表扁平化的多种实现方法》在Python编程中,我们常常会遇到需要将嵌套列表(即列表中包含列表)转换为一个一维的扁平列表的需求,本文将给大家介绍了多种实现这一目标的方法,需要的朋... 目录python中将嵌套列表扁平化的方法技术背景实现步骤1. 使用嵌套列表推导式2. 使用itert

Python使用pip工具实现包自动更新的多种方法

《Python使用pip工具实现包自动更新的多种方法》本文深入探讨了使用Python的pip工具实现包自动更新的各种方法和技术,我们将从基础概念开始,逐步介绍手动更新方法、自动化脚本编写、结合CI/C... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核

在Linux中改变echo输出颜色的实现方法

《在Linux中改变echo输出颜色的实现方法》在Linux系统的命令行环境下,为了使输出信息更加清晰、突出,便于用户快速识别和区分不同类型的信息,常常需要改变echo命令的输出颜色,所以本文给大家介... 目python录在linux中改变echo输出颜色的方法技术背景实现步骤使用ANSI转义码使用tpu

Conda与Python venv虚拟环境的区别与使用方法详解

《Conda与Pythonvenv虚拟环境的区别与使用方法详解》随着Python社区的成长,虚拟环境的概念和技术也在不断发展,:本文主要介绍Conda与Pythonvenv虚拟环境的区别与使用... 目录前言一、Conda 与 python venv 的核心区别1. Conda 的特点2. Python v

Spring Boot中WebSocket常用使用方法详解

《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消... 目录一、WebSocket基础概念1.1 什么是WebSocket1.2 WebSocket与HTTP

SQL Server配置管理器无法打开的四种解决方法

《SQLServer配置管理器无法打开的四种解决方法》本文总结了SQLServer配置管理器无法打开的四种解决方法,文中通过图文示例介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录方法一:桌面图标进入方法二:运行窗口进入检查版本号对照表php方法三:查找文件路径方法四:检查 S