我正在尝试创建一个记事本克隆,并在状态栏中添加了运行中字数统计的功能。单词计数不准确,将重复的空格或回车符作为新单词计数。这是我尝试的字数统计功能的方法:
procedure TFormMain.Memo1Change(Sender: TObject);
var
wordSeparatorSet: Set of Char;
count: integer;
i: integer;
s: string;
inWord: Boolean;
begin
wordSeparatorSet := [#13, #32]; // CR, space
count := 0;
s := Memo1.Text;
inWord := False;
for i := 1 to Length(s) do
begin
// if the char is a CR or space, you're at the end of a word; increase the count
if (s[i] in wordSeparatorSet) and (inWord=True) then
begin
Inc(count);
inWord := False;
end
else
// the char is not a delimiter, so you're in a word
begin
inWord := True;
end;
end;
// OK, all done counting. If you're still inside a word, don't forget to count it too
if inWord then
Inc(count);
StatusBar1.Panels[0].Text := 'Words: ' + IntToStr(count);
end;
当然,我愿意接受任何替代或改进。我真的不明白为什么这段代码在每次空格和回车符时都会增加字数(count
)。我认为用户点击空格键(递增count
)后,变量inWord
现在应该为False,因此如果用户点击空格键或Enter,if (s[i] in wordSeparatorSet) and (inWord=True)
应该解析为False。再次按键。但这不是事实。
答案 0 :(得分:2)
我真的不明白为什么这段代码在每次空格和回车符时都会增加字数(
count
)。
在单词后面的第一个空格处,确实确实将inWord
设置为False
。因此,如果下一个字符也是一个空格,您将(错误地)运行inWord := True
,因此,如果下一个(第三个)字符再次是空格,您将(错误地)执行Inc(count)
。>
您还可以注意到,(s[i] in wordSeparatorSet) and (inWord=True)
的否定并不意味着“与字符{不是分隔符”,因为它与inWord
一起使用。 De Morgan对(s[i] in wordSeparatorSet) and (inWord=True)
的取反是not (s[i] in wordSeparatorSet) or not (inWord=True)
,这与not (s[i] in wordSeparatorSet)
是不同的。
固定版本看起来更像
function WordCount(const AText: string): Integer;
var
InWord: Boolean;
i: Integer;
begin
Result := 0;
InWord := False;
for i := 1 to Length(AText) do
if InWord then
begin
if IsWordSep(AText[i]) then
InWord := False;
end
else
begin
if not IsWordSep(AText[i]) then
begin
InWord := True;
Inc(Result);
end;
end;
end;
其中IsWordSep(chr)
的定义类似于chr.IsWhitespace
,但是有许多微妙之处,正如我在on my web site中所讨论的。