IO操作 Excel导入与导出

IO(input,output)

IO操作:文件/文件夹操作与读写

文件夹:用来管理计算机文件的,每一个文件夹对应一块磁盘空间,它提供了指向对应磁盘空间的地址,它没有扩展名,不像文件用扩展名来标识文件的格式.

文件:

流:流是字节序列,可用于对后备存储进行读取和写入操作.。后备存储:磁盘、内存等。

Stream 支持读取和写入字节,所有表示流的类都继承自Stream类。流涉及三个基本操作

        读取:将数据从流传输到数据结构(如字节数组)中。

        写入:将数据从数据源传输到流。

        查找:对流中的当前位置进行查询和修改

IO命名空间常用类:

        System.IO命名空间里常用的类:

        File、FileInfo、Directory、DirectoryInfo、Path、

        StreamReader、StreamWriter、

        FileStream、MemoryStream、BufferedStream、

        BinaryReader、BinaryWriter、

        StringReader、StringWriter、

        TextReader、TextWriter、

        Stream、

 Path类操作

string path1 = @"D:\A";
string path2 = @"B\test.txt";
string str = Path.ChangeExtension(path2, "doc");  //  B\test.doc
str = Path.Combine(path1, path2);   //  D:\A\B\test.txt
str = Path.GetExtension(path2);   //  .txt
str = Path.GetFileName(path2);      //  test.txt
str = Path.GetFileNameWithoutExtension(path2);  //  test
bool b = Path.HasExtension(path2);      //   true

Directory、DirectoryInfo类操作

Directory:用于创建、移动、删除和枚举目录和子目录的静态类。DirectoryInfo:用于创建、移动、删除和枚举目录和子目录的一般类。两者使用其中一个即可。

static void Main(string[] args)
{
    string path = @"D:\A\B";
    if (!Directory.Exists(path))
        Directory.CreateDirectory(path);    //创建目录
    Directory.Delete(path);  //删除目录,该目录下没有子目录和文件,否则会报错
    Directory.Delete(path, true);  //删除目录,如果该目录下有子目录和文件也会一并删除
    Directory.Move(@"D:\A\B", @"D:\A2"); //先创建D:\A2目录,然后将B文件夹下的内容全部移动到D:\A2下,最后删除目录D:\A\B. 注意如果目录D:\A2在移动前存在会报错                                                

    string[] strs = Directory.GetDirectories(path);  //获取path目录下的子目录列表(一级子目录)
    string[] strs2 = Directory.GetFiles(path);  //获取path目录下的文件列表(一级文件)

    //递归遍历所有的目录和文件
    List<string> lists = new List<string>();
    GetChildDirectoriesAndFiles(path, lists);

}
//获取path目录下的子目录和文件列表,保存到lists里
private static void GetChildDirectoriesAndFiles(string path, List<string> lists)
{
    string[] dires = Directory.GetDirectories(path);
    string[] files = Directory.GetFiles(path);
    lists.AddRange(files);
    lists.AddRange(dires);
    foreach (var dire in dires)
    {
        GetChildDirectoriesAndFiles(dire, lists);
    }
}

File和FileInfo类操作

File:创建、移动、删除、复制和打开文件的静态类,并协助创建FileStream对象。

FileInfo:创建、移动、删除、复制和打开文件的一般类,并协助创建FileStream对象。

FileMode:打开文件的方式:Create   CreateNew   Append   Open   OpenOrCreate

FileAccess: 文件访问的权限:Read   Write   ReadWrite

文件的创建、移动、删除、复制:

