我做了一个程序,其中给出的输入应该是字符流和程序计数 非空白字符和单词。 word被定义为由空白字符分隔的字符流。所以这是程序..
#include <stdio.h>
#include<ctype.h>
#include <stdbool.h>
#include<iso646.h>
int main(void)
{ unsigned long int wordcount = 0,charcount = 0, count=1;
int ch;
bool flag, prev;
while ((ch = getchar()) != EOF)
{ if(isgraph(ch))
flag=true;
else
flag=false;
if(flag)
charcount++;
if(count ==1)
prev = flag;
if(count != 1)
{ if(prev and (not flag))
wordcount++;
prev = flag;
}
count++;
}
if((ch == EOF) and flag)
wordcount++;
printf("\nnumber of words counted are %lu \n", wordcount);
printf("\nnumber of characters counted are %lu \n", charcount);
return 0;
}
现在我用简单的句子检查了这个程序。但只是为了练习,我想做 详细的软件测试。那我该怎么办呢?我只是给出更多的句子吗?我试图从项目古腾堡的一些小说中给出一些段落。 我还能在这做什么?我也可以提高这个程序的效率吗?
答案 0 :(得分:3)
有各种基本测试:
所以它继续......这是边界测试;确保代码在边界条件下正常工作。
您从getchar()
到unsigned long int
(现在已在问题中修复)的值分配是不寻常的。由于常规字符的返回值为正,文件结尾或错误的返回值为负(EOF),因此将其分配给签名的普通int
是正常的。
循环后的测试ch == EOF
是多余的;退出循环的唯一方法是条件为真。
使用<iso646.h>
和(宏)关键字and
和not
也不常见。
最常见的是,人们不会将代码放在与块的开括号相同的行上。
您可以在设置charcount
的{{1}}块中增加if
。您可以使用flag = true;
块而不是else
。事实上,AFAICT,你的代码:
if (count != 1)
可以写成:
if(count ==1)
prev = flag;
if(count != 1)
{ if(prev and (not flag))
wordcount++;
prev = flag;
}
描述'计算的字符数'并不严格准确;它是您报告的图形(非空白,非控制)字符的数量。然而,这可能是关于繁琐程度的过度挑剔的结果(同时观察到'单词数量'是一个单数量且应该是'是'而不是'是')。
将if (count > 1 and prev and (not flag))
wordcount++;
prev = flag;
设为1而不是零,这有点不寻常。它似乎记录了“比读入程序的原始字符数多一个”,这是一个不寻常的记录数量。更常见的是,你也将它初始化为0,并修改我重写的测试:
count
(您可以使用if (count != 0 and prev and (not flag))
或count != 0
;对于无符号值,这些字词是等效的。)
您可以通过恰当地初始化count > 0
(可能是prev
)来简化您的条件。
答案 1 :(得分:0)
养成在左侧测试常量的习惯,如
while(EOF!=(ch = getchar()))
...因为这会让你免于花费无数个小时,就像你最不能承受挫折,当你意外地输入=当你的意思是==时。由于您无法将变量分配给常量,编译器将标记您的错误,并保存您的对接。
根据我的经验,一旦你习惯了阅读这种代码,你就会发现当找到if(旁边的那个)时,找到正在测试的内容要快得多(,而不是在某个地方寻找它)测试的主体。如果您有很长的测试列表,例如打开文件,套接字等,然后通过malloc()分配内存来保存文件数据,则尤其如此。
PS:经过一些检查,有一些基本的CS 101值得一提......
第1,你有一个经典案例 - 在这种情况下,因为你需要在一个字符后面查看,即使是在第一次通过while()循环时 - 用于播种while()循环。解决方案是使用简单的if()块设置while()循环,该块执行与while()循环相同的逻辑。 (FYI,while()是一组无限的if()s,具有终止条件)
这样做的正确方法如图所示。回报是能够抛出所有if()测试,并检查这是否是每次通过该循环的第一次通过while()循环。这里的第一次传递由while()循环之前的if()测试处理。
第二,我发现你的变量名不具备信息。这并不意味着它们是“错误的”,但可能有人试图维护你的代码也会很困难。根据我的经验,随着您更好地理解一段代码,变量名称变得越来越好。使用它作为一个试金石测试你是否理解问题,有一个很好的解决方案,并知道原因。
第3,当你发现自己将main()中的变量初始化为1时,它应该在你的脑海中提出一个关于正确流控制的标志,就像现在的PassKnt一样,被设置为1.另外,一般来说,你想要在循环的END / if / while处递增循环计数器,而不是它的开头。同样,这应该让你质疑你的逻辑。
注意:默认情况下,记事本以unicode格式保存。如果使用记事本为此程序创建测试文件,请务必以 ANSI 格式保存。
我把它留了下来,因为它使程序更容易理解,但这里不需要IsGraphFlg。不是将IsGraphFlg分配给循环底部的WasGraphFlg,而是可以在if-else块的顶部和底部两半的底部完成,因为上下文提供与IsGraphFlag相同的信息。
while (EOF != (ch = fgetc(pFile))) {
if(isgraph(ch)) {
IsGraphFlg=true;
charcount++;
} else { // this char is whitespace, last char was part of a word
IsGraphFlg=false;
if(WasGraphFlg) {
wordcount++;
}
}
WasGraphFlg = IsGraphFlg;
PassKnt++;
}
你可能也注意到PassKnt现在也没有任何用处,不再需要。
有人建议isgraph()是最佳的,但是当我创建一个bool数组并使用isgraph()对其进行初始化时,代码就会运行(从内存缓冲区中运行,这个数据的速度是此文件的10倍〜戴尔XPS 8500)在不到2/3的时间内 - 9.25个时钟而不是每个字符14.75个。这是一个完全可选的优化 - 虽然是一个重要的优化。
bool IsGph[256];
for(i=0; i<sizeof(IsGph); i++) {
IsGph[i] = isgraph((unsigned char)i);
}
在使用中,if(isgraph(i))被主字符和字计数循环中的if(IsGph [i])替换。
代码已更新12/30/2012
// Word_Counter.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "stdafx.h"
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <memory.h>
#include <locale>
#define UCHAR unsigned char
#define dbl double
#define LLONG __int64
#define PROCESSOR_HZ ((LLONG) 3400000000)
#pragma warning(disable : 4996)
//
// function prototypes
FILE *OpenFiles (int *FileSz, char *FileName);
// -----------------------------------------------------------------------
FILE *OpenFiles (int *FileSz, char *FileName) {
FILE *pFile=NULL;
if (NULL == (pFile = fopen ((char *)FileName, "r+t" ))) {
printf ( "Can't open %s\n", FileName );
return NULL;
} else {
fseek(pFile,0,SEEK_END);
*FileSz = ftell(pFile);
rewind(pFile);
printf("\nFile size is %i", *FileSz);
return pFile;
}
}
// -----------------------------------------------------------------------
int _tmain(int argc, char *argv[]) {
bool IsGph[256];
UCHAR *p, *pBuff=NULL;
int WrdKnt=0,CharKnt=0;
int i, j, FileSz, LoopKnt=3500;
time_t Etime=0,start=0, Eclocks=0;
FILE *pFile=NULL;
bool WasGraphFlg=false;
// Initialize boolean array to detect printable characters
for(i=0; i<sizeof(IsGph); i++) {
IsGph[i] = isgraph((unsigned char)i);
}
if(NULL == (pFile = OpenFiles(&FileSz, (char *)argv[1]))) {
return 0;
}
// --- Process out of buffer, not stdin -------------------------------
pBuff = (unsigned char *)calloc(FileSz, sizeof(char));
fread(pBuff, sizeof(char), FileSz, pFile);
start = clock();
for(i=LoopKnt; i; i--) {
p= pBuff;
CharKnt=0;
WrdKnt=0;
for(j=FileSz; j; j--) {
if(IsGph[*p++]) {
CharKnt++;
WasGraphFlg = true;
} else { // this char is whitespace, and
if(WasGraphFlg) { // last char was part of word ?
WrdKnt++;
}
WasGraphFlg = false;
}
}
}
Etime = clock() - start;
Eclocks= Etime * PROCESSOR_HZ/(LLONG) CLOCKS_PER_SEC;
printf("\nElapsed time for %10i loops was %10i milliseconds",
LoopKnt, Etime);
printf("\nCPU cycles consumed per char were %2f\n",
(dbl)Eclocks/(dbl)((LLONG)FileSz*(LLONG)LoopKnt));
printf("\n%i words counted per loop", WrdKnt);
printf("\n%i chars counted per loop\n", CharKnt);
getchar();
return _fcloseall();
free(pBuff);
}
如果您的命令行参数指定文件名时遇到问题,请在Visual Studio中的“项目 - >>属性 - >配置属性 - >常规”下,将“Unicode”更改为严重错误的“Multi” -Byte“字符集。您总是可以在调试器中查看argv [1]的内存,以找出argv []中实际存在的内容。