大二课程设计中的一次小练习:
设计 C 或 C++ 程序,统计英文文本文件中,出现了多少个单词,每个单词出现了几次。连续的英文字符都认为单词 (不包括数字 ) ,单词之间用空格或标点符号分隔。
单词解析部分应该可以用正则表达式的,之后会进行补充。
第一次发博文,大家多来指点下哇!
/***********************************************************************
* File Name: WordsCount.cpp
* Author: 夜阳 Version: 1.0
* Created: 2012-6-18 23:28:22
* Last Change: 2012-6-20 9:04:44
* Discription: 统计英文文本文件中,出现了多少个单词,每个单词出现了几次
*
* 运行WordsCount.cpp
* 第一步输入待检索文件名(如TUT.txt)
* 第二步输入输出的文件名(如result.txt)
* 检索结果保存在result.txt中
***********************************************************************/
#include <stdio.h>
#include "SqList.h" // 线性表的存储与操作
int pickword ( FILE *f, char *fword );
int main()
{
FILE *f1, *f2;
// 指定默认文件名,用字符串保存文件名,便于操作
const int MAX_FILENAME = 20; // 文件名长度
char fname1[MAX_FILENAME] = "TUT.txt";
char fname2[MAX_FILENAME] = "result.txt";
// 用户指定源文件名
printf ( "请指定源文件名(不超过%d个字符):", MAX_FILENAME );
scanf ( "%s", fname1 );
// 用户指定输出文件名
printf ( "请指定要输出结果的文件名(不超过%d个字符):", MAX_FILENAME );
scanf ( "%s", fname2 );
//打开文件
if ( ( f1 = fopen ( fname1, "r" ) ) == NULL ) // 异常处理
{
printf ( "打开文件%s失败!\n", fname1 );
return 0;
}
else
printf ( "打开文件%s成功!\n", fname1 );
SqList L; // 建立线性表
SqListInit ( &L ); // 初始化线性表
char fword[MAX_CHARACTER]; // 使用fword数组保存文件中的单词
fword[MAX_CHARACTER - 1] = '\0';
int i = -1; // 设置i为插入位置
while ( !feof ( f1 ) ) // 读文件未结束
{
int judge = pickword ( f1, fword ); // 从f指向的文件中提取单词到fword中
if ( -1 == judge ) // 数组越界时提示并退出
{
printf ( "存在单词字符长度超过数组界限\n" );
return -1;
}
if ( SqListBSearch ( &L, fword, i ) ) // i返回插入位置或单词在线性表中位置
{
// 在线性表中找到该单词
L.elem[i].count++; // 单词出现次数加1
}
else
{
// 线性表中未找到该单词
SqListInsert ( &L, i, fword ); // 在第i个位置上插入
}
}
// 打开文件fname2,将内容写入
if ( ( f2 = fopen ( fname2, "w" ) ) == NULL ) // 异常处理
{
printf ( "写入文件%s失败!\n", fname2 );
return 0;
}
else
printf ( "文件已写入%s!\n", fname2 );
// 将结果写入f2指向的文件中
SqListPrint ( f2, fname1, &L );
// 关闭文件
fclose ( f1 );
fclose ( f2 );
}
int pickword ( FILE *f, char *fword ) // 从f指向的文件中提取单词到fword中
{
char ch; // ch储存待检测字符
for ( int j = 0 , flag = 0 ; !feof ( f ) ; ) // 逐个对字符进行检测,flag用于标记,为0时表示单词中无字母
{
if ( j >= MAX_CHARACTER ) // 判断数组是否越界
{
return -1;
}
ch = fgetc ( f ); // 获取字符
if ( ch >= 'A' && ch <= 'Z' ) // 大写字符转小写保存在fword数组中
{
fword[j++] = ch + 32;
flag = 1;
}
if ( ( ch >= 'a' && ch <= 'z' ) ) // 小写字符保存在fword数组中
{
fword[j++] = ch;
flag = 1;
}
if ( '-' == ch && fword[j - 1] >= 'a' && fword[j - 1] <= 'z' ) // 若单词中带连字符,将连字符保存在fword数组中
{
fword[j++] = ch;
}
if ( ! ( ( ch >= 'A' && ch <= 'Z' ) || ( ch >= 'a' && ch <= 'z' ) || '-' == ch )
&& flag == 1 ) // 过滤单词中的非字母字符
{
if ( fword[j - 1] == '-' ) // 排除类似于 a- 的单词
fword[j - 1] = '\0';
fword[j] = '\0'; // fword数组以'\0'结尾
return 0;
}
}
}
/***********************************************************************
* File Name: SqList.h
* Author: 夜阳 Version: 1.0
* Created: 2012-6-18 10:25:27
* Last Change: 2012-6-19 21:36:53
* Discription: 顺序表的初始化,建立,插入,查找,相关结果输出
***********************************************************************/
#include <stdio.h>
#include <malloc.h>
#include <string.h>
const int MAX_CHARACTER = 50; // 单词最大长度定为50(TUT.txt文中最长单词为production-education-researching)
//函数结果状态代码
#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0
#define OVERFLOW -2
// 线性表的动态分配顺序存储结构
#define LIST_INIT_SIZE 100 // 线性表存储空间的初始分配量
#define LISTINCREMENT 10 // 线性表存储空间的分配增量
// 顺序存储结构的线性表类型
typedef struct
{
char word[MAX_CHARACTER]; // 存储单词,不超过50个字符
int count; // 单词出现次数
} ElemType;
typedef struct
{
ElemType *elem; // 存储空间基址
int length; // 当前长度
int listsize; // 当前分配的存储容量(以sizeof(ElemType)为单位)
} SqList;
// 线性表的初始化
int SqListInit ( SqList *L )
{
// 构造一个空的线性表L
L->elem = ( ElemType * ) malloc ( LIST_INIT_SIZE * sizeof ( ElemType ) );
if ( !L->elem ) return OVERFLOW; // 存储分配失败
L->length = 0;
L->listsize = LIST_INIT_SIZE;
return OK;
}
// 线性表的插入
int SqListInsert ( SqList *L, int i, char *fword )
{
// 在顺序线性表L中第i个位置之前插入新的元素e
// i的合法值为1≤i≤L.Length + 1
if ( i < 0 || i > L->length )
{
printf ( "i的值不合法!" );
return ERROR; // i的值不合法
}
if ( L->length >= L->listsize )
{
// 当前存储空间已满,增加分配
ElemType *newbase = ( ElemType * ) realloc ( L->elem,
( L->listsize + LISTINCREMENT ) * sizeof ( ElemType ) );
if ( !newbase )
return OVERFLOW; // 存储分配失败
L->elem = newbase; // 新基址
L->listsize += LISTINCREMENT; // 增加存储容量
}
ElemType *p, *q;
q = &L->elem[i];
for ( p = &L->elem[L->length - 1]; p >= q; p-- ) // 插入位置之后元素逐个右移
{
* ( p + 1 ) = *p;
}
strcpy ( q->word, fword ); // 复制fword中的字符到L->elem[i-1].word中
L->elem[i].count = 1; // 设置计数初值为1
L->length++; // 表长增1
return OK;
}
// 线性表二分法查找
int SqListBSearch ( SqList *L, char *sword, int &i )
{
if ( L->length == 0 ) // 当线性表为空时
{
i = 0; // i返回单词插入位置
return ERROR;
}
// 线性表不空时,在线性表L中查找元素sword,用i返回其在线性表中的位置
int low = 0, high = L->length - 1, mid = L->length;
while ( low <= high )
{
mid = ( low + high ) / 2;
int k = strcmp ( L->elem[mid].word, sword );
if ( k == 0 ) // 待查单词sword等于中间值,找到待查元素
{
i = mid;
return OK; // 查找成功,函数返回值为1,用i返回所查元素在线性表中的位置
}
else if ( k > 0 ) // 待查单词sword小于中间值,继续在前半区间进行查找
{
high = mid - 1;
i = low;
}
else // 待查单词sword大于中间值,继续在后半区间进行查找
{
low = mid + 1;
i = high + 1;
}
}
return ERROR; // 线性表中不存在待查元素,函数返回值为0,i返回单词插入位置
}
void SqListPrint ( FILE *f, char fname[], SqList *L )
{
fprintf ( f, "文档 %s 中总计有%d个单词\n", fname, L->length ); // 输出统计信息,L->length为单词个数
fprintf ( f, "序号 单词 个数\n" );
for ( int i = 0, j = 1 ; i < L->length ; i++, j++ ) // j-序号
fprintf ( f, "%-5d %-20s %d\n", j, L->elem[i].word, L->elem[i].count );
}
结果显示: