C#成神之路<17> C#使用磁盘数据文件(1)

1、目录
文件的输入和输出使用的命名空间:using System.IO。

(1)DriveInfo类
方法和属性:
GetDrives():返回一个包括计算机所有逻辑驱动器的数组。
AvaliableFreeSpace:给出一个驱动器上的自由磁盘空间数量(以字节表示)。
DriveFormat:返回驱动器的格式(NTFS或者FAT32)。
DriveType:返回驱动器的类型(固定,可移动,RAM等)。
Name:给出驱动器的名称。
TotalFreeSpace:给出驱动器上未用空间数量。
TotalSize:给出驱动器的数量。

(2)Directory类

方法和属性:
CreateDirectory():创建给定路径名的目录。
Delete:删除指定目录。
Exists():确定一个个指定目录是否存在。
GetCreationTime():返回创建目录的时间,类型为DateTime。
GetCurrentDirectory():获得应用程序的当前工作目录。
GetFiles():返回给定目录的文件名。
GetLastAccessTime():返回上次访问目录的日期和时间。
GetLastWriteTime():返回上次写入目录的日期和时间。
GetParent():返回给定目录的父目录。
Move():将文件或者目录移动到一个新的位置。
SetCreationTime():设置文件或者目录的创建时间。
SetLastWriteTime():设置上次写入文件或目录的日期和时间。

(3)DirectoryInfo()类:

方法和属性:
Create():创建目录。
CreatSubdirectory():创建子目录。
Delete():删除给定目录。
Equals():比较目录对象。
GetDirectories():返回当前目录的子目录。
GetFiles():返回当前目录中的文件。(这是一个重载方法,这样更加便于搜索。)
GetFileSystemInfos():返回文件和子目录的强类型FileSystemInfo。
MoveTo():将DirectoryInfo对象的内容移动到一个指定路径。
Exists:返回一个值,指示某个目录是否存在。
Extension:返回一个字符串,表示文件名的扩展名的内容。
FullName:返回目录或者路径的完整路径。

示例程序:
程序列出目录的表示方法和属性:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Collections;


    class clsDirectory
    {
        const string TAB = " ";
        static private int visits; 
        //跟踪任意给定时刻我们沿着这颗目录树走了多远。
        //也用来锁紧显示在listbox对象中的目录名。
        private int dirCounter;
        //读取文件的数量
        public clsDirectory()
        {
            dirCounter = 1;
        }
        //默认构造函数
        public int DirectoryCount
        {
            get {
                return dirCounter;
            }
            //取值函数
        }
        //传递了三个参数:一个DirctoryInfo对象,一个跟踪我们在目录结构
        //中的何处整数。一个存储目录名的变量ArrayList。
        public int ShowDirectory(DirectoryInfo curDir,int inLevel,ArrayList dirs) {
            int i;
            string indent = " ";
            try
            {
                for (i = 0; i < visits; i++)
                {
                    indent += TAB;
                }
                //方便文件打出的时候的缩进。
                dirs.Add(indent + curDir.Name);
                visits++;

                foreach (DirectoryInfo subDir in curDir.GetDirectories())//返回当前目录的子目录。
                {
                    dirCounter++;
                    //保持了读取目录的数量
                    ShowDirectory(subDir, visits, dirs);
                    //FileInfo[] files = subDir.GetFiles();
                  //要得到一个给定子目录中的文件列表。
                }
                visits--;

                if (indent.Length > 0)
                {
                    indent.Substring(0, indent.Length - TAB.Length);
                }
            }
            catch(Exception ex)
            {
                return - 1;
            }
            return dirCounter;
        }
    }

以上是类的编写,中心思想就是列出路径,并且返回的有多少个文件。
具体说明用注释形式标出。
下面给出frmMain类的代码:

