在C#中使用SQLite简介

SQLite

SQLite 是一种小型的关系数据库。相比于 MySQL、SQLServer 这样的客户端/服务器模式,它的存储方式更接近对文件的读写。而相比于普通的文件,SQLite 又支持我们使用 SQL 语句来操纵数据。本文将会简单介绍一下如何在 C# 语言中使用 SQLite。

System.Data.SQLite

在 C# 中操作 SQLite 我们需要使用 System.Data.SQLite 这个类库。这个库并不是 .NET 的标准库,所以我们需要手动下载它。这里给出一个可用的下载链接

在使用之前,我们需要将 dll 文件拷贝到工程项目中,然后添加对 dll 文件的引用。

基本操作

1.创建数据库文件

SQLiteConnection.CreateFile("sqlite.db");

2.创建和打开数据库连接

SQLiteConnection conn = new SQLiteConnection("Data Source=sqlite.db;Version=3;");
conn.Open();

3.创建表

string sql = "create table employee (name varchar(20), age int)";
SQLiteCommand cmd = new SQLiteCommand(sql, conn);

4.插入数据记录

sql = "insert into employee (name,age) values ('wuzhiyu',25)";
cmd = new SQLiteCommand(sql, conn);
cmd.ExecuteNonQuery();

其中 cmd.ExecuteNonQuery(); 一般用来执行插入、更新和删除操作。

5.查询数据

sql = "select * from employee";
cmd = new SQLiteCommand(sql, conn);
SQLiteDataReader reader = cmd.ExecuteReader();
while(reader.Read())
{
    Console.WriteLine("Name:" + reader["name"] + "\t Age:" + reader["age"]);
}

SQLiteDataAdapter adapter = new SQLiteDataAdapter(sql,conn);
DataTable dt=new DataTable();
int count = adapter.Fill(dt);
for(int i=0;i<count;i++)
{
    Console.WriteLine("Name: {0}\t Age: {1}", dt.Rows[i][0], dt.Rows[i][1]);
}

上面给出了两种读取数据的方法,一个是使用类似迭代器(或者说游标)的方法一行行来读取数据记录,第二种则是使用类似适配器的方式将数据填充到 DataTable 中,然后我们对 DataTable 进行操作。

源代码

namespace SQLite
{
    class Program
    {
        static void Main(string[] args)
        {
            //1. Create a database file
            SQLiteConnection.CreateFile("sqlite.db");

            //2. Connecting to a database
            SQLiteConnection conn = new SQLiteConnection("Data Source=sqlite.db;Version=3;");
            conn.Open();

            //3. Create a table
            string sql = "create table employee (name varchar(20), age int)";
            SQLiteCommand cmd = new SQLiteCommand(sql, conn);
            cmd.ExecuteNonQuery();

            //4. Insert records
            sql = "insert into employee (name,age) values ('wuzhiyu',25)";
            cmd = new SQLiteCommand(sql, conn);
            cmd.ExecuteNonQuery();
            sql = "insert into employee (name,age) values ('Frank',50)";
            cmd = new SQLiteCommand(sql, conn);
            cmd.ExecuteNonQuery();

            //5. Query the records
            sql = "select * from employee";
            cmd = new SQLiteCommand(sql, conn);
            SQLiteDataReader reader = cmd.ExecuteReader();
            while(reader.Read())
            {
                Console.WriteLine("Name:" + reader["name"] + "\t Age:" + reader["age"]);
            }

            SQLiteDataAdapter adapter = new SQLiteDataAdapter(sql,conn);
            DataTable dt=new DataTable();
            int count = adapter.Fill(dt);
            for(int i=0;i<count;i++)
            {
                Console.WriteLine("Name: {0}\t Age: {1}", dt.Rows[i][0], dt.Rows[i][1]);
            }
        }
    }
}

参考资料

使用iTextSharp来填充PDF模板文件

需求简介