string path = @"D:\A\B\TEST.txt";//path中路径@"D:\A\B"必须是真实存在的,否则下面的方法会报错
//File.Create会返回FileStream,后面再研究FileStream是如何操作的。
File.Create(path); //创建或者覆盖文件,如果文件不存在会创建,文件存在会覆盖
File.Create(path, 1024); //这个具有缓冲区的一会了解一下
File.Move(path, @"D:\A1\TEST1.txt"); //将指定文件移动到新位置,并且新创建的文件名为TEST1.txt。如果文件@"D:\A1\TEST1.txt"已存在会报错
File.Delete(path); 
File.Copy(path, @"D:\A1\TEST1.txt"); //将现有文件复制到新文件,不允许覆盖同名的文件,也就是@"D:\A1\TEST1.txt"文件在复制前就存在会报错
File.Copy(path, @"D:\A1\TEST1.txt",true); //将现有文件复制到新文件,允许覆盖同名的文件,也就是@"D:\A1\TEST1.txt"文件在复制前就存在不会报错,会对该文件进行覆盖
//追加文本
File.AppendAllText(path,"这是追加的一句话");//打开一个文件,向其中追加指定的字符串,然后关闭文件。如果文件不存在,将创建一个文件,然后将指定的字符串写入文件后关闭文件
File.AppendAllLines(path, new string[] { "Line1", "Line2", "Line2" });
//File.AppendText会返回StreamWriter,后面再研究StreamWriter是如何操作的。
File.AppendText(path);

文件的打开、读取和写入:        

string path = @"D:\A\B\TEST.txt";
//打开
FileStream fs = File.OpenRead(path);//打开现有文件。只读文件流。
fs = File.OpenWrite(path);//打开现有文件或创建一个新文件以进行写入。只写文件流

StreamReader sr = File.OpenText(path);//打开现有UTF-8文本文件以进行读取。只读文本流

fs = File.Open(path, FileMode.Open);//以读/写访问权限打开指定路径上的文件。读写文件流。FileMode.Open表示打开的是现有文件,如果文件不存在会报异常
fs = File.Open(path, FileMode.Create);//以读/写访问权限打开指定路径上的文件。读写文件流。FileMode.Create表示指定操作系统创建一个新的文件,如果该文件存在则会覆盖他。
fs = File.Open(path, FileMode.CreateNew);//以读/写访问权限打开指定路径上的文件。读写文件流。FileMode.CreateNew表示指定操作系统创建一个新的文件,如果该文件存在则异常。
fs = File.Open(path, FileMode.Append);//以读/写访问权限打开指定路径上的文件。读写文件流。FileMode.Append表示如果文件存在查找到文件的末尾,不存在创建一个新的文件,配合FileAccess.Write使用
fs = File.Open(path, FileMode.OpenOrCreate);//以读/写访问权限打开指定路径上的文件。读写文件流。FileMode.OpenOrCreate表示如果文件存在则打开,不存在则创建。

fs = File.Open(path, FileMode.OpenOrCreate, FileAccess.Read);//FileAccess可以对File.Open的读写权限进行限定,将其限定为Read、Write或ReadWrite

//读取
byte[] bytes = File.ReadAllBytes(path);//打开一个二进制文件,将文件内容读入字节数据,然后关闭该文件。
string s = System.Text.Encoding.UTF8.GetString(bytes);

s = File.ReadAllText(path);//打开文本文件,读取所有行,然后关闭该文件。
string[] ss=File.ReadAllLines(path);

//写入
File.WriteAllBytes(path,bytes);//创建一个新文件,向其中写入指定的字节数组,然后关闭文件。如果文件已存在则覆盖。

File.WriteAllText(path,"写入文本");//创建一个新文件,向其中写入指定的字符串,然后关闭文件。如果文件已存在则覆盖。

Stream

        这是一个抽象类,是所有流的抽象基类。提供了以字节的形式从流中读写内容StreamReaderStreamWriter帮助我们实现在流上读写字符串的功能。

