如何准确统计备忘录中的字数?
How to accurately count words in a Memo?
我正在尝试制作一个记事本克隆,并在状态栏中增加 运行 字数统计功能。字数统计不准确,重复space或回车return算新词。这是我尝试过的字数统计功能:
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;
当然,我愿意接受任何替代方案或改进。我真的不明白为什么这段代码会增加每个 space 和回车符 return 的字数 (count
)。我认为在用户点击 space 条(递增 count
)之后,变量 inWord
现在应该为 False,因此如果用户点击 if (s[i] in wordSeparatorSet) and (inWord=True)
应该解析为 False space 栏或 Enter 键第二次。但事实并非如此。
I really don't understand why this code increases the word count (count
) with every space and carriage return.
在单词后的第一个 space,您确实将 inWord
设置为 False
。因此,如果下一个字符也是 space,您将(错误地)运行 inWord := True
,因此如果下一个(第三个)字符再次是 space,您将(错误地)做 Inc(count)
.
您还可以注意到 (s[i] in wordSeparatorSet) and (inWord=True)
的否定并不意味着“字符不是定界符”,因为与 inWord
的连词。 (s[i] in wordSeparatorSet) and (inWord=True)
的否定是 De Morgan,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.
我正在尝试制作一个记事本克隆,并在状态栏中增加 运行 字数统计功能。字数统计不准确,重复space或回车return算新词。这是我尝试过的字数统计功能:
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;
当然,我愿意接受任何替代方案或改进。我真的不明白为什么这段代码会增加每个 space 和回车符 return 的字数 (count
)。我认为在用户点击 space 条(递增 count
)之后,变量 inWord
现在应该为 False,因此如果用户点击 if (s[i] in wordSeparatorSet) and (inWord=True)
应该解析为 False space 栏或 Enter 键第二次。但事实并非如此。
I really don't understand why this code increases the word count (
count
) with every space and carriage return.
在单词后的第一个 space,您确实将 inWord
设置为 False
。因此,如果下一个字符也是 space,您将(错误地)运行 inWord := True
,因此如果下一个(第三个)字符再次是 space,您将(错误地)做 Inc(count)
.
您还可以注意到 (s[i] in wordSeparatorSet) and (inWord=True)
的否定并不意味着“字符不是定界符”,因为与 inWord
的连词。 (s[i] in wordSeparatorSet) and (inWord=True)
的否定是 De Morgan,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.