遇到了这样一个需求:某公司需要为所有用户的培训生成一个培训记录,过程如下:

  1. 用户在培训完之后会进入到一个填写信息的界面。
  2. 用户填写信息。
  3. 生成PDF格式的培训记录。
  4. 用户下载并打印归档。

思路

因为每次培训后生成的PDF文件内容都不完全一样,但是格式却是完全相同的,所以很容易让人联想到使用模板文件。每次只需要提取用户输入的有效信息,然后复制模板、填充模板、弹出下载即可。

解决方案

(1)制作模板:我先使用Microsoft Office 2010编辑模板文件,在保存的时候将文件保存为pdf文件。然后用Adobe Acrobat X编辑刚刚生成的PDF文件,把Textbox、Checkbox等域拖动到指定的位置,然后通过预览功能调整各个域的位置和其中文字的字体、大小,完成后保存。模板完成!(我用Google搜索的时候也看到过用OpenOffice来完成模板的,不过没点开看)

(2)编程填充PDF文件:本人使用的编程语言是C#,所以使用对应的免费开源类库iTextSharp来完成填充的功能。程序的执行过程是:先读取网页中的有效信息,然后打开模板文件填充到模板文件的域中间,最后另存为一个PDF文件。代码见下文。

(3)服务器端推送文件给浏览器下载。

遇到的问题

(1)首先遇到的问题是Checkbox的传参问题。一开始本人是从一些英文网站查看的相关资料,里面在设置Checkbox使用的语句是:pdfFormFields.SetField(“male”, “Yes”); 试了很多遍,结果就是不行,Checkbox并没有被勾选上。后来无奈了点开Checkbox属性中的选项值一栏,发现它有一栏名为导出值,其中的值为“是”。我猜想我的模板由于使用中文版,其中参数的设置也发生了相应的变化,一试之下果然如此。pdfFormFields.SetField(“male”, “是”); 就是行得通。

(2)显示中文的问题。如果直接把中文字符串设置到PDF模板中去的话中文字符一个都不会显示。Google了一下,应该是亚洲的文字都不能显示,为此还需要两个附加组件:iTextAsianCmaps.dll和iTextAsian.dll。这两个组件可以在sourceforge上面下载。大家不妨参考:http://www.cnblogs.com/haogj/archive/2011/09/05/2167659.html 。

(3)以为这就好了?差远了!在输入第一句BaseFont.AddToResourceSearch(“iTextAsian.dll”);的时候有错误?对了!目前iTextSharp最新的版本是5.4.4,也就是我当时使用的版本。估计版本的变迁使得方法的调用也出了问题,于是果断换成了5.1.2的版本。

(4)改完之后一路顺畅地写完了方法。试验一下,又出错!BaseFont bf = BaseFont.CreateFont(“STSong-Light”, “UniGB-UCS2-H”, BaseFont.EMBEDDED);中的STSong-Light和UniGB-UCS2-H无法被识别!这个问题我直到最后都没能解决(看了好几篇帖子还是没能解决,大家如果有成功的通知小弟一声,谢谢啊!),退而求其次我只好使用TTF 字体。注:使用TTF 字体貌似是在PDF文件中嵌入字体文件,这使得PDF文件体积巨大,成了我的心病。

源代码

FillPdfTemplate.cs

Anyway,代码还是最重要的,以下的静态类用来填充PDF模板:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Diagnostics;
using System.IO;
using iTextSharp.text.pdf;