常用的Stream的子类:

        1.MemoryStream:存储在内存中的字节流

        2.FileStream:存储在文件系统的字节流

        3.BufferedStream:为其他流提供缓冲的流。(此流也是字节流

Stream属性:

        CanRead 

        CanSeek

        CanWrite

        Length

        Position

Stream方法:

        Close

        Flush

        Read

        Seek

        Write

TextReader

        表示可读取有序字符序列的读取器。抽象类,不能直接进行实例化。此类实现了IDisposable接口,可以通过using进行释放。

        StreamReader派生自TextReader,以一种特定的编码字节流中读取字符,默认为UTF-8编码。默认情况下StreamReader不是线程安全的(为什么?)

        StringReader也派生自TextReader,实现从字符串(字符串流)中读取字符

TextReader方法:

        Close():关闭TextReader,并释放与它相关的所有的系统资源.

        Dispose():释放TextReader对象的所有资源。

        Peek():读取下一个字符而不更改读取器的状态和读取位置。

        Read():读取文本读取器的下一个字符并使读取位置提升一个字符.

        ReadLine():从文本读取器读取下一行字符并将数据作为字符串返回

        ReadToEnd():读取从文本读取器当前位置到结尾的所有字符并将它作为一个字符串返回

        Read(Char[],int32,int32):从当前读取器中读取指定数目的字符,并从指定索引开始将数据写入缓冲区

        ReadAsync(Char[],int32,int32):异步...

           

代码简单展示StreamReader的使用:

string path = @"D:\A\B\Test.txt";
TextReader tr = new StreamReader(path,Encoding.UTF8);
string ss=tr.ReadLine();//如果读取完所有的字符会返回null
while (ss!=null)
{
    Console.WriteLine(ss);
    ss=tr.ReadLine();
}

//其他读取方法ReadToEnd()和Read(char[],int32,int32);
ss=tr.ReadToEnd();//此时已经读取不到数据了,因为读取器的读取位置已经到末尾了,这里只是做个展示
//将数据读入缓冲区
char[] chars = new char[20];//这是字符,不是字节byte哦
//tr.Read(chars,0,chars.Length)是一个比较重要的方法,用于读取大型文件.
int n = tr.Read(chars,0,chars.Length); //n是读取的字符数,如果没有字符可读会返回0
while (n != 0)
{
    Console.WriteLine(new string(chars));//new string(chars)将字符数据转化成字符串,nice呀.
}
tr.Close();

TextWriter

        可以编写有序字符序列的编写器,抽象类,实现了IDisposable接口。

StreamWriterStringWriter派生自TextWriter,分别将字符写入字符串(字符串流)

StreamWriter:以一种特定的编码向流中写入字符

默认情况下:TextWriter不是线程安全的(为什么?)

TextWriter方法:

        Close()    Dispose()  (这两个具体的区别是什么?)

        Flush():清理当前编写器的所有缓冲区,并将缓冲数据写入基础设备.

        Write():(各种类型的数据)将字符写入[文本]流(各种类型的数据是什么意思)

        WriteLine():(各种类型的数据)将字符写入[文本]流,并跟上终止符.

        Write(Char[],int32,int32):

请注意:音频和视频的读取是以字节流读取的,以字符流读取是不能播放的.(个人观点:如果音频和视频读取和写入的编码是一致的话,也是可以播放的)

代码简单展示StreamWriter的使用: 

string path = @"D:\A\B\Test.txt";         //默认的编码就是UTF8,所以下面的Encoding.UTF8可以不写
TextWriter tw = new StreamWriter(path,true,Encoding.UTF8,4);//如果文件不存在会创建一个新的,true表示附加,false表示覆写
tw.Write("这是写入的字符串1");
tw.WriteLine("这是写入的字符串2");//这一句后面会跟个终止符
tw.WriteLine(123);
tw.WriteLine(true);//这就是所谓的写入各种类型的数据
tw.Flush(); //要想在关闭写入器之前就将流的缓冲区数据写入流(基础设备)需要使用Flush()
tw.Close(); //关闭写入器会将流的缓冲区中的数据写入流(基础设备)


string path2 = @"D:\A\B\Test2.txt";
using (TextReader tr2=new StreamReader(path))
{
    using (TextWriter tw2=new StreamWriter(path2))//如果文件不存在会自动创建,存在的话会对文件进行覆写
    {
        char[] chars = new char[20];
        while (true)
        {                   
            int n = tr2.Read(chars, 0, chars.Length);
            if (n == 0) break;
            tw2.Write(chars);//将数据写入到输出流的缓冲区中,并没有写入到流中
        }
    }
}

FileStream

        对文件系统中的文件进行进行读取、写入、打开和关闭,使用Read、Write、CopyTo、Flush方法来执行同步操作,或使用ReadAsyn、WriteAsyn、CopyToAsyn、FlushAsyn方法执行异步操作。使用异步方法来执行占用大量资源的文件操作不会阻止主线程。

构造函数:FileStream(string,FileMode,FileAccess,FileShare,Int32)

string:文件路径   FileMode:文件创建和打开的方式   FileAccess:文件访问的方式

FileShare:文件共享的方式   Int32:缓冲区大小

代码简单展示FileStream的使用:(同步方式)

string path1 = @"D:\A\B\Test.txt";     //此文件可以任意文件
string path2= @"D:\A\Test.txt";
using (FileStream fs1=new FileStream(path1,FileMode.Open,FileAccess.Read))
{
    using (FileStream fs2=new FileStream(path2,FileMode.OpenOrCreate,FileAccess.Write))  //注意如果文件存在会对文件进行覆写
    {
        byte[] bytes = new byte[1024];
        while (true)
        {
            int count = fs1.Read(bytes, 0, bytes.Length);//如果读取位置已经到达文件流(字节流)的末端,再次读取时会返回0
            if (count == 0) break;
            fs2.Write(bytes,0,bytes.Length);
        }
    }
}

代码简单展示FileStream的使用:(异步方式)

        在学完多线程的时候在看视频进行总结吧.

MemoryStream

MemoryStream 创建一个流,其后备存储为内存

内存流 数据以无符号字节数组的形式保存在内存中,系统可以直接访问这些封装的数据而不必读取磁盘文件。更加贴近底层数据,读取的效率更高(读取的速度更快,和文件流的主要区别)

内存流可以降低系统对临时缓冲区和临时文件的需要。(为什么?)

因此我们编程中常常用内存流作为中转,与其他流进行数据交换。(如利用MemoryStream操作文件,然后传给FileStream)

内存流到文件流的转换:将数据写入内存流,再转给文件流,再写入文件

代码简单展示MemoryStream的使用:(同步方式)

//实例化
MemoryStream ms = new MemoryStream();//初始化为0的可扩展容量
MemoryStream ms1 = new MemoryStream(1024);//初始化为1024的可扩展容量
byte[] data = new byte[1024 * 1024];
MemoryStream ms2 = new MemoryStream(data);//初始化为data的不可扩展容量

//操作
int count = ms.Read(data, 0, data.Length); //流中读取内容写入缓冲区
ms.Write(data, 0, data.Length);//缓冲区内容写入流
//ms.Seek(2,SeekOrigin.Begin);//设置流的位置(这个是干什么用的?算了估计不常用,就不了解了)

//中转应用
string path1 = @"D:\A\B\Test.txt";
string path2 = @"D:\A\Test.txt";
byte[] bytes = new byte[1024];
MemoryStream mid = new MemoryStream();
using (FileStream fr = new FileStream(path1, FileMode.Open, FileAccess.Read))
{
    while (true)
    {
        int n = fr.Read(bytes, 0, bytes.Length);
        if (n == 0) break;
        mid.Write(bytes,0,n);//将缓冲区bytes的数据写入流mid
        //(此处有个疑问:如果文件比较大,这样一直从文件流中读取数据往内存流中写入,内存流的空间越来越大不就出问题了吗?)
    }
}
using (FileStream fw=new FileStream(path2,FileMode.OpenOrCreate,FileAccess.Write))
{
            
    mid.WriteTo(fw);//将内存流中的内容全部写入另一个资料流(执行此句后内存流中的数据会依然存在)
    long l = fw.Length;
}

疑问:如果文件比较大,这样一直从文件流中读取数据往内存流中写入,内存流的空间越来越大不就出问题了吗?

BufferedStream

缓冲流,给另一个流添加一个缓冲区,以进行读写操作.

BufferedStream比StreamReader和StreamWriter的效率更高,特别是对于大文件。(弱弱的问一句为什么?

缓冲区是内存中用于缓冲数据的字节块, 缓冲数据能够减少对操作系统的调用次数,缓冲数据主要存储在缓冲区中。缓冲区提高读写性能。(想要真正理解这两句话感觉需要学习操作系统和计算机原理方面的知识)缓冲区可用于读取或写入,但不能同时使用这两种方法

注意:每次写入数据的大小如果大于buffer的大小,会导致BufferedStream的效率下降。

代码简单展示BufferedStream​​​​​​​的使用

string path = @"D:\A\B\Test.txt";
string pathTo = @"D:\Test.txt";
FileStream fr = new FileStream(path, FileMode.Open, FileAccess.Read);
FileStream fw = new FileStream(pathTo, FileMode.OpenOrCreate, FileAccess.Write);

byte[] bytes = new byte[1024];
using (BufferedStream br = new BufferedStream(fr, 1024))
using (BufferedStream bw = new BufferedStream(fw, 1024))
{
    int count = 0;
    while (true)
    {
        count=br.Read(bytes, 0, bytes.Length);//
        if (count == 0) break;
        bw.Write(bytes, 0, count);
    }
}
fr.Close();
fw.Close();

BinaryReader与BinaryWriter

二进制文件的读写器。

在字节级上操作文件,他们可以将一个数字或者字符按指定个数字节写入,也可以一次读取指定个数字节转化成字符或数字。

BinaryReader:用特定的编码将基元数据类型读作二进制值

BinaryWriter:以二进制形式将基元类型写入流,并支持用特定的编码写入字符串

BinaryReader有ReadByte、ReadBytes、Read方法,它们读出来的是原生态的二进制值,像ReadInt32,ReadDouble他们相当于多了一步处理,将二进制值转换成相应的数据类型。

代码简单展示BinaryReader与BinaryWriter的使用

string path = @"D:\A\B\Test.txt";
string pathTo = @"D:\Test.txt";
FileStream fr = new FileStream(path, FileMode.Open, FileAccess.Read);
FileStream fw = new FileStream(pathTo, FileMode.OpenOrCreate, FileAccess.Write);

byte[] bytes = new byte[1024*1024];
using (BinaryReader br = new BinaryReader(fr))
using (BinaryWriter bw = new BinaryWriter(fw))
{
    int count = 0;
    while (true)
    {
        count=br.Read(bytes, 0, bytes.Length);//
        if (count == 0) break;
        bw.Write(bytes, 0, count);
    }
}
fr.Close();
fw.Close();
string path = @"D:\A\B\Test.txt";

using (FileStream fw = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write))     
using (BinaryWriter bw = new BinaryWriter(fw))
{
    bw.Write("abcde");
    bw.Write(123);
    bw.Write(true);
    bw.Write(123.789M);//
    bw.Flush();
}
using (FileStream fr = new FileStream(path, FileMode.Open, FileAccess.Read))
using (BinaryReader br = new BinaryReader(fr))
{

    //要按照上面的写入顺序进行读取,如果顺序不一致会报错。
    string str = br.ReadString();
    int num = br.ReadInt32();
    bool flag = br.ReadBoolean();
    decimal dec = br.ReadDecimal();
}

Excel导入与导出

导入导出Excel的方式:

        1.Office组件方式(速度慢、效率低、问题多--摒弃)

        2.Ado.Net Oledb提供程序获取Excel数据到DataTable,StreamWriter将根据DataTable拼接成的字符串数据写入Excel

        3.基于NPOI组件读写Excel.

第一种方式我就不总结了,我主要总结第二和第三种导入方式.

Excel导入与导出--第二种方式:

Ado.Net Oledb提供程序导入:

这种导入方式,类似于将数据库中的一张表填充到DataTable中.

这种方式加载速度快,但受版本限制,不同版本的Excel文件,连接字符串有差别.

如果是.xls,即07之前的版本,连接字符串:

        strConn=@"Provider=Microsoft.Jet.OLEDB.4.0;Data Source="+excel_path+";Extended Properties='Excel 8.0;HDR=YES;IMEX=1'";  (HDR:header row   IMEX:import export mode)

如果是.xlsx,即07之后的版本,连接字符串为:(注意:07之前的版本也可以使用此连接字符串)

        strConn=@"Provider=Microsoft.ACE.OLEDB.12.0;Data Source="+excel_path+";Extended Properties='Excel 12.0;HDR=YES;IMEX=1'";

        其中excel_path是Excel文件路径;HDR=YES表示第一行是标题,No表示第一行是数据,不是标题;IMEX=0表示Excel只能用于写入, =1只能用于读取, =2读取都可以.

代码简单展示Ado.Net Oledb提供程序导入Excel的使用

//其中FileUpload1是webform框架下web表单中的一个FileUpload控件的ID
DataTable dtData = new DataTable();
if (FileUpload1.HasFile == false)
{
    throw new Exception("请先选择上传的文件");
}
else
{
    string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Temp");
    string fileName = DateTime.Now.ToString("yyyyMMddHHmmss"); //给文件FileUpload1.FileName另外命名为不带.xlsx后缀的DateTime.Now.ToString("yyyyMMddHHmmss")
    string path = Path.Combine(dir, fileName);
    if (!Directory.Exists(dir))
    {
        Directory.CreateDirectory(dir);
    }
    FileUpload1.SaveAs(path);//先将文件上传到服务器

    string strcon = "";
    if (Path.GetExtension(FileUpload1.FileName) == ".xls")
    {
        strcon = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + path + ";Extended Properties='Excel 8.0;HDR=YES;IMEX=1'";
    }
    else if (Path.GetExtension(fileName) == ".xlsx")//如果扩展名是.xls也可以用下面的连接字符串
    {
        strcon = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + path + ";Extended Properties='Excel 12.0;HDR=YES;IMEX=1'";
    }
    else
    {
        throw new Exception("请上传Execel格式的文件");
    }

    using (OleDbConnection con = new OleDbConnection(strcon))
    {
        con.Open();
        DataTable dtNames = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "Table" });
        string sheetName = dtNames.Rows[0][2].ToString();
        string sql = "select * from [" + sheetName + "]";  //注意这里的表名一定要用中括号括起来,不然会报错.获取到的表名竟然都有个$符号后缀
        OleDbDataAdapter ap = new OleDbDataAdapter(sql, con);
        ap.Fill(dtData);
    }
    File.Delete(path);//获取到Excel里面的数据后,将服务器上的Excel附件删掉
}

