如何准确计算备忘录中的单词数?

时间:2020-11-03 19:21:06

标签: delphi word-count

我正在尝试创建一个记事本克隆,并在状态栏中添加了运行中字数统计的功能。单词计数不准确,将重复的空格或回车符作为新单词计数。这是我尝试的字数统计功能的方法:

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。再次按键。但这不是事实。

1 个答案:

答案 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中所讨论的。