public class FillPdfTemplate
{
    public static void GetEnPdf(string templatePath, string newFilePath, Dictionary<string, string> parameters)
    {
        PdfReader pdfReader = new PdfReader(templatePath);
        PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(newFilePath,
FileMode.Create));
        //获取域的集合;
        AcroFields pdfFormFields = pdfStamper.AcroFields;
        //为需要赋值的域设置值;
        foreach (KeyValuePair<string, string> parameter in parameters)
        {
            pdfFormFields.SetField(parameter.Key, parameter.Value);
        }
        //这句很重要,如果为false那么生成的PDF文件还能编辑,一定要设为true;
        pdfStamper.FormFlattening = true;
        pdfStamper.Close();
        pdfReader.Close();
    }

    public static void GetChPdf(string templatePath, string newFilePath, string iTextAsianCmapsPath, Dictionary<string, string> parameters)
    {
        PdfReader pdfReader = new PdfReader(templatePath);
        PdfStamper pdfStamper = new PdfStamper(pdfReader, new FileStream(newFilePath, FileMode.Create));
        //获取域的集合;
        AcroFields pdfFormFields = pdfStamper.AcroFields;

        BaseFont.AddToResourceSearch(iTextAsianCmapsPath);
        //创建中文字体,第一个参数是中文字体的路径,第二个参数表示文字方向水平,第三个貌似是字体嵌入PDF文件;
        BaseFont baseFT = BaseFont.CreateFont(@"C:WindowsFontssimsun.ttc,1", BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
        foreach (KeyValuePair<string, string> parameter in parameters)
        {
            //要输入中文就要设置域的字体;
            pdfFormFields.SetFieldProperty(parameter.Key, "textfont", baseFT, null);
            //为需要赋值的域设置值;
            pdfFormFields.SetField(parameter.Key, parameter.Value);
        }
        //这句很重要,如果为false那么生成的PDF文件还能编辑,一定要设为true;
        pdfStamper.FormFlattening = true;
        pdfStamper.Close();
        pdfReader.Close();
    }
}
事件响应

下面是参数的获取和调用的过程和推送PDF文件下载的方法:

protected void btnDownLoad_Click(object sender, EventArgs e)
{
    string position = Text1.Value;
    string venue = Text2.Value;
    string method = Text3.Value;
    string date = Text4.Value;
    string teacher = Text5.Value;
    string content = TextArea1.Value;
    string examination = Checkbox1.Checked ? "是" : "否";
    string selfassessment = Checkbox2.Checked ? "是" : "否";
    string certificate = Checkbox3.Checked ? "是" : "否";
    string etc = Checkbox4.Checked ? "是" : "否";
    string trainee = Text6.Value;

    Dictionary<string, string> dict = new Dictionary<string, string>();
    dict.Add("TextPosition", position);
    dict.Add("TextVenue", venue);
    dict.Add("TextMethod", method);
    dict.Add("TextDate", date);
    dict.Add("TextTeacher", teacher);
    dict.Add("TextContent", content);
    dict.Add("TextTrainee", trainee);
    dict.Add("CheckBoxExamination", examination);
    dict.Add("CheckBoxSelf-assessment", selfassessment);
    dict.Add("CheckBoxCertificate", certificate);
    dict.Add("CheckBoxEtc", etc);

    string template = Server.MapPath("~/PDFTemplate/ch.pdf");
    string newFile = Server.MapPath("~/PDFTemplate") + "\\" + Session["UserID"].ToString() + ".pdf";
    string iTextAsianCmaps = Server.MapPath("~/Libs/iTextAsianCmaps.dll");
    TrainingRecordToPDF.GetChPdf(template, newFile, iTextAsianCmaps, dict);

    OutFile(newFile);
}

public void OutFile(string  filename)
{
    System.IO.FileInfo file = new System.IO.FileInfo(filename);
    Response.Clear();
    Response.Charset = "GB2312";
    Response.ContentEncoding = System.Text.Encoding.UTF8;
    Response.AddHeader("Content-Disposition", "attachment; filename=" + Server.UrlEncode(file.Name));
    Response.AddHeader("Content-Length", file.Length.ToString());
    Response.ContentType = "application/x-bittorrent";
    Response.WriteFile(file.FullName);
    Response.End();  

}

使用ICSharpZipLib将文件夹压缩为zip文件