using System;
using System.Windows.Forms;
using System.IO;
using System.Collections;
public class frmMain : Form
{
    private Label label1;
    private TextBox txtStartingPath;
    private Button btnList;
    private Button btnClose;
    private Label lblDriveInfo;
    private ListBox lstDirectories;
    #region Windows code
    private void InitializeComponent()
    {
            this.label1 = new System.Windows.Forms.Label();
            this.txtStartingPath = new System.Windows.Forms.TextBox();
            this.btnList = new System.Windows.Forms.Button();
            this.btnClose = new System.Windows.Forms.Button();
            this.lblDriveInfo = new System.Windows.Forms.Label();
            this.lstDirectories = new System.Windows.Forms.ListBox();
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
            this.label1.Location = new System.Drawing.Point(23, 25);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(100, 23);
            this.label1.TabIndex = 0;
            this.label1.Text = "Starting directory:";
            this.label1.TextAlign = System.Drawing.ContentAlignment.TopRight;
            // 
            // txtStartingPath
            // 
            this.txtStartingPath.Location = new System.Drawing.Point(139, 27);
            this.txtStartingPath.Name = "txtStartingPath";
            this.txtStartingPath.Size = new System.Drawing.Size(342, 21);
            this.txtStartingPath.TabIndex = 1;
            // 
            // btnList
            // 
            this.btnList.Location = new System.Drawing.Point(23, 69);
            this.btnList.Name = "btnList";
            this.btnList.Size = new System.Drawing.Size(75, 23);
            this.btnList.TabIndex = 2;
            this.btnList.Text = "List";
            this.btnList.UseVisualStyleBackColor = true;
            this.btnList.Click += new System.EventHandler(this.btnList_Click);
            // 
            // btnClose
            // 
            this.btnClose.Location = new System.Drawing.Point(406, 69);
            this.btnClose.Name = "btnClose";
            this.btnClose.Size = new System.Drawing.Size(75, 23);
            this.btnClose.TabIndex = 3;
            this.btnClose.Text = "Close";
            this.btnClose.UseVisualStyleBackColor = true;
            this.btnClose.Click += new System.EventHandler(this.button2_Click);
            // 
            // lblDriveInfo
            // 
            this.lblDriveInfo.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
            this.lblDriveInfo.Location = new System.Drawing.Point(23, 108);
            this.lblDriveInfo.Name = "lblDriveInfo";
            this.lblDriveInfo.Size = new System.Drawing.Size(455, 23);
            this.lblDriveInfo.TabIndex = 4;
            this.lblDriveInfo.Text = " ";
            this.lblDriveInfo.Click += new System.EventHandler(this.label2_Click);
            // 
            // lstDirectories
            // 
            this.lstDirectories.FormattingEnabled = true;
            this.lstDirectories.ItemHeight = 12;
            this.lstDirectories.Location = new System.Drawing.Point(23, 150);
            this.lstDirectories.Name = "lstDirectories";
            this.lstDirectories.Size = new System.Drawing.Size(455, 316);
            this.lstDirectories.TabIndex = 5;
            this.lstDirectories.SelectedIndexChanged += new System.EventHandler(this.listBox1_SelectedIndexChanged);
            // 
            // frmMain
            // 
            this.ClientSize = new System.Drawing.Size(507, 499);
            this.Controls.Add(this.lstDirectories);
            this.Controls.Add(this.lblDriveInfo);
            this.Controls.Add(this.btnClose);
            this.Controls.Add(this.btnList);
            this.Controls.Add(this.txtStartingPath);
            this.Controls.Add(this.label1);
            this.Name = "frmMain";
            this.ResumeLayout(false);
            this.PerformLayout();

    }
    #endregion
    //#region是C# 预处理器指令。 
    //#region 使您可以在使用 Visual Studio
    //代码编辑器的大纲显示功能时指定可展开或折叠的代码块。
    public frmMain()
    {
        InitializeComponent();
    }

    public static void Main()
    {
        frmMain main = new frmMain();
        Application.Run(main);
    }

    private void button2_Click(object sender, EventArgs e)
    {
        Close();
    }

    private void listBox1_SelectedIndexChanged(object sender, EventArgs e)
    {

    }
    //辅助方法,收集关于用户输入的磁盘驱动器的统计信息。
    private void ShowDriveInfo()
    {
        int pos;
        long driveBytes;
        string buff;

        try
        {
            pos = txtStartingPath.Text.IndexOf('\\');
            buff = txtStartingPath.Text.Substring(0, pos);
            DriveInfo myDrive = new DriveInfo(@buff);
            //将驱动器的名称传递给该对象的构造函数。
            driveBytes = myDrive.TotalSize / 1000000;
            lblDriveInfo.Text = "Drive " + buff + " has " + driveBytes.ToString() + " MBbytes, with "
                + myDrive.TotalFreeSpace / 1000000;
        }
        catch
        {
            txtStartingPath.Text = " ";
        }
    }



