一、前言

  作业具体要求见[https://edu.cnblogs.com/campus/nenu/SWE2017FALL/homework/922]。一开始用JAVA写了个词频统计,然而没想出输入格式怎么解决,于9/17日晚将JAVA程序改成用C#程序写。9/17晚上八点~9/18下午四点前做的工作,主要都是做技术原型,分析题中哪些是自己不确定或不会完成的地方。到了下午五点左右就开始真正完成满足题目要求的各项功能。代码地址[https://git.coding.net/Dawnfox/wf.git]

二、可能存在的困难

  • 困难一:C#中文件输入
             string fName = "";//文件路径
FileStream isrr = new FileStream(fName, FileMode.Open, FileAccess.Read);
StreamReader ioData = new StreamReader(isrr);
string s = "";//每行数据
while ((s = ioData.ReadLine()) != null)
{
//处理数据 }
ioData.Close();
  • 困难二:C#中判断目录、文件是否存在
         //判断是否为目录
static bool IsDir(String fDir)
{
bool res = false;
if (Directory.Exists(fDir))
{
res = true;
}
return res;
}
//判断是否为文件
static bool IsFile(String fPos)
{
bool res = false;
if (File.Exists(fPos))
{
res = true;
}
return res;
}
  • 困难三:C#中遍历指定目录中的文件
         //扫描目录 扫描拓展名为.txt
static List<string> scanDir(String fFir)
{
List<string> filesPath = new List<string>();
DirectoryInfo dir = new DirectoryInfo(fFir);
//fFir为某个目录,如: “D:\Program Files”
FileInfo[] inf = dir.GetFiles();
//int fNum = 0;
foreach (FileInfo finf in inf)
{
if (finf.Extension.Equals(fExtension))
{
filesPath.Add(finf.FullName);
}
}
return filesPath;
}
  • 困难四:C#字典(Dictionary)排序
 Dictionary<String, int> mp = new Dictionary<string, int>();//key:单词,value:词频
mp = mp.OrderByDescending(r => r.Value).ToDictionary(r => r.Key, r => r.Value);//排序
  • 困难五:C#格式化输出
 //格式码
Console.WriteLine("{0,-20}{1,10}", kvp.Key, kvp.Value);
  • 困难六:C#获取控制台重定向文件内容

这块挺卡人的。一直没想到如何用C#对通过控制台进行重定向的文件进行处理。直到今天(9/22)通过MSDN,查看关于C#的I/O流与.net中的console类[点击]。才想到解决办法。

                 if (Console.IsInputRedirected)//判断是否存在重定向输入
{
StreamReader ioData = null;
//获取控制台重定向文件数据流 2017/9/22 14:29:03
ioData = new StreamReader(Console.OpenStandardInput());
}
  • 困难七:C#多行输入,允许从控制台复制粘贴(Ctrl+c),并以F6或者Ctrl+z结束输入(9/25)
                     ConsoleKeyInfo consoleKeyInfo;//获得输入键盘输入 2017/9/25 19:43:47
Console.TreatControlCAsInput = true;//将复制Ctrl+C作为普通输入
String input = "";//输入字符串
consoleKeyInfo = Console.ReadKey();//获取字符串
input += consoleKeyInfo.KeyChar;
/*warning 多行输入操作 不支持单词删除后再输入单词 不允许回退操作 不支持特殊功能键操作*/
// F6 退出 Ctrl+z
while (!(consoleKeyInfo.Key == ConsoleKey.F6 || (consoleKeyInfo.Modifiers == ConsoleModifiers.Control && consoleKeyInfo.Key == ConsoleKey.Z)))
{
consoleKeyInfo = Console.ReadKey();
if (consoleKeyInfo.Key == ConsoleKey.Enter)
{
input += Environment.NewLine;//输入字符串换行,则在字符串尾部加上系统的换行符
Console.SetCursorPosition(, Console.CursorTop + );//换行之后,控制台光标移动到新的一行首部
}
input += consoleKeyInfo.KeyChar;
}

三、需要注意的地方

  需要注意四个功能(实际上是五个功能,功能四分为功能4-1和功能4-2)的输入输出格式。五个功能输出单词总数和出现次数最多的TOP10的单词及频率时,英语单词左对齐,数字右对齐。(假设n为单词数)

  • 功能一的输出格式需要注意单词总数和所列出TOP10的单词频率之间有空行,单词总数格式为“total  n”。
  • 功能二的输出格式需要注意单词总数和所列出TOP10的单词频率之间有空行,单词总数格式为“total  n words"。
  • 功能三的输出格式需要注意单词总数和所列出TOP10的单词频率之间有空行,单词总数格式为“total  n words",同时需要注意还要输出被测文件的名称,不用文件数据用“----”(四个短横)分割。
  • 功能4-1的输出格式需要注意单词总数和所列出TOP10的单词频率之间无空行,单词总数格式为“total  n",输入参数和输出结果之间存在空行。
  • 功能4-2的输出格式需要注意单词总数和所列出TOP10的单词频率之间无空行,单词总数格式为“total  n",输入的待测字符串和输出结果之间存在空行。
  • 功能4-2需要注意两种情况,一是直接从控制台输入多行数据;二是通过复制粘贴输入数据(Ctrl+c)。输入字符串与输出结果有空行,需要注意。(9/25)
  • 功能4-2结束输入应该是控制台标准结束输入(F6或者Ctrl+z)。(9/25)

四、解题思路

  总的思路就是扫描待测文件,用正则表达式将数字、换行、标点符号(小写的减号不需要替换)换成空格,至于上撇号之后的内容我是用正则表达式舍去不考虑。然后我就直接暴力通过空格将字符串切割成单词,然后将单词的值和出现的频率作为词典的key和value。

完整代码如下。

 using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions; namespace wf
{
class Program
{
static private String fExtension = ".txt";//文件为文本类型
static private Regex regex1 = new Regex("(\\d|\\r|\\n)");//空格替换数字和换行
static private Regex regex2 = new Regex(@"\p{Pc}|\p{Ps}|\p{Pe}|\p{Pi}|\p{Pf}|\p{Po}|\p{N}|\p{C}");//空格替换标点 短横线不替换
static private Regex regex3 = new Regex("(\\w+)'\\w+");////舍去上撇号之后内容
//功能四-2对应处理
static void FuncBaseT(string s)
{
string[] sl;//保存空格分割出的单词
int Freq = , wordNum = ;//Freq,单词出现频率 单词总数(重复也算)
Dictionary<String, int> mp = new Dictionary<string, int>();//key:单词,value:词频
s = regex1.Replace(s, " ");
s = regex3.Replace(s, "$1");
s = regex2.Replace(s, " ");
s = s.ToLower();//小写单词 避免单词大小写造成同一个单词被记作不同单词
sl = s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string sElem in sl)
{
wordNum++;
if (!mp.ContainsKey(sElem))
{
mp.Add(sElem, );
}
else
{
mp.TryGetValue(sElem, out Freq);
mp.Remove(sElem);
mp.Add(sElem, (Freq + )); }
}
//输出不重复的单词
Console.WriteLine("{0,-20}{1,10}", "total", mp.Count);
// ling 对字典排序 取TOP10 单词
mp = mp.OrderByDescending(r => r.Value).ToDictionary(r => r.Key, r => r.Value);//排序
int flag = ;
foreach (KeyValuePair<string, int> kvp in mp)
{
Console.WriteLine("{0,-20}{1,10}", kvp.Key, kvp.Value);
flag++;
if (flag >= )
break;
} }
//功能一、二、三 4-1对应处理
static void FuncBase(string ifName, bool isShowWords)
{
Dictionary<String, int> mp = new Dictionary<string, int>();//key:单词,value:词频
StreamReader ioData = null;
Boolean isFuncF = (ifName.Count() == );//是否为功能4-1 重定向文件
try
{
if (isFuncF)
{
//获取控制台重定向文件数据流 2017/9/22 14:29:03
ioData = new StreamReader(Console.OpenStandardInput());
}
else {
//通过文件路径读取文件内容
FileStream isr = new FileStream(ifName, FileMode.Open, FileAccess.Read);
ioData = new StreamReader(isr);
} try
{
string s;
string[] sl;
int Freq = , wordNum = ;//Freq,单词出现频率 单词总数(重复也算)
while ((s = ioData.ReadLine()) != null)
{
s = regex1.Replace(s, " ");
s = regex3.Replace(s, "$1");
s = regex2.Replace(s, " ");
s = s.ToLower();//小写单词 避免单词大小写造成同一个单词被记作不同单词
sl = s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); foreach (string sElem in sl)
{
wordNum++;
if (!mp.ContainsKey(sElem))
{
mp.Add(sElem, );
}
else
{
mp.TryGetValue(sElem, out Freq);
mp.Remove(sElem);
mp.Add(sElem, (Freq + )); }
}
}
}
finally
{
ioData.Close(); //功能4-1 输入与输出之间有换行
if (isFuncF)
{
Console.WriteLine("\r\n");
} //不重复单词数
if (isShowWords)
{
Console.WriteLine("{0,-20}{1,10} words", "total", mp.Count);//功能二和三
}
else
{
Console.WriteLine("{0,-20}{1,10}", "total", mp.Count);//功能一 功能4-1
} ////功能4-1 输出总数与单次频率显示之间无换行,但是功能一、二、三有换行
if (!isFuncF)
{
Console.WriteLine("\r\n");
} // ling 对字典排序 取TOP10 单词
mp = mp.OrderByDescending(r => r.Value).ToDictionary(r => r.Key, r => r.Value);//排序
int flag = ;
foreach (KeyValuePair<string, int> kvp in mp)
{
Console.WriteLine("{0,-20}{1,10}", kvp.Key, kvp.Value);
flag++;
if (flag >= )
break;
}
}
}
catch (IOException e)
{ }
} //功能一
static void FuncOne(string[] argsT)
{
if (argsT[].Equals("-s") && IsFile(argsT[]))
{ FuncBase(argsT[], false); }
else
{
Console.WriteLine("无效参数");
} }
//功能二/三选择
static void FuncTwoThreeFour(string[] argS)
{
string argT = "";
argT = argS[];
if (IsDir(argT))
{
FuncThree(argT);
}
else if (argT.Equals("-s")) {
if (Console.IsInputRedirected)
{
FuncBase("", false);
}
else {
Console.WriteLine("无效参数");
} }
else
{
FuncTwo(argT); }
} //功能二 参数文件名 不带后缀
static void FuncTwo(string fName)
{
fName = fName + fExtension;
if (IsFile(fName))
{
FuncBase(fName, true); }
else
{
Console.WriteLine("无效参数");
}
} //功能三 输入为文件目录 对该目录下txt文本统计字数
static void FuncThree(string dicName)
{
//获取txt文件列表 完整路径+带后缀的文件名
List<String> filesPath = ScanDir(dicName);
int flag = ;//用于控制横线格式输出
String filePath = "";
for (flag =;flag < filesPath.Count;flag++)
{
if (flag != )
{
Console.WriteLine("----");
}
filePath = filesPath[flag];
Console.WriteLine(Path.GetFileNameWithoutExtension(filePath));
FuncBase(filePath, true);
}
} //功能四 从dos输入一段文字 进行统计
static void FuncFour(string fContent)
{
FuncBaseT(fContent);
}
//判断是否为目录
static bool IsDir(String fDir)
{
bool res = false;
if (Directory.Exists(fDir))
{
res = true;
}
return res;
}
//判断是否为文件
static bool IsFile(String fPos)
{
bool res = false;
if (File.Exists(fPos))
{
res = true;
}
return res;
}
//扫描目录 扫描拓展名为.txt
static List<string> ScanDir(String fFir)
{
List<string> filesPath = new List<string>();
DirectoryInfo dir = new DirectoryInfo(fFir);
//fFir为某个目录,如: “D:\Program Files”
FileInfo[] inf = dir.GetFiles();
//int fNum = 0;
foreach (FileInfo finf in inf)
{
if (finf.Extension.Equals(fExtension))
{
filesPath.Add(finf.FullName);
}
}
return filesPath;
}
static void Main(string[] args)
{
int argNum = ;//输入参数个数决定功能
argNum = args.Length;
//零个参数 功能4-2 换行接受字符串
//1个参数 功能二或者功能三 ,如果为参数1为目录则为功能三,若为文件则为功能二,若为“-s”则可能为功能4-1(重定向)
//2个参数 功能一
//其他情况 参数无效
switch (argNum)
{
case :
// Console.ReadLine(); //接收字符串
ConsoleKeyInfo consoleKeyInfo;//获得输入键盘输入 2017/9/25 19:43:47
Console.TreatControlCAsInput = true;//将复制Ctrl+C作为普通输入
String input = "";//输入字符串
consoleKeyInfo = Console.ReadKey();//获取字符串
input += consoleKeyInfo.KeyChar;
/*warning 多行输入操作 不支持单词删除后再输入单词 不允许回退操作 不支持特殊功能键操作*/
// F6 退出 Ctrl+z
while (!(consoleKeyInfo.Key == ConsoleKey.F6 || (consoleKeyInfo.Modifiers == ConsoleModifiers.Control && consoleKeyInfo.Key == ConsoleKey.Z)))
{
consoleKeyInfo = Console.ReadKey();
if (consoleKeyInfo.Key == ConsoleKey.Enter)
{
input += Environment.NewLine;//输入字符串换行,则在字符串尾部加上系统的换行符
Console.SetCursorPosition(, Console.CursorTop + );//换行之后,控制台光标移动到新的一行首部
}
input += consoleKeyInfo.KeyChar;
}
Console.WriteLine("\r\n");//输入字符串和输出结果直接的空行
FuncFour(input);
break;
case :
FuncTwoThreeFour(args);
break;
case :
FuncOne(args);
break;
default:
Console.WriteLine("无效参数"); break; } }
}
}

  其实通过观察,每种功能的输出大同小异,输入的参数个数也有规律可循。功能一是输入两个参数;功能二和功能三是输入一个参数,通过判断这个参数是否为目录或者文件,来决定程序的功能;功能四可认为是两个小功能,功能4-1是一个参数输入和重定向文件输入,而功能4-2则是无参数输入,然后换行,接着输入的就是待测试的字符串。因此可以通过判断输入参数的个数和检验参数的形式来决定程序执行什么功能。于是,我将四个功能分别抽象成四个函数,主函数只负责传递参数,四个功能函数检验参数合法性。同时将输出形式分别用函数funcBaseT和函数funcBase来完成,由于功能一、二、三和功能4-1处理输入输出时基本一致,于是函数funcBase作为四个功能的输出处理函数,而功能4-2输入和输出和其他三种功能存在差异,所以我单独用一个函数funcBaseT负责其输出。