在我接触Git和SVN之前,我最常用的保存数据的办法就是把文件夹压缩成一个zip文件,添加上时间戳。下面是我在学习C#的文件操作之后做的一个练习,使用开源的ICSharpZipLib来压缩文件夹并保存到指定的目录,还附带简单的日志文件。

关于ICSharpZipLib

ICSharpZipLib是一个开源的应用于.NET平台的开源类库。运用C#语言和这个类库,我们可以很轻松地创建和操作压缩文件,例如:Zip,GZip, Tar and BZip2。

程序界面

放上一张程序的截图:
interface
主要是三个输入内容:源文件夹、压缩包目标文件夹以及单次存档的备份说明。再放上一张压缩好的效果解图:
package

源代码

废话不多说,咱直接上源代码。说实话,使用ICSharpZipLib的博客应该也有很多,但是试了一下好多不能用。下面的这个类是我根据一个印度程序员(英文网站上的,不知道到底是啥名字,但是长得很三哥!)的博客改写的。测试过的哦,亲!

CompressFolderIntoZip.cs

执行将文件压缩的主要代码。

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using ICSharpCode.SharpZipLib.Zip;

namespace Data_Archiving
{
    class CompressFolderIntoZip
    {
        /// <summary>
        /// 用来进行文件夹的压缩,输入参数源文件夹路径和目标文件夹路径即可。
        /// </summary>
        /// <param name="sourceFolderPath"></param>
        /// <param name="destFolderPath"></param>
        /// <returns></returns>
        public static bool CompressFolder(string sourceFolderPath, string destFolderPath)
        {
            try
            {
                //文件名=文件夹名+时间戳+.zip;
                string zipFileName = destFolderPath + "\" +
                    sourceFolderPath.Substring(sourceFolderPath.LastIndexOf('\') + 1) 
                    + "(" + DateTime.Now.ToString("yyyy-MM-dd hh-mm-ss") + ").zip";
                ZipFile zipFile = ZipFile.Create(zipFileName);
                zipFile.BeginUpdate();
                //压缩文件夹时默认采用源文件夹的上一级作为根目录;
                AddFolderToZip(zipFile,
                    sourceFolderPath.Substring(0, sourceFolderPath.LastIndexOf('\')),
                    sourceFolderPath);
                zipFile.CommitUpdate();
                zipFile.Close();
            }
            catch (Exception exception)
            {
                Console.WriteLine(exception.ToString());
                return false;
            }
            return true;
        }

        /// <summary>
        /// 该方法用来实现文件夹及其中文件和子文件夹的递归压缩
        /// ZipFile zipFile=ZipFile.Create(string <zipfilename>)
        /// folderPath是需要压缩的文件夹
        /// root是压缩文件中的根目录,如果folderPath=@"C:Test"而root=@"C:"那么打开压缩文件会先显示一个名为Test的文件夹。如果folderPath=@"C:Test"而root=@"C:Test"那么压缩文件打开会直接显示Test文件夹下所有文件盒子文件夹。
        /// </summary>
        /// <param name="zipFile"></param>
        /// <param name="root"></param>
        /// <param name="folderPath"></param>
        private static void AddFolderToZip(ZipFile zipFile, string root, string folderPath)
        {
            string relativePath = folderPath.Substring(root.Length);
            if (relativePath.Length > 0)
            {
                zipFile.AddDirectory(relativePath);
            }

            foreach (string file in Directory.GetFiles(folderPath))
            {
                relativePath = file.Substring(root.Length);
                zipFile.Add(file, relativePath);
            }

            foreach (string subFolder in Directory.GetDirectories(folderPath))
            {
                AddFolderToZip(zipFile, root, subFolder);
            }
        }
    }
}
Form1.cs

负责校验数据和响应界面上的各种触发事件。

using System;
using System.IO;
using System.Windows.Forms;

namespace Data_Archiving
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
        }

        private void buttonChooseSourceFolder_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog.ShowDialog() == DialogResult.Cancel) return;
            textBoxSourceFolderPath.Text = folderBrowserDialog.SelectedPath;
        }

