|
1.1 英汉汉英词典
1.1.1 英汉汉英词典设计方法
在LabWindows/CVI 程序设计中,经常要参考和查询一些函数,阅读英文说明文档,英汉词典是不可或缺的工具。常用的电子词典主要是有道、金山等,一般的词典安装程序至少也在几兆大小,安装之后可能还会带有插件,能不能设计一个更简洁的词典程序,只要满足日常需要就可以呢?对于简洁的词典程序,其词库最好为文本类型,不使用任何数据库产品,减少冗余。在本例程中,利用字符串查找与匹配算法,制作了一个简单的英汉汉英电子词典,实现单词的查找释义、汉字的英文对照。
1.1.2 英汉汉英词典程序设计
(1)面板设计
编写一个英汉汉英词典查询程序,在文本框内输入英文单词或中文,程序会打开当前目录下的“英汉汉英词典.txt ”词典文件,查找与输入相匹配项,并将匹配度最高的结果及其后续的相关结果显示出来。面板设计如图1-1 所示,面板中主要控件属性设置如表1-1 所示。
图1-1 英汉汉英词典面板设计
表1-1 控件属性设置表
常量名
| 控件类型
| 控件的主要属性
| PANEL
| Panel
| 标题:英汉汉英词典回调函数:PanelCB
| STRING_FIND
| String
| 回调函数:Find
| TEXTBOX
| Text Box
| ( 显示查询结果)
|
(2)程序源代码
//头文件声明
#include <ansi_c.h>
#include <formatio.h>
#include <cvirte.h>
#include <userint.h>
#include "英汉汉英词典.h"
//全局静态变量
static int BytesRead;
static int FileHandle;
static char *buffer;
static int panelHandle; //主函数
int main (int argc, char *argv[])
{
long FileSize;
if (InitCVIRTE (0, argv, 0) == 0)
return -1; /* out of memory */ if ((panelHandle = LoadPanel (0, " 英汉汉英词典.uir", PANEL)) < 0)
return -1;
//打开字典文件
FileHandle = OpenFile (" 英汉汉英词典.txt", VAL_READ_ONLY, VAL_APPEND, VAL_ASCII);
//获得字典文件信息
GetFileInfo (" 英汉汉英词典.txt", &FileSize);
//动态分配内存
buffer = malloc (FileSize * sizeof(char) + 1); //初始化动态分配内存
buffer[0] = '\0'; //读取字典文件内容
BytesRead = ReadFile (FileHandle, buffer, FileSize * sizeof(char));
DisplayPanel (panelHandle);
RunUserInterface (); //释放字典文件占用的内存
free (buffer);
CloseFile (FileHandle);
DiscardPanel (panelHandle);
return 0;
} //面板回调函数
int CVICALLBACK PanelCB (int panel, int event, void *callbackData,
int eventData1, int eventData2)
{
switch (event)
{
case EVENT_CLOSE:
QuitUserInterface (0);
break;
}
return 0;
}
//查找文 本框回调函数
int CVIC ALLBACK Find (int panel, int control, int event,
void *callbackData, int eventData1, int eventData2)
{
int Index;
int len;
char *str;
int i;
int j;
stat ic int flag = 0;
int num;
char *temp;
switch (event)
{
case EVENT_VAL_CHANGED:
// 获得输入文本长度
GetCtrlAttribute (panelHandle, PANEL_STRING_FIND, ATTR_STRING_TEXT_LENGTH,
&len);
// 将获得查找字符串
str = malloc (len * sizeof(char) + 1);
str[0] = '\0';
GetCtrlVal (panelHandle, PANEL_STRING_FIND, str);
flag = 0;
// 字符串匹配
for (i = 0; i < BytesRead; i ++)
{
// 标志清零
flag = 0;
// 判断字符串是否匹配
for (j = 0; j < len; j ++)
{
// 字符串匹配并且遇到换行,标志自增,否则查询下一个,标志清零
if ((str[j] == buffer[i+j]) && ((i == 0) || (buffer[i-1<=0?0:i-1] == 0x0a)))
{
flag ++;
}
else
{
flag = 0;
continue;
}
}
// 如果字符串匹配,显示四组左右连续查询结果
if (flag == len)
{
// 清空以前查询内容
ResetTextBox (panelHandle, PANEL_TEXTBOX, "");
num = 0;
j = 0;
// 记录四组数据的长度,每组以换行符结束
while (j <= 4)
{
if (*(buffer + num) == 0x0a)
{
j ++;
}
num ++;
}
temp = malloc (num * sizeof(char) + 1);
temp[0] = '\0';
memcpy (temp, buffer + i, num);
temp[num<0?0:num] = '\0';
// 查找字符串中是否存在换行符
Index = FindPattern (temp, 0, -1, "\n", 0, 1);
// 如果不存在匹配字符串,清空temp ,否则,将换行符改为结束符
if (Index == -1)
{
temp[0] = '\0';
}
else
{
temp[Index] = '\0';
}
// 显示查询结果
SetCtrlVal (panelHandle, PANEL_TEXTBOX, temp);
free (temp);
break;
}
}
// 清空查询内容
if (len == 0)
{
ResetTextBox (panelHandle, PANEL_TEXTBOX, "");
}
// 释放资源
free (str);
break;
}
return 0;
}
(3)程序注释
① OpenFile 函数打开文件,设置文件的读写操作,并返回文件句柄以备后续操作。注意,在使用完成之后,应当用CloseFile 函数释放资源。由于Windows API 中也包含有同名函数,需要把windows.h 头文件放在formatio.h 头文件之前,以避免产生编译错误。函数原型为:
int OpenFile (char File_Name[], int ReadWrite_Mode, int Action, int File_Type); File_Name[] :指定打开的文件名。ReadWrite :指定文件读写方式,默认为只读方式。如果为写或读写方式,当指定的文件不存在时,函数会先创建该文件。可以用GetFileInfo 函数判断文件是否存在。读写方式参数说明如表1-2 所示。
表1-2 读写方式参数说明表
常量名
| 常量值
| 说明
| VAL_READ_WRITE
| 0
| 以读写方式打开文件
| VAL_READ_ONLY
| 1
| 以只读方式打开文件
| VAL_WRITE_ONLY
| 2
| 以只写方式打开文件
|
Action:指定是否要删除以前的文件内容以及写操作时文件指针的位置,只有当写或读写方式时有效。操作方式说明如表1-3 所示。
表1-3 操作方式说明表
常量名
| 常量值
| 说明
| VAL_TRUNCATE
| 0
| 文件指针定位在开始位置,删除以前文件内容
| VAL_APPEND
| 1
| 将新文件内容添加到原有文件的末尾
| VAL_OPEN_AS_IS
| 2
| 文件指针定位在开始位置,不影响以前文件内容
|
File_Type :指定文件类型为ASCII 码或二进制类型。如果以ASCII 码类型打开文件,当为读方式时,回车/换行(CR/LF )被转换为换行,当为写方式时,换行被转换为回车/换行。文件类型如表1-4 所示。
表1-4 文件类型表
常量名
| 常量值
| 说明
| VAL_BINARY
| 0
| 以二进制模式打开文件
| VAL_ASCII
| 1
| 以ASCII 码模式打开文件
|
返回值:文件句柄。如果返回值为-1,表示有错误产生,函数调用失败,可能是文件无法打开或参数传递错误。
② ReadFile 函数将数据从文件或标准输入端口读到缓冲区。读指针位于文件指针开始位置,当读完一个字节后,文件指针指向下一个未读字节,读到NULL 结束符时,文件读操作结束。
int ReadFile (int fileHandle, char buffer[], int count);
fileHandle:文件句柄。
buffer[] :读取文件缓冲区。
Count :最大读取字节数,不应大于缓冲区大小。
返回值:实际读取字节数。-1 表示有错误产生,0 表示没有读到任何字节。
③ CloseFile 函数关闭文件。函数原型为:int CloseFile (int File_Handle);File_Handle:要关闭的文件句柄。
④ FindPattern 函数在指定的字符串缓冲区中查找匹配字符串,并返回匹配字符串在字符串缓冲区中出现的位置信息。函数原型为:
int FindPattern (char *buffer, int startingIndex, int numberOfBytes, char *pattern, int caseSensitive, int startFromRight);
*buffer :字符串缓冲区。startingIndex :开始查找的位置索引,从0 开始计数,类似于字符串数组的下标。numberOfBytes :如果输入-1,函数从startingIndex 位置索引开始查找,直到字符串缓冲区结
束为止。如果输入值超过UINT_MAX(0xffffffff),函数返回一个错误号。
*pattern :指定查找的匹配字符串。caseSensitive :是否大小写敏感。如果为0,则大小写不敏感,非0 值表示大小写敏感。startFromRight :指定是从左向右匹配或是从右向左匹配。如果为0,则从最左边开始查找匹
配字符串,如果为非0 值,则从最右边开始查找匹配字符串。返回值:返回在缓冲区中找到的第一个匹配字符串索引。如果没有匹配字符串,则返回-1。举例说明:
index = FindPattern ("1ab2ab3ab4", 3, -1, "AB", 0, 0);
函数返回值index 为4,不返回1 的原因是,虽然从最左边开始查找,但计数初值从3 开始,且不区分大小写。
index = FindPattern ("1ab2ab3ab4", 3, -1, "AB", 0, 1);
函数返回值index 为7,从最右边开始查找,且不区分大小写。
⑤ memcpy 函数从源缓冲区复制指定数目的字符串到目标缓冲区。如果源缓冲区与目标缓
冲区存在重叠,需要使用memmove 函数。函数原型为:void *memcpy (void *targetBuffer, const void *sourceBuffer, size_t number_ofBytes);
*targetBuffer :目标缓冲区。
*sourceBuffer :源缓冲区。number_ofBytes :复制字节数目。返回值:目标缓冲区指针。
⑥ EVENT_VAL_CHANGED 事件常量当用户改变控件中的值时将持续触发该事件。在本例程中,当输入到String 控件中的字母或中文字符发生改变时,其查询结果也会随之发生变化。若采用EVENT_COMMIT 事件,只有当输入完成并按下回车键时,String 控件的回调事件才能获得响应,较EVENT_VAL_CHANGED 事件,反应速度慢,有显示滞后的感觉,与常用的英汉词典程序比较,用户体验差距较大。
⑦ 可能会出现的编译错误问题当在查询文本框内输入中文之后,点击标题栏的退出按钮,可能会弹出致命运行时错误对话框,如图1-2 所示。
图1-2 致命运行时错误对话框
该对话框显示为一个不可调试的“通用保护”错误,并给出了错误线程。这主要是由于在程序中采用了中文字符,在进行字符串操作时引起了严重的错误。作者考虑为编译器的语法分析部分产生了问题,由于一个中文字符由两个字节组成,编译系统没有较好考虑双字节数据在作为字符时与将其拆分为两个单独字节时的问题,并且可能在繁体中文、日文和韩文中也同样存在此问题。选择菜单Window→Threads,弹出Threads 对话框,查看线程错误信息,如图1-3 所示。从线程提示中,得不到任何与调试相关的提示信息。
图1-3 Threads 对话框
(4)运行效果图
点击工具栏中的Debug Project 按钮,程序开始运行,其效果如图1-4 所示。
图1-4 运行效果图 |
|