代码简单展示使用StreamWriter将根据DataTable拼接成的字符串数据写入Excel

#region  获取Excel里面的数据到DataTable
//其中FileUpload1是webform框架下web表单中的一个FileUpload控件的ID
DataTable dtData = new DataTable();
if (FileUpload1.HasFile == false)
{
    throw new Exception("请先选择上传的文件");
}
else
{
    string dir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Temp");
    string fileName = DateTime.Now.ToString("yyyyMMddHHmmss"); //给文件FileUpload1.FileName另外命名为不带.xlsx后缀的DateTime.Now.ToString("yyyyMMddHHmmss")也是可以的
    string path = Path.Combine(dir, fileName);
    if (!Directory.Exists(dir))
    {
        Directory.CreateDirectory(dir);
    }
    FileUpload1.SaveAs(path);//先将文件上传到服务器

    string strcon = "";
    if (Path.GetExtension(FileUpload1.FileName) == ".xls")
    {
        strcon = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + path + ";Extended Properties='Excel 8.0;HDR=YES;IMEX=1'";
    }
    else if (Path.GetExtension(FileUpload1.FileName) == ".xlsx")
    {
        strcon = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + path + ";Extended Properties='Excel 12.0;HDR=YES;IMEX=1'";
    }
    else
    {
        throw new Exception("请上传Execel格式的文件");
    }

    using (OleDbConnection con = new OleDbConnection(strcon))
    {
        con.Open();
        DataTable dtNames = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, new object[] { null, null, null, "Table" });
        string sheetName = dtNames.Rows[0][2].ToString();
        string sql = "select * from [" + sheetName + "]";  //注意这里的表名一定要用中括号括起来,不然会报错.获取到的表名竟然都有个$符号后缀
        OleDbDataAdapter ap = new OleDbDataAdapter(sql, con);
        ap.Fill(dtData);
    }
    File.Delete(path);//获取到Excel里面的数据后,将服务器上的Excel附件删掉
}
#endregion

#region 获取DataTable里面的数据到Excel
DataTable dt = dtData;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < dt.Columns.Count; i++)
{
    sb.Append(dt.Columns[i].ColumnName + "\t");//  \t代表制表符,其作用是在Excel中向右移动一个表格
}
sb.Append(Environment.NewLine);//  \n可以吗?  答:也是可以的 
for (int i = 0; i < dt.Rows.Count; i++)
{
    for (int j = 0; j < dt.Columns.Count; j++)
    {
        sb.Append(dt.Rows[i][j] + "\t");
    }
    sb.Append("\n");
}
string path2 = @"D:\A\222.xls";//使用StreamWriter写入Excel数据,Excel文件格式必须是.xls,才能正常打开文件。
using (StreamWriter sw = new StreamWriter(path2, false, Encoding.Default))//编码使用Encoding.UTF-8 "\t"会失去右移一格的作用,但是能解决繁体写入Excel乱码的问题。编码最好使用Encoding.Default,繁体既不会乱码,"\t"又不会失去右移一格的作用。
{
    sw.Write(sb.ToString());
    //sw.Flush();
}
#endregion

Excel导入与导出--第三种方式:

基于NPOI组件读写Excel.

NPOI是指构建在POI 3.x版本之上的一个程序,NPOI可以在没有安装Office的情况下对Excel或Word文档进行读写操作,包含了大部分Excel的特性(单元格样式、数据格式、公式等)。

