一、需求分析
1、写需求文档
对于我们想要的结果,应该包括
a)复习,测试等不同训练模式
b)可以切换中英文进行训练扩展等
c)可以判断结果,同时展示出并写入文本文件
d)可以增加新单词,扩充词汇本单词量
e)确定文本单词数据格式,方便读写
2、确定数据格式
首先我们得包含单词序号,中英文以及音标,考虑到中文词性的多样性,暂且定为3个中文,同时包含错误次数,方便日后分析单词薄弱环节,所以组合为 “单词序号,英文,音标,中文1,中文2,中文3,错误次数” 7项,表述十分抽象,接下来放一张图方便理解。
3、考虑单词存放格式
题主刚开始考虑使用结构体进行存储,但发现十分复杂,索性考虑使用类以及泛型列表存储,列表内存储的是类,简单来说就是列表内的每一项都是一个类的实例。
4、大致画出主程序界面
二、建立项目
1、新建项目
打开visual studio,点击创建新项目(没有的读者可以去微软官网下载,选择2019社区版即可)
选择C#Windows窗体应用
自由创建名字和路径
2、进入界面后,根据界面分析,画出程序主体内容
序号为此单词在文本中的次序,错误次数为历史以来错误填写次数,0/0为当前单词序号/总单词数。
3、引入读写数据库
using System.IO; //读取写入文件
四、创建一系列自定义函数
1、初始化一系列变量
t1为计数单词数据中的第一个的单词序号
t2为写新单词的计数,方便直接写出新单词的序号
t3为所有单词的总个数
t4为左上方0/0的单词计数
model为复习模式,测试模式等的标记
test_model为中英文切换标记
同时还定义了一系列y,yy,yyy为昨天,前天,大前天的路径,方便日后扩展功能
int t1 = 0; // 单词序号计数器
int t2 = 1; // 写新计数器
int t3 = 1; // Allwords计数器
int t4 = 2; // 单词计数器
int model = 0; // 学习模式
int test_model = 0; // 中->英 or 英->中
string base_path = "../../../../../";
string nowaday_path, yesterday_path, yyesterday_path, yyyesterday_path, Allwords_path;
2、创建类以及生成泛型空列表
public class word
{
public int number;
public string English;
public string SoundMark;
public int numoferror;
public string Chinese1;
public string Chinese2;
public string Chinese3;
}
List<word> words = new List<word>(); //创建了一个泛型空列表
3、遍历函数
创建此函数的作用是方便调用,以及可以直接读取文本内的内容并添加至words列表中,由于我们的数据是用逗号分隔,则直接使用split函数可以读取各分量数据,方便添加至words列表中。形参为需要读取的文件路径。
public void Traverse(string Path)
{
// 读取path文本数据保存在alllines数组里
string[] alllines = File.ReadAllLines(Path, Encoding.Default);
// 遍历文本
for (int i = 0; i < alllines.Length; i++)
{
List<string> list = new List<string>(alllines[i].Split(','));
word word = new word();
word.number = i + 1;
word.English = list[1];
word.SoundMark = list[2];
word.Chinese1 = list[3];
word.Chinese2 = list[4];
word.Chinese3 = list[5];
word.numoferror = Convert.ToInt32(list[6]);
words.Add(word);
}
}
4、只展示中英文函数
形参为传入的单词
public void ShowChinese(word temp)
{
label5.Text = "<-" + Convert.ToString(temp.number) + "->";
label10.Text = "null";
label11.Text = "null";
label12.Text = temp.Chinese1;
label15.Text = temp.Chinese2;
label16.Text = temp.Chinese3;
label13.Text = Convert.ToString(temp.numoferror);
label17.Text = Convert.ToString(t4) + "/" + words.Count;
}
public void ShowEnglish(word temp)
{
label5.Text = "<-" + Convert.ToString(temp.number) + "->";
label10.Text = temp.English;
label11.Text = temp.SoundMark;
label12.Text = "null";
label15.Text = "null";
label16.Text = "null";
label13.Text = Convert.ToString(temp.numoferror);
label17.Text = Convert.ToString(t4) + "/" + words.Count;
}
此函数中的labelX中的X为创建的显示区块对应label序号,例如ShowChinese中显示中文1,对应为主界面的Label12
5、ShowAll函数
只展示了中英文也不够啊,当你选择了答案之后肯定得呈现出答案才行,方便记忆不是吗,所以创建ShowAll,当调用此函数时,展现中英文音标等所有信息。形参为传入的单词。
// 显示所有信息函数
public void ShowAll(word temp)
{
label10.Text = temp.English;
label11.Text = temp.SoundMark;
label12.Text = temp.Chinese1;
label15.Text = temp.Chinese2;
label16.Text = temp.Chinese3;
label13.Text = Convert.ToString(temp.numoferror);
}
6、写文件
形参为写入文件的路径,以及需要写入的各内容分量。
// 写文件函数
public void Write(string Path,int Number, string English, string SoundMark,string Chinese1,string Chinese2, string Chinese3, int numoferror)
{
StreamWriter sw = new StreamWriter(Path, true, Encoding.Default);//实例化StreamWriter
sw.WriteLine(Convert.ToString(Number) + "," + English + ",/" + SoundMark + "/," + Chinese1 + "," + Chinese2 + "," + Chinese3 + "," + Convert.ToString(numoferror));
sw.Flush();
sw.Close();
label14.Text = "写入成功!";
}
7、清除函数
清除函数清除了泛型列表words中的内容以及清除ListBox中的内容
// 清除words和list函数
public void Clear()
{
words.Clear();
listBox1.Items.Clear();
}
8、Check函数
在写入文件时,得检查此单词是否已存在,不存在才需写入,已存在则放弃写入,形参为需检查的单词
// 检查是否有重复单词
public void Check_Write(string word)
{
foreach (word temp in words)
{
if (temp.English == word)
return;
}
Write(Allwords_path, ++t3, textBox2.Text, textBox4.Text, textBox3.Text, textBox5.Text, textBox6.Text, 0);
}
9、随机数生成
形参为单词列表的总个数,中->英模式还是英->中模式,以及生成随机数个数(默认为30)
// 生成n个不重复的随机数
public void RandGetwords(int maxNum, int test_model, int n = 30)
{
int[] index = new int[maxNum];
for (int i = 0; i < maxNum; i++)
index[i] = i + 1;
Random r = new Random();
//用来保存随机生成的不重复的n个数
int[] result = new int[n];
for (int j = 0; j < n; j++)
{
//在随机位置取出一个数,保存到结果数组
int num = r.Next(0, maxNum);
result[j] = index[num];
//最后一个数复制到当前位置
index[num] = index[maxNum - 1];
//位置的上限减少一
maxNum--;
}
// 读取path文本数据保存在alllines数组里
string[] alllines = File.ReadAllLines(Allwords_path, Encoding.Default);
// 遍历文本
for (int i = 0; i < alllines.Length; i++)
{
List<string> list = new List<string>(alllines[i].Split(','));
word word = new word();
word.number = i + 1;
word.English = list[1];
word.SoundMark = list[2];
word.Chinese1 = list[3];
word.Chinese2 = list[4];
word.Chinese3 = list[5];
word.numoferror = Convert.ToInt32(list[6]);
for (int j = 0; j < n; j++)
if (word.number == result[j])
words.Add(word);
}
}
10、裁决边界函数
对于按下一个按钮,得判断列表words中是有数还是没数,从而弹出不同的选框
// 判断下标过小还是过大
public void JudgeofBounds()
{
if (words.Count == 0)
MessageBox.Show("请先选择模式!", "Warn");
else
MessageBox.Show("恭喜你,完成了本次训练!", "Complete");
}
11、写一号单词函数
传入test_model为中英文标记,根据标记呈现第一位单词
public void FirstWord(int test_model)
{
switch (test_model)
{
// 英->中
case 0:
ShowEnglish(words[0]);
label17.Text = 1 + "/" + words.Count;
break;
// 中->英
case 1:
ShowChinese(words[0]);
label17.Text = 1 + "/" + words.Count;
break;
}
}
四、编辑具体按钮内容
1、窗体加载
窗体加载的任务是
a)首先组合各路径
b)提取出单词本中的单词个数至t3,方便之后追加新单词
c)清空单词列表words
具体代码如下
private void Form1_Load(object sender, EventArgs e)
{
// 加载各路径
nowaday_path = base_path + "Day_List/" + DateTime.Now.ToShortDateString() + ".txt";
yesterday_path = base_path + "Day_List/" + DateTime.Now.AddDays(-1).ToShortDateString() + ".txt";
yyesterday_path = base_path + "Day_List/" + DateTime.Now.AddDays(-2).ToShortDateString() + ".txt";
yyyesterday_path = base_path + "Day_List/" + DateTime.Now.AddDays(-2).ToShortDateString() + ".txt";
Allwords_path = base_path + "Sum_List/Allwords.txt";
// 遍历Allwords取出count
Traverse(Allwords_path);
t3 = words.Count;
Clear();
}
2、复习模式
复习模式的任务是
a)标记model,计数清零,清除文本框以及单词列表
b)读入昨天写入的新单词
c)并写入第一个单词至上方Label框内
具体代码如下
private void button7_Click(object sender, EventArgs e)
{
t1 = 0;
model = 0;
Clear();
Traverse(yesterday_path);
FirstWord(test_model);
}
3、测试模式
复习模式的任务是
a)标记model,计数清零,清除文本框以及单词列表
b)读入单词本内的所有单词,并随机取出n个单词进行测试
c)并写入第一个单词至上方Label框内
具体代码如下
private void button8_Click(object sender, EventArgs e)
{
t1 = 0;
model = 1;
Clear();
RandGetwords(t3,test_model);
FirstWord(test_model);
}
[注]由于在自定函数中我们已经初始过n=30,所以这里不传入n也能运行
4、英->中
英->中模式的任务是
a)标记test_model
b)写出此单词的英文
c)裁决边界
具体代码如下
private void button2_Click(object sender, EventArgs e)
{
try
{
test_model = 0;
ShowEnglish(words[t1]);
}
catch
{
JudgeofBounds();
}
}
5、英->中:×
英->中:×的任务是
a)判断目前是否是英->中模式,若不是则弹出窗口提示
b)展现此单词所有信息,并在ListBox中添加错词
c)裁决边界
具体代码如下
private void button4_Click(object sender, EventArgs e)
{
try
{
if (test_model == 1)
MessageBox.Show("当前是 “中->英” 模式,请先切换模式!", "Warn");
else
{
ShowAll(words[t1]);
this.listBox1.Items.Add(words[t1].number + ":" + words[t1].English + "," + Convert.ToString(words[t1].numoferror));
}
}
catch(System.ArgumentOutOfRangeException)
{
JudgeofBounds();
}
}
6、英->中:√
英->中:√的任务是
a)判断目前是否是英->中模式,若不是则弹出窗口提示
b)展现此单词所有信息
c)裁决边界
具体代码如下
private void button5_Click_1(object sender, EventArgs e)
{
try
{
if (test_model == 1)
MessageBox.Show("当前是 “中->英” 模式,请先切换模式!", "Warn");
else
ShowAll(words[t1]);
}
catch
{
JudgeofBounds();
}
}
7、中->英
中->英模式的任务是
a)标记test_model
b)写出此单词的中文
c)裁决边界
具体代码如下
private void button1_Click(object sender, EventArgs e)
{
try
{
test_model = 1;
ShowChinese(words[t1]);
}
catch
{
JudgeofBounds();
}
}
8、中->英:判断
中->英:判断的任务是
a)判断目前是否是中->英模式,若不是则弹出窗口提示
b)判断单词是否拼写正确,若不正确则在ListBox中添加错词
c)展现所有单词信息,清空拼写单词文本框
d)裁决边界
具体代码如下
private void button3_Click(object sender, EventArgs e)
{
try
{
if(test_model == 0)
MessageBox.Show("当前是 ”英->中“ 模式,请先切换模式!", "Warn");
else
{
ShowAll(words[t1]);
textBox1.Text = null;
if (textBox1.Text != words[t1].English)
this.listBox1.Items.Add(words[t1].number + ":" + words[t1].English + ":" + Convert.ToString(words[t1].numoferror));
}
}
catch
{
JudgeofBounds();
}
}
9、Next
Next的任务是
a)判断是否填写答案,若无则弹窗提示
b)根据test_model中英文标记显示下一位单词的具体信息
c)裁决边界
具体代码如下
private void button9_Click(object sender, EventArgs e)
{
try
{
if (test_model == 0 && label12.Text != "null")
{
word temp = words[++t1];
ShowEnglish(temp);
t4++;
}
else if (test_model == 1 && label10.Text != "null")
{
word temp = words[++t1];
ShowChinese(temp);
t4++;
}
else
MessageBox.Show("请先选择你的答案!", "Warn");
}
catch
{
JudgeofBounds();
}
}
10、写入单词
写入单词的任务是
a)判断是否有错词,若无则无需写入,弹窗提示
b)根据具体模式选择相应的路径
c)清空原先的文档,并依照错词的序号,把相应单词的错误次数自加1,重新写入文档
具体代码如下
private void button10_Click(object sender, EventArgs e)
{
if (listBox1.Items.Count == 0)
MessageBox.Show("无错词,无需更改文档!", "Warn");
string path = null;
label14.ForeColor = Color.Green;
// 根据模式选择路径
switch (model)
{
case 0:
path = yesterday_path;
break;
case 1:
path = Allwords_path;
break;
}
// 清空文本文档
words.Clear();
Traverse(path);
FileStream fs = new FileStream(path, FileMode.Truncate, FileAccess.ReadWrite);
fs.Close();
// new 一个长度为 listBox1.Items.Count的数组
int[] a = new int[listBox1.Items.Count];
// 循环便利listBox1中的每一项,且把序号存储在数组a中
for (int i = 0; i < listBox1.Items.Count; i++)
{
List<string> list1 = new List<string>(Convert.ToString(listBox1.Items[i]).Split(':'));
a[i] = Convert.ToInt32(list1[0])-1;
words[a[i]].numoferror++;
}
// 在清空的文档中追加写入文件
for(int i = 0; i < words.Count; i++)
{
word temp = words[i];
Write(path, i+1, temp.English, temp.SoundMark.Replace("/",""), temp.Chinese1, temp.Chinese2, temp.Chinese3, temp.numoferror);
}
}
11、写新
写新的任务是
a)判断中英文文本框中是否有内容,若无则弹窗提示
b)根据今天的日期创建新文档并写入单词,同时在单词本中新追加单词
c)清空写新单词中的文本框
具体代码如下
private void button6_Click(object sender, EventArgs e)
{
// 判断英文和中文框内是否有内容
if ( textBox2.Text == "" && textBox3.Text == "")
MessageBox.Show("请输入中文&英文!", "Warn");
else
{
// 写入Day_List,nowaday
Write(nowaday_path, t2++, textBox2.Text, textBox4.Text, textBox3.Text, textBox5.Text, textBox6.Text, 0);
// 写入Allwords
Check_Write(textBox2.Text);
// 更换label4文本框颜色
if (t2 % 2 == 0)
label14.ForeColor = Color.Red;
else
label14.ForeColor = Color.Black;
// 清空文本框
textBox2.Text = null;
textBox3.Text = null;
textBox4.Text = null;
textBox5.Text = null;
textBox6.Text = null;
}
}
五、测试程序
至此程序大体框架搭建完成,接下来进行测试
测试没有问题,可以正常使用
六、附录
鉴于本人能力所限,文中难免存有疏漏之处,恳请读者谅解并指正。
接下来放出程序源码,方便看到最后的读者学习交流,同时题主给出yyesterday,yyyesterday的变量的定义,以及新建Wrong_List的文件夹,感兴趣的读者可以自由修改添加新内容,根据复习模式选择复习昨天的单词还是前天大前天的单词,或者整理错词,提取错误次数大于n的单词写入至Wrong_List文件夹等。
百度网盘,提取码:hlf9