五、程序运行结果截图

  • 功能一:

  • 功能二:

  • 功能三:

  • 功能四:

可分解为功能4-1和功能4-2(手动输入和复制粘贴ctrl+C)。

六、关于词频统计的PSP

  就9/18日晚上正式写代码开始,预计的总时间与实际消耗时间差了36分钟左右。主要花费的时间是在做与程序相关的一些技术原型和对程序整体运行的流程设计。实际开始写代码,占据(9/18)总时间比例46.9%。事实上还有9/17晚上和9/18早上做一些技术原型花的时间(约600min)并没算进来。

词频统计V2.5的更多相关文章

  1. 【原创】大数据基础之词频统计Word Count

    对文件进行词频统计,是一个大数据领域的hello word级别的应用,来看下实现有多简单: 1 Linux单机处理 egrep -o "\b[[:alpha:]]+\b" test ...

  2. 作业3-个人项目<词频统计>

    上了一天的课,现在终于可以静下来更新我的博客了.       越来越发现,写博客是一种享受.来看看这次小林老师的“作战任务”.                词频统计 单词: 包含有4个或4个以上的字 ...

  3. C语言实现词频统计——第二版

    原需求 1.读取文件,文件内包可含英文字符,及常见标点,空格级换行符. 2.统计英文单词在本文件的出现次数 3.将统计结果排序 4.显示排序结果 新需求: 1.小文件输入. 为表明程序能跑 2.支持命 ...

  4. c语言实现词频统计

    需求: 1.设计一个词频统计软件,统计给定英文文章的单词频率. 2.文章中包含的标点不计入统计. 3.将统计结果以从大到小的排序方式输出. 设计: 1.因为是跨专业0.0···并不会c++和java, ...

  5. 软件工程第一次个人项目——词频统计by11061153柴泽华

    一.预计工程设计时间 明确要求: 15min: 查阅资料: 1h: 学习C++基础知识与特性: 4-5h: 主函数编写及输入输出部分: 0.5h: 文件的遍历: 1h: 编写两种模式的词频统计函数: ...

  6. python瓦登尔湖词频统计

    #瓦登尔湖词频统计: import string path = 'D:/python3/Walden.txt' with open(path,'r',encoding= 'utf-8') as tex ...

  7. Hadoop上的中文分词与词频统计实践 (有待学习 http://www.cnblogs.com/jiejue/archive/2012/12/16/2820788.html)

    解决问题的方案 Hadoop上的中文分词与词频统计实践 首先来推荐相关材料:http://xiaoxia.org/2011/12/18/map-reduce-program-of-rmm-word-c ...

  8. pyspark进行词频统计并返回topN

    Part I:词频统计并返回topN 统计的文本数据: what do you do how do you do how do you do how are you from operator imp ...

  9. 使用storm分别进行计数和词频统计

    计数 直接上代码 public class LocalStormSumTopology { public static void main(String[] agrs) { //Topology是通过 ...

随机推荐

  1. Python3 tkinter基础 Menu add_radiobutton 单选的下拉菜单

             Python : 3.7.0          OS : Ubuntu 18.04.1 LTS         IDE : PyCharm 2018.2.4       Conda ...

  2. Redis setnx命令 分布式缓存

    setnx命令 将 key 的值设为 value,当且仅当 key 不存在. 若给定的 key 已经存在,则 SETNX 不做任何动作. SETNX 是SET if Not eXists的简写. re ...

  3. python from entry to abandon

    学习Linux已经有大致两周了,依然感觉到自己仍然在运维的大门外徘徊.于是我想要找到一个在Linux之外的业余方向,可以以作为枯燥基础学习的调节.没过多久我就发现了Python可以说是钦定的选择,它作 ...

  4. 元注解——java.lang.annotation.Target(1.8)

    参考资料:https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Target.html 普通注解’只能用来注解’代码’,而’元注 ...

  5. RN中API之NetInfo--浅谈

    我们在做移动端项目和手机APP应用时,避免不了要获取用户手机的网络状况.在使用RN技术开发APP时,其内置的NetInfo API就是为了解决这一问题的.下面简单的讲下NetInfo如何使用. 最新的 ...

  6. babel-node + Express NodeJS项目搭建指南

    1.搭建Node.js环境 从官网下载安装 2.搭建Express环境 express 是 node.js的短精简的Web框架,官网:http://www.expressjs.com.cn/ 安装: ...

  7. e1000e 网卡如遇到大包未线速问题解法

    e1000e 网卡如遇到大包(>1280)未线速,把'DEFAULT_ITR'改为0, 不设中断频率上限试试 see@intel/e1000e/param.c/* Interrupt Throt ...

  8. hihocoder #1044 : 状态压缩·一 状压DP

    http://hihocoder.com/problemset/problem/1044 可以看出来每一位的选取只与前m位有关,我们把每个位置起始的前m位选取状态看出01序列,就可以作为一个数字来存储 ...

  9. 【Git】【1】简单介绍

    前言: Git:资源管理,版本控制:其实我之前用的是SVN,据说是不好管理分支,不过我做的项目参与人数不多,所以SVN其实是够用的 Git客户端:安转后,可以直接在文件夹进行管理,不需要用命令行形式管 ...

  10. map传参上下文赋值的问题

    今天开发遇到一个问题就是声明一个map<String,String> param ,给param赋值,明明有结果但是就是返回为空:下面附上代码: 因为在一个大的循环中,param是公用赋值 ...