    private void btnList_Click(object sender, EventArgs e)
    {
        string startingPath;
        int count;
        int i;
        ArrayList dirs = new ArrayList();

        startingPath = @txtStartingPath.Text;

        try
        {
            DirectoryInfo myDirInfo = new DirectoryInfo(startingPath);
            if (myDirInfo.Exists == false)
            {
                MessageBox.Show("Cannot find diretory.re-enter", "Directory not found", MessageBoxButtons.OK, MessageBoxIcon.Stop);
                txtStartingPath.Focus();
                return;
            }
            clsDirectory myDirs = new clsDirectory();

            ShowDriveInfo();

            lstDirectories.Items.Clear();

            count = myDirs.ShowDirectory(myDirInfo, 0, dirs);
            for (i = 0; i < dirs.Count; i++)
            {
                lstDirectories.Items.Add(dirs[i]);
            }
            this.Text = "Directories found" + count.ToString();

        }
        catch(Exception ex)
        {
            MessageBox.Show("Error: "+ex.Message);
            return;
        }
    }

    private void label2_Click(object sender, EventArgs e)
    {

    }
}

以上是作为目录类的相关使用方法。

2、file命名空间
File名称空间提供了一些将在程序中使用的有用方法。
方法和属性:
AppendAllText():将文本字符串附加到指定文件后面。(这个方法是重载方法,因此可以使用不同的编码形式。)
AppendText():用StreamWriter对象向指定文件后面附加一个UTF-8-encode文本;UTF-8是8位Unicode Transformation Format ,它与ASCII字符集是向后兼容的。
Copy():复制指定文件。
Creat():创建指定文件。
CreatText():创建或打开UTF-8-encoded文本的文件。
Delete():删除指定文件。
Exits():检验一个指定文件是否存在。
GetCreationTime():返回创建文件的日期和时间。
Move():将指定文件移动到指定位置。
Open():用FileStream对象打开指定文件。
OpenRead():打开要读取的现有文件。
OpenText():打开要读取的UTF-8文件。
OpenWrite():打开要写的现有文件。
ReadAllBytes():打开一个二进制文件,并将内容复制到一个字节数组中,每个ReadAll*()方法都有一个对应的WriteAll *()方法。
ReadAllLines():打开一个文本文件,将文件中所有代码行读入到一个字符串数组中并且关闭它。
ReadAllText():打开一个文本文件,将内容读入一个字符串,并且关闭它。
Replace():用另一个文件的内容替换一个文件的内容,删除原始文件并为被替换的文件做一个备份。
SetAttributes():设置文件的属性。

3、FileInfo类
FileInfo类的语句以注释的形式标出。这个类提供了一个关于在系统上发现的文件的详细信息。
属性和方法:
AppendText():用StreamWriter附加当前FileInfo对象的文本。
CopyTo():将现有文件复制到一个新文件中。
Creat():创建新文件。
CreatText():创建用来写新文本文件的StreamWriter对象。
Delete():删除文件。
Equals():确定两个FileInfo对象是否相等。
MoveTo():将文件移动到一个新的位置,用一个选项来重命名它。
Open():用各种各样的读写权限打开文件。
Replace():用当前FileInfo文件的内容替换指定文件。
Attributes():获得指定文件的属性。
CreationTime:获得或者设置文件的创建时间。
Directory:获得父目录的一个实例。
DirectoryName:返回作为一个字符串的完整路径名称。
Exists:判断一个文件是否存在。
Extension:返回文件扩展名的字符串表示。
FullName:返回文件或目录的完整路径。
LastAccessTime:返回上次访问文件的时间。
Length:返回文件中的字节数。
Name:返回当前文件的名称。