NPOI是一个开源的C#读写Excel、Word等微软OLE2组件文档的项目。

NPOI是POI项目的.NET版本。POI是一个开源的Java读写Excel、Word等微软OLE2组件文档的项目。

使用NPOI的优势:

        1.您可以免费使用该框架

        2.包含了大量的Excel特性(单元格样式,数据格式,公式等)

        3.支持处理的文件格式包括.xls, .xlsx, .docx

        4.采用面向接口的设计架构(可以查看NPOI.SS的命名空间)

        5.同时支持文件的导入和导出

        6.你不需要在服务器上安装微软的Office,避免了版权问题。(OLEDB数据提供程序是需要在服务器上安装微软的Office)

相关程序集:

使用NPOI进行Excel的导入和导出:

在使用前需要引入NPOI程序集,从下图可以看出NPOI程序集已经包含了NPOI.OOXML程序集所含的功能。

简单代码展示

public class ExcelHelper
{
    public ExcelHelper()
    {
        //
        // TODO: 在此处添加构造函数逻辑
        //
    }
    /// <summary>
    /// 获取Excel中的数据到DataTable
    /// </summary>
    /// <param name="filePath">Excel文件路径</param>
    /// <param name="sheetName">Excel中某个sheet表的名称</param>
    /// <param name="isColumnName">sheet表中第一行是否是列名称</param>
    /// <returns></returns>
    public static DataTable DataFromExcelToDataTable(string filePath, string sheetName = "", bool isColumnName = true)
    {
        DataTable dt = new DataTable();
        //1.创建工作簿
        IWorkbook workbook = null;
        string fileExt = Path.GetExtension(filePath).ToLower();
        using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
        {
            if (fileExt == ".xlsx")
            {
                workbook = new XSSFWorkbook(fs);
            }
            else if (fileExt == ".xls")
            {
                workbook = new HSSFWorkbook(fs);
            }
        }
        //2.获取工作表
        ISheet sheet = null;
        if (!string.IsNullOrEmpty(sheetName))
        {
            sheet = workbook.GetSheet(sheetName);//获取指定名称的sheet表
            if (sheet == null)
                sheet = workbook.GetSheetAt(0);//获取第一个工作表
        }
        else
        {
            sheet = workbook.GetSheetAt(0);
        }

        //3.获取表头  sheet.FirstRowNum为0
        IRow firstRow = sheet.GetRow(sheet.FirstRowNum);
        int startRow = 0;
        if (isColumnName)
        {//如果第一行是列名
            startRow = sheet.FirstRowNum + 1;
            for (int i = firstRow.FirstCellNum; i < firstRow.LastCellNum; i++)//注意:header.FirstCellNum是header行第一个单元格的索引,即为0;header.LastCellNum是header行有多少个单元格,并不是索引
            {
                //4.获取指定索引的单元格
                ICell cell = firstRow.GetCell(i);
                if (cell != null)
                {
                    string cellValue = cell.ToString();//cell.ToString()是单元格的值
                    if (cellValue != null)
                    {
                        DataColumn col = new DataColumn(cellValue);
                        dt.Columns.Add(col);
                    }
                    else
                    {
                        DataColumn col = new DataColumn();
                        dt.Columns.Add(col);
                    }
                }
            }
        }
        else
        {
            for (int i = firstRow.FirstCellNum; i < firstRow.LastCellNum; i++)//注意:firstRow.FirstCellNum是firstRow行第一个单元格的索引,即为0;firstRow.LastCellNum是header行有多少个单元格,并不是索引
            {
                DataColumn col = new DataColumn();
                dt.Columns.Add(col);
            }
        }

        for (int i = startRow; i <= sheet.LastRowNum; i++)//sheet.LastRowNum是sheet最后一行的索引
        {
            IRow row = sheet.GetRow(i);
            if (row == null)
            {
                continue;
            }
            DataRow dr = dt.NewRow();//DataRow类没有包含0个参数的构建函数,所以这里用dt.NewRow()创建DataRow实例,而不是New DataRow(),而且dt会根据dt.Columns的值决定DataRow实例有几列
            for (int j = row.FirstCellNum; j < row.LastCellNum; j++)
            {
                if (row.GetCell(j) != null)
                {
                    dr[j] = row.GetCell(j).ToString();
                }

            }
            dt.Rows.Add(dr);
        }
        return dt;
    }

    /// <summary>
    /// 获取DataTable中的数据到Excel中
    /// </summary>
    /// <param name="dt"></param>
    /// <param name="filePath">导出的Excel路径</param>
    /// <param name="sheetName">在Excel中创建的sheet表的名称</param>
    public static void DataFromDataTableToExcel(DataTable dt, string filePath,string sheetName="sheet1")
    {
        string strExt = Path.GetExtension(filePath);
        //1.创建一个工作簿实例
        IWorkbook workbook = null;
        if (strExt==".xls")
        {
            workbook = new HSSFWorkbook();
        }
        else if(strExt == ".xlsx")
        {
            workbook = new XSSFWorkbook();
        }
        else
        {
            return;
        }
        //创建一个表实例
        ISheet sheet = workbook.CreateSheet(sheetName);
        int rowIndex = 0;
        //设置列名
        if (dt.Columns.Count > 0)
        {
            IRow row = sheet.CreateRow(rowIndex);//创建第一行
                
            for (int i=0;i<dt.Columns.Count;i++)
            {
                ICell cell = row.CreateCell(i);//创建单元格
                cell.SetCellValue(dt.Columns[i].ColumnName);
            }
        }
        //添加数据
        if (dt.Rows.Count > 0)
        {
            for (int i=0;i<dt.Rows.Count;i++)
            {
                ++rowIndex;
                IRow row = sheet.CreateRow(rowIndex);
                for(int j = 0; j < dt.Columns.Count; j++)
                {
                    ICell cell = row.CreateCell(j);
                    cell.SetCellValue(dt.Rows[i][j].ToString());
                }
            }
        }
        for(int i = 0; i < dt.Columns.Count; i++)
        {
            sheet.AutoSizeColumn(i);//自适应单元格大小
        }

        using (FileStream fs=new FileStream(filePath, FileMode.Create, FileAccess.Write))
        {
            workbook.Write(fs);//将工作簿写入到流
        }

        //导出成功
    }
}

  • 0
    点赞
  • 4
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:1024 设计师:我叫白小胖 返回首页
评论

打赏作者

宋韩

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值