将字符串复制到StringGrid中?

6
我想将Memo的内容复制到TStringGrid中。
如果字符串之间有空格或间隙,则该单词应添加到StringGrid中的自己的单元格中。
因此,假设我的Wordwrapped Memo包含以下信息:
如何将该信息复制到StringGrid中?
为了说明结果应如何,请参考以下示例图像:
重要的是要知道,我不会总是知道要使用多少列,例如,如果从文本文件加载Memo。
也许预定义的列数会更好,例如5或6列。行数也未知。
我该如何做到这一点?

如果字符串网格中的单元格数量不足怎么办?应该增加列数、行数(或两者都增加,如果是这样,如何增加?)还是应该忽略剩余的单元格? - Andreas Rejbrand
我认为增加/添加行数会更好 - 这样我就可以保持网格的宽度在可接受的范围内。我认为这比添加更多列来防止网格过宽要好。 - user1175743
Windows的EDIT控件是否公开有关文本换行的信息。如果您无法要求控件告诉您它如何换行文本,那么您基本上就没有办法了。 - David Heffernan
对于一个有多个创意解决方案的问题,给予加1。 - NGLN
3个回答

5
如果我理解正确,那么这应该可以解决问题:
procedure TForm1.FormClick(Sender: TObject);
type
  TWordPos = record
    Start, &End: integer;
  end;
const
  ALLOC_BY = 1024;
var
  Words: array of TWordPos;
  ActualLength, i: integer;
  txt: string;
  ThisWhite, PrevWhite: boolean;
begin

  ActualLength := 0;
  txt := Memo1.Text;
  PrevWhite := true;
  for i := 1 to Length(txt) do
  begin
    ThisWhite := Character.IsWhiteSpace(txt[i]);
    if PrevWhite and not ThisWhite then
    begin
      if ActualLength = Length(Words) then
        SetLength(Words, Length(Words) + ALLOC_BY);
      Words[ActualLength].Start := i;
      inc(ActualLength);
      PrevWhite := false;
    end else if (ActualLength>0) and ThisWhite then
      Words[ActualLength - 1].&End := i;
    PrevWhite := ThisWhite;
  end;

  SetLength(Words, ActualLength);

  StringGrid1.RowCount := Ceil(Length(Words) / StringGrid1.ColCount);

  for i := 0 to Length(Words) - 1 do
  begin
    StringGrid1.Cells[i mod StringGrid1.ColCount, i div StringGrid1.ColCount] :=
      Copy(Memo1.Text, Words[i].Start, Words[i].&End - Words[i].Start);
  end;

end;

Screenshot


3
噢,真不好啊!仅仅因为你能做到并不代表你应该这样做!&End. - David Heffernan
这完全正确,非常感谢Andreas。你们专家是如何做到这些的??你们总是让它看起来如此简单和不费力! - user1175743
@DavidHeffernan为什么这样做是不好的事情? - user1175743
4
@blobby 这个程序最好分成两部分。一部分用于执行标记化或拆分操作,另一部分用于填充网格。这样你就可以重复使用标记化操作了。然而,肯定有好的标记器可用,而不必自己编写。有人知道 Delphi 是否在 rtl 中提供了这个工具,或者是否有一个好的轻量级第三方库吗? - David Heffernan
令牌化器是什么意思,这是否意味着特殊字符?在我的情况下,字母之间的空格是一个令牌吗? - user1175743
显示剩余2条评论

5

在RTL中有一个Tokenizer(由David评论),它可以使用您选择的分隔符将文本拆分为单词。

这个例子来自Olaf Moinen在Zarko Gaijic的一篇文章中的评论:how-to-split-a-delphi-string-to-words-tokens.htm

uses HTTPUtil;

procedure TForm1.Button1Click(Sender: TObject);
var
  LTokenizer: IStringTokenizer;
begin
  Memo1.Clear;
  LTokenizer := StringTokenizer(Edit1.Text, ' ');
  while LTokenizer.hasMoreTokens do
  Memo1.Lines.Add(LTokenizer.nextToken);
end;

该功能将从编辑控件中获取文本并将其放入备忘录中。 我将把如何将备忘录内容放入字符串网格视图中作为练习留给您。


1
+1 现在这才是我想说的!借用Sebatien Vettel的话来说,非常棒。 - David Heffernan
+1 感谢分享 LU RD。当我能够理解它并知道它的工作原理时,我相信它一定会派上用场。 - user1175743
2
很高兴知道有一个rtl分词器,我已经使用hyperstr的分词器很多年了,但它不再受支持,因此无法移植到D2010。 - Richard A

2

TStringGrid具有填充不存在的单元格的功能,即超出ColCount * RowCount的单元格。因此,在填写字符串网格之前不需要计算单词数。

然后,一个简单直接的方法如下:

procedure TForm1.Button1Click(Sender: TObject);
var
  WordCount: Integer;
  WordStart: Integer;
  S: String;
  I: Integer;
begin
  WordCount := 0;
  WordStart := 1;
  S := Memo.Text + ' ';
  for I := 1 to Length(S) do
    if S[I] = ' ' then
    begin
      if WordStart <> I then
      begin
        Grid.Cells[WordCount mod Grid.ColCount, WordCount div Grid.ColCount] :=
          Copy(S, WordStart, I - WordStart);
        Inc(WordCount);
      end;
      WordStart := I + 1;
    end;
  Grid.RowCount := ((WordCount - 1) div Grid.ColCount) + 1;
end;

注意:为了避免因添加' '而导致文本的额外内存分配,请在循环结束后将最后一个单词添加到网格中。

附加功能:

为了能够调整列数,可以将字符串网格子类化,然后所有单元格都会自动重新排列:

type
  TStringGrid = class(Grids.TStringGrid)
  protected
    procedure SizeChanged(OldColCount, OldRowCount: Integer); override;
  end;

  TForm1 = class(TForm)
  ...

procedure TStringGrid.SizeChanged(OldColCount, OldRowCount: Integer);
var
  I: Integer;
begin
  if OldColCount < ColCount then
  begin
    for I := 0 to OldColCount * OldRowCount - 1 do
      Cells[I mod ColCount, I div ColCount] :=
        Cells[I mod OldColCount, I div OldColCount];
  end
  else if OldColCount > ColCount then
  begin
    for I := OldColCount * OldRowCount - 1 downto 0 do
      Cells[I mod ColCount, I div ColCount] :=
        Cells[I mod OldColCount, I div OldColCount];
  end;
  if OldColCount <> OldRowCount then
    for I := OldColCount * OldRowCount to ColCount * RowCount - 1 do
      Cells[I mod ColCount, I div ColCount] := '';
end;

1
不想贬低其他答案,你是对的NGLN,我可能接受得太快了。让我惊讶的是有几种特殊的方法可以做到这一点。+1给你的方法和输入。 - user1175743

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接