4、文件的类型
从编程语言的观点来看,有两种基本文件类型:包含文本数据的类型和包含二进制数据的类型。根据名称往往可以判断给定文件是文本还是二进制文件。
文件名:由基本文件名(文件名)和辅助文件名(文件扩展名)组成,并被存储在磁盘上。

(1)文本与二进制数据文件
文本文件:包含文本数据的文件通常是由附加到文件末尾的字符串编译而来的。
文本文件只能包含字符串!!!
优点:易于读取。

二进制文件:二进制文件与文本文件略有不同。如果一个数字写到二进制文件中,那么就不会发生从数字到字符串的转换。
优点:存储二进制文件的值无论多大,都只占4个字节。
同时,存储为二进制的值可以移动到内存中,使用时不需要将他们的值从存储在磁盘上的字符串格式转换成在程序中使用的十六进制格式。将数据从磁盘中读入程序中的速度更快一些。
第一行的值显示为00000,说明是以16进制格式显示数字所占的字节数。然后以ASCII码的转换格式进行数据的转换,就可以读懂相关的二进制文件。

示例程序:写文本文件。
用错误日志消息作为异常处理的一部分是一种有用的调试技术。
改程序让用户选择要跑出的错误类型来测试错误日志的功能。做出选择之后,用户单机Throw按钮,就会抛出一个适当的异常。然后该异常会被写到一个错误日志中。然后错误日志的内容将显示在列表框对象中。

程序包含知识
StreamWrite对象
代码用StreamWriter对象将错误消息以及相关消息写到一个名为ErrorLog.txt的磁盘文件中。

sw=new StreamWiter(Path.Combine(pathName.fileName),true);

调用StreamWriter的构造函数从而实例化sw StreamWrite对象。该构造函数是重载的。
第一个参数:Path.Combine(pathName.fileName)将路径名和文件名结合起来形成一个参数。默认情况下,如果没有提供文件的路径名,那么C#会将文件存储在与程序的可执行文件相同的目录中。
第二个参数:true。如果第二个参数是true那么所有新数据都被附加到文件末尾。如果该文件不存在那么就创建这个文件。 如果第二个参数是false,那么会创建该文件并且将新字符串数据写给该文件中。
示例程序节选:

sw.WriteLine(currentDT.ToShortDateString()+","currentDT.ToShortTimeString()+":"+errorMessage);
sw.WriteLine("--------------");
sw.Close();

上述程序可以说明相关的写入函数使用方法。

StreamReader对象
程序编译路径和文件名,并将它们赋予变量pfn,然后代码用一个File对象来确定该文件是否存在。

clsErrorLog类

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using  System.IO;

    class clsErrorLog
    {
        private string fileName;
        private string pathName;
        private string errorMessage;
        private int errorFlag;

        StreamWriter sw = null;
        StreamReader sr = null;

        public clsErrorLog(string msg)
        {
            errorMessage = msg;
            errorFlag = 0;
            fileName = "ErrorLog.txt";
        }

        public string Filename
        {
            get
            {
                return fileName;
            }
            set
            {
                if (value.Length > 0)
                {
                    fileName = value;
                }
            }
        }

        public string Message
        {
            get {
                return errorMessage;
            }
            set
            {
                if (value.Length > 0)
                {
                    errorMessage = value;
                }
            }
        }

        public string PathName
        {
            get {
                return pathName;
            }
            set
            {
                if (value.Length > 0)
                {
                    pathName = value;
                }
            }
        }

        public string ReadErrorLog()
        {
            string buff;
            try
            {
                string pfn = Path.Combine(pathName, fileName);
                if (File.Exists(pfn) == true)
                {
                    sr = new StreamReader(pfn);
                    buff = sr.ReadToEnd();
                    return buff;
                }
            }
            catch
            {
                return " ";
            }
            return " ";
        }
        //下列方法是进行重载的。在frmMain中,把错误消息传递给构造函数,
        //然后将错误消息赋予名为errorMessage。如果clsErrorLog的一个对象
        //已经实例化,而且用户要向文件中写入一条错误消息,可以使用
        //带参数的WriteErrorLog()方法。
        public int WriteErrorLog()
        {
            errorFlag = 0;
            DateTime currentDT = DateTime.Now;

            try
            {
                if (errorMessage.Length != 0 && pathName.Length != 0 && fileName.Length != 0)
                {
                    sw = new StreamWriter(Path.Combine(pathName, fileName), true);
                    sw.WriteLine(currentDT.ToShortDateString() + "," + currentDT.ToShortTimeString() + ":" + errorMessage);
                    sw.WriteLine("----------------");
                    sw.Close();
                }
                else
                {
                    errorFlag = 1;
                }
            }
            catch (Exception ex)
            {
                errorMessage = ex.Message;
                return 1;
            }
            return errorFlag;
        }

        public int WriteErrorLog(string msg)
        {
            errorMessage = msg;
            WriteErrorLog();
            return errorFlag;
        }
    }

frmMain代码:

using System.Windows.Forms;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;


public class frmMain : Form
{
    string err;

    private GroupBox groupBox1;
    private RadioButton rbNumericOverflow;
    private RadioButton rbFileNotFound;
    private RadioButton rbDividBy0;
    private Button btnThrow;
    private TextBox txtErrorBox;
    private Button btnClose;
    #region Windows code
    private void InitializeComponent()
    {
            this.groupBox1 = new System.Windows.Forms.GroupBox();
            this.rbDividBy0 = new System.Windows.Forms.RadioButton();
            this.rbFileNotFound = new System.Windows.Forms.RadioButton();
            this.rbNumericOverflow = new System.Windows.Forms.RadioButton();
            this.btnThrow = new System.Windows.Forms.Button();
            this.btnClose = new System.Windows.Forms.Button();
            this.txtErrorBox = new System.Windows.Forms.TextBox();
            this.groupBox1.SuspendLayout();
            this.SuspendLayout();
            // 
            // groupBox1
            // 
            this.groupBox1.Controls.Add(this.rbNumericOverflow);
            this.groupBox1.Controls.Add(this.rbFileNotFound);
            this.groupBox1.Controls.Add(this.rbDividBy0);
            this.groupBox1.Location = new System.Drawing.Point(43, 30);
            this.groupBox1.Name = "groupBox1";
            this.groupBox1.Size = new System.Drawing.Size(407, 89);
            this.groupBox1.TabIndex = 0;
            this.groupBox1.TabStop = false;
            this.groupBox1.Text = "Exception:";
            // 
            // rbDividBy0
            // 
            this.rbDividBy0.AutoSize = true;
            this.rbDividBy0.Location = new System.Drawing.Point(20, 40);
            this.rbDividBy0.Name = "rbDividBy0";
            this.rbDividBy0.Size = new System.Drawing.Size(83, 16);
            this.rbDividBy0.TabIndex = 0;
            this.rbDividBy0.TabStop = true;
            this.rbDividBy0.Text = "Divid by 0";
            this.rbDividBy0.UseVisualStyleBackColor = true;
            this.rbDividBy0.CheckedChanged += new System.EventHandler(this.rbDividBy0_CheckedChanged);
            // 
            // rbFileNotFound
            // 
            this.rbFileNotFound.AutoSize = true;
            this.rbFileNotFound.Location = new System.Drawing.Point(143, 40);
            this.rbFileNotFound.Name = "rbFileNotFound";
            this.rbFileNotFound.Size = new System.Drawing.Size(107, 16);
            this.rbFileNotFound.TabIndex = 1;
            this.rbFileNotFound.TabStop = true;
            this.rbFileNotFound.Text = "File Not found";
            this.rbFileNotFound.UseVisualStyleBackColor = true;
            // 
            // rbNumericOverflow
            // 
            this.rbNumericOverflow.AutoSize = true;
            this.rbNumericOverflow.Location = new System.Drawing.Point(279, 40);
            this.rbNumericOverflow.Name = "rbNumericOverflow";
            this.rbNumericOverflow.Size = new System.Drawing.Size(119, 16);
            this.rbNumericOverflow.TabIndex = 2;
            this.rbNumericOverflow.TabStop = true;
            this.rbNumericOverflow.Text = "Numeric Overflow";
            this.rbNumericOverflow.UseVisualStyleBackColor = true;
            // 
            // btnThrow
            // 
            this.btnThrow.Location = new System.Drawing.Point(464, 40);
            this.btnThrow.Name = "btnThrow";
            this.btnThrow.Size = new System.Drawing.Size(75, 23);
            this.btnThrow.TabIndex = 1;
            this.btnThrow.Text = "Throw";
            this.btnThrow.UseVisualStyleBackColor = true;
            this.btnThrow.Click += new System.EventHandler(this.btnThrow_Click);
            // 
            // btnClose
            // 
            this.btnClose.Location = new System.Drawing.Point(464, 96);
            this.btnClose.Name = "btnClose";
            this.btnClose.Size = new System.Drawing.Size(75, 23);
            this.btnClose.TabIndex = 2;
            this.btnClose.Text = "Close";
            this.btnClose.UseVisualStyleBackColor = true;
            this.btnClose.Click += new System.EventHandler(this.button2_Click);
            // 
            // txtErrorBox
            // 
            this.txtErrorBox.Location = new System.Drawing.Point(43, 139);
            this.txtErrorBox.Multiline = true;
            this.txtErrorBox.Name = "txtErrorBox";
            this.txtErrorBox.Size = new System.Drawing.Size(496, 203);
            this.txtErrorBox.TabIndex = 3;
            // 
            // frmMain
            // 
            this.ClientSize = new System.Drawing.Size(584, 373);
            this.Controls.Add(this.txtErrorBox);
            this.Controls.Add(this.btnClose);
            this.Controls.Add(this.btnThrow);
            this.Controls.Add(this.groupBox1);
            this.Name = "frmMain";
            this.Text = "ErrorLog";
            this.Load += new System.EventHandler(this.frmMain_Load);
            this.groupBox1.ResumeLayout(false);
            this.groupBox1.PerformLayout();
            this.ResumeLayout(false);
            this.PerformLayout();

    }
    #endregion
    //#region是C# 预处理器指令。 
    //#region 使您可以在使用 Visual Studio
    //代码编辑器的大纲显示功能时指定可展开或折叠的代码块。
    public frmMain()
    {
        InitializeComponent();
    }

