次のように、テストする定数を左側に配置する習慣を身につけてください。
while (EOF != (ch = getchar()))
...これにより、== の意味で誤って = を入力してしまったときに、最も後退する余裕がないときに、数え切れないほどの時間を費やす必要がなくなります。変数を定数に割り当てることはできないため、コンパイラはエラーにフラグを立てて、お尻を救います。
私の経験では、この種のコードを読むことに慣れると、 if(, while(, の本体のどこかで探すよりも、 if(, while(, ) のすぐ隣にある方がテスト対象を見つける方がはるかに速くなります。これは、ファイルやソケットなどを開き、ファイル データを保持するために malloc() を介してメモリを割り当てるなど、テストの長いリストがある場合に特に当てはまります。
PS: いくつかの検討の後、いくつかの基本的な CS 101 について言及する価値があります...
1 つ目は、ここに古典的なケースがあります。この場合、while() ループを介した最初のパスであっても、1 文字後ろを見る必要があるためです。while() ループをシードするためです。解決策は、while() ループと同じロジックを 1 回通過する単純な if() ブロックを使用して while() ループを設定することです。(参考までに、while() は終了条件付きの無限の if() のセットです)
これを行う適切な方法は次のとおりです。その見返りは、これが while() ループを通過するたびにその最初のパスであるかどうかを確認することで、付随するすべての if() テストを捨てることができることです。ここでの最初のパスは、while() ループの前にある if() テストによって処理されます。
第 2 に、変数名が有益ではないことがわかりました。それは彼らが「間違っていた」という意味ではありませんが、あなたのコードを維持しようとする人も同様に苦労する可能性があります. 私の経験では、コードの一部をよりよく理解するにつれて、変数名はますます良くなります。それを、問題を理解しているかどうか、適切な解決策があるかどうか、およびその理由を知っているかどうかのリトマス試験紙として使用してください。
第 3 に、main() 内の変数を 1 に初期化していることに気付いた場合、現在の PassKnt が 1 に設定されているように、正しいフロー制御についてのフラグが頭に浮かぶはずです。 loop/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() を使用して初期化すると、コードが実行されました (メモリ バッファーが不足しました。これは、この Dell XPS 8500 のファイルからの速度の約 10 倍です)。 ) 時間の 3 分の 2 弱 - 1 文字あたり 14.75 クロックではなく 9.25 クロック。これは完全にオプションの最適化ですが、重要なものです。
bool IsGph[256];
for(i=0; i<sizeof(IsGph); i++) {
IsGph[i] = isgraph((unsigned char)i);
}
使用中、if(isgraph(i)) は、メイン キャラクターと単語カウント ループで if(IsGph[i]) に置き換えられます。
コード更新日: 2012 年 12 月 30 日
// 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" をひどく誤ったラベルの "マルチバイト" 文字セットに変更します。デバッガーで argv[1] のメモリをいつでも調べて、実際に argv[] にあるものを見つけることができます。