        private void buttonChooseDestinationFolder_Click(object sender, EventArgs e)
        {
            if (folderBrowserDialog.ShowDialog() != DialogResult.Cancel)
                textBoxDestinationFolderPath.Text = folderBrowserDialog.SelectedPath;
        }

        private void buttonQuit_Click(object sender, EventArgs e)
        {
            Dispose();
        }

        private void buttonExecute_Click(object sender, EventArgs e)
        {
            if (String.IsNullOrEmpty(textBoxSourceFolderPath.Text)
                || String.IsNullOrEmpty(textBoxDestinationFolderPath.Text)
                || String.IsNullOrEmpty(textBoxDescription.Text))
            {
                MessageBox.Show("输入框中的内容不可为空!");
                return;
            }

            //TODO:用正则表达式校验路径的有效性;

            DirectoryInfo source = new DirectoryInfo(textBoxSourceFolderPath.Text);
            DirectoryInfo destination = new DirectoryInfo(textBoxDestinationFolderPath.Text);

            if (!source.Exists)
            {
                MessageBox.Show("该文件夹不存在!");
                return;
            }
            if (!destination.Exists)
            {
                destination.Create();
            }

            string sourcePath = source.FullName;
            int startPos = sourcePath.LastIndexOf('\');
            string subDirName = destination.FullName + sourcePath.Substring(startPos);
            string logFileName = subDirName + "\Log.txt";

            var subDirInfo = new DirectoryInfo(subDirName);
            if (!subDirInfo.Exists)
            {
                subDirInfo.Create();
            }

            using (var sw = new StreamWriter(logFileName,true))
            {
                try
                {
                    if (new FileInfo(logFileName).Length == 0)
                    {
                        sw.WriteLine("This is the log of data archiving of folder "
                                     + sourcePath);
                        sw.WriteLine("This log is created at {0}",
                            DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
                        sw.WriteLine("=================================================");
                    }

                    sw.WriteLine("Update time: {0}",
                        DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss"));
                    sw.WriteLine("Description:");
                    sw.WriteLine(textBoxDescription.Text);
                    sw.WriteLine("=================================================");
                    sw.Flush();
                    sw.Close();

                    if (!CompressFolderIntoZip.
                        CompressFolder(source.FullName, subDirName))
                    {
                        MessageBox.Show("归档失败!");
                    }

                    textBoxDescription.Text = string.Empty;
                    MessageBox.Show("归档成功!");
                }
                catch (IOException exception)
                {
                    MessageBox.Show(exception.ToString());
                }
            }
        }
    }
}

简易WiFi热点设置程序

虚拟WiFi

从Windows 7开始,微软的操作系统内建了对虚拟WiFi的支持。一般来说,我们可以使用两种方式来开启该功能:命令提示符或者第三方软件。这两种方式各有优缺点,第三方软件比如Connectify,功能强大齐全,但是不知道为什么在我的联想Y470上反应非常慢。所以我一般使用第二种方式——命令提示符。

使用命令提示符来启动虚拟WiFi

在命令提示符下,开启虚拟WiFi的步骤可以分为:

1.使用管理员权限启动命令提示符cmd.exe
2.建立虚拟WiFi
netsh wlan set hostednetwork mode=allow ssid=NameString key=KeyString

其中mode=disallow可以禁用虚拟WiFi,ssid表示热点名称,key则为连接到热点的密码。

3.设置连接源

打开控制面板,按照 控制面板 => 网络和 Internet => 网络连接的顺序打开,你会发现多了一个Microsoft Virtual WiFi Miniport Adapter。按照你的信号源(如果连有线的就选本地连接,连无线的就选无线网络连接),右键属性 => 共享 勾选第一个选项,然后在下拉列表中选择那个多出来的无线网络连接。

4.开启虚拟WiFi
netsh wlan start hostednetwork
5.关闭虚拟WiFi
netsh wlan stop hostednetwork

可以想见,如果每次都要输入这么繁琐的命令真的比较麻烦,所以一个比较简单的方法是将上述命令分别写到bat文件中,用来批处理执行就好了。

窗体

总体来说桌面上放几个bat文件还是不美观的,所以写了一个简单的Windows Form窗体程序来包装这几个命令。程序的原理也比较简单,无非就是 cmd 的二次调用加上输入流、输出流和错误流的重定向而已。上一张图:

wifi

窗体上的两个输入框用来在更改和设置虚拟WiFi的SSID和连接密码。显示信息可以显示出连接的连接设备数目等信息。开启和关闭分别对应start和stop命令,禁用会删除虚拟的无线连接。最后说说这个输出面板,这是用来显示 cmd 中的输入信息,但是不能从这里输入。

源代码

Command.cs

接受一个命令字符串来对 cmd 进行二次调用。

using System.Collections.Generic;
using System.Diagnostics;

namespace VirtualWiFi
{
    internal class Command
    {
        public static string RunCMD(List<string> commands)
        {
            var p = new Process();
            //设定程序名;
            p.StartInfo.FileName = "cmd.exe";
            //关闭shell的使用;
            p.StartInfo.UseShellExecute = false;
            //cmd窗口不显示;
            p.StartInfo.CreateNoWindow = true;
            //重定向标准输入、输出、错误输出;
            p.StartInfo.RedirectStandardError = true;
            p.StartInfo.RedirectStandardInput = true;
            p.StartInfo.RedirectStandardOutput = true;

            p.Start();
            foreach (string command in commands)
            {
                p.StandardInput.WriteLine(command);
            }
            p.StandardInput.WriteLine("exit");

            //读取返回信息;
            return p.StandardOutput.ReadToEnd();
        }
    }
}
Form1.cs

负责响应按钮事件。

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace VirtualWiFi
{
    public partial class Form1 : Form
    {
        private readonly string allow;
        private readonly string disallow;
        private readonly string show;
        private readonly string start;
        private readonly string stop;

        public Form1()
        {
            InitializeComponent();
            show = @"netsh wlan show hostednetwork";
            start = @"netsh wlan start hostednetwork";
            stop = @"netsh wlan stop hostednetwork";
            allow = @"netsh wlan set hostednetwork mode=allow";
            disallow = @"netsh wlan set hostednetwork mode=disallow";
        }

        private void buttonOK_Click(object sender, EventArgs e)
        {
            if (textBox1.Text == null || textBox1.Text.Length == 0 ||
                textBox2.Text == null || textBox2.Text.Length == 0)
            {
                MessageBox.Show("SSID以及密码不可为空!");
                return;
            }
            string setting = allow + " ssid=" + textBox1.Text + " key=" + textBox2.Text;
            var commands = new List<string>();
            commands.Add(setting);
            commands.Add(show);
            Command.RunCMD(commands);
        }

        private void radioButtonStart_CheckedChanged(object sender, EventArgs e)
        {
            var commands = new List<string>();
            commands.Add(start);
            textBox3.Text = Command.RunCMD(commands);
        }

        private void radioButtonStop_CheckedChanged(object sender, EventArgs e)
        {
            var commands = new List<string>();
            commands.Add(stop);
            textBox3.Text = Command.RunCMD(commands);
        }

        private void buttonDisplay_Click(object sender, EventArgs e)
        {
            var commands = new List<string>();
            commands.Add(show);
            textBox3.Text = Command.RunCMD(commands);
        }

        private void buttonForbidden_Click(object sender, EventArgs e)
        {
            var commands = new List<string>();
            commands.Add(disallow);
            textBox3.Text = Command.RunCMD(commands);
        }
    }
}