    public static void Main()
    {
        frmMain main = new frmMain();
        Application.Run(main);
    }

    private void button2_Click(object sender, EventArgs e)
    {
        Close();
    }

    private void btnThrow_Click(object sender, EventArgs e)
    {
        try
        {
            if (rbDividBy0.Checked == true)
            {
                throw new System.DivideByZeroException();
            }
            else
            {
                if (rbFileNotFound.Checked == true)
                {
                    throw new System.IO.FileNotFoundException();
                }
                else
                {
                    if (rbNumericOverflow.Checked == true)
                    {
                        throw new System.OverflowException();
                    }
                }
            }
        }
        catch (DivideByZeroException ex)
        {
            MessageBox.Show("Dividebyzeroexception throw", "Exception error");
            err = "DivideByZeroException: " + ex.Message;
        }
        catch (FileNotFoundException ex)
        {
            MessageBox.Show("FileNotFoundException throw", "Exception Error");
            err = "FileNotFoundException: " + ex.Message;
        }
        catch (OverflowException ex)
        {
            MessageBox.Show("OverflowException throw", "Exception Error");
            err = "OverflowException: " + ex.Message;
        }
        catch (Exception ex)
        {
            MessageBox.Show("Exception throw", "Exception Error");
            err = "Exception: " + ex.Message;
        }
        finally
        {
            clsErrorLog myErrorLog = new clsErrorLog(err);

            myErrorLog.PathName = Application.StartupPath;
            //Application提供static属性和方法管理应用程序
            //StartupPath获取应用程序的可执行路径的文件。
            myErrorLog.WriteErrorLog();
            txtErrorBox.Text = myErrorLog.ReadErrorLog();
        }

    }

    private void frmMain_Load(object sender, EventArgs e)
    {

    }

    private void rbDividBy0_CheckedChanged(object sender, EventArgs e)
    {

    }
}

小结:错误日志将文本数据写到一个名为ErrorLog.txt的磁盘文件中,如果该日志文件不存在那么就创建这个文件。如果这个文件存在,而且已有信息存储在其中,那么新数据就附加到现有文件的末尾。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值