TMemo中的文本块问题

4
我正在尝试使用TMemo创建一个基本的十六进制查看器,我知道这可能不是最理想的选择,但只有我个人使用所以并不重要。
首先,假设Memo中填写了十六进制信息,如下所示:
如何获取所有文本块的数量,忽略空白?因此,在此图像中的结果将为28。
这是我尝试的方法,但我知道这完全是错误的,因为我正在访问Memo行,但我不知道如何访问每个字符。
我似乎无法解决这个简单的问题:(
function CountWordBlocks(Memo: TMemo): Integer;
var
  i: Integer;
  vCount: Integer;
begin
  for i := 0 to Memo.Lines.Count - 1 do
  begin
    if Length(Memo.Lines.Strings[i]) = 2 then
    begin
      Inc(vCount);
    end;
  end;

  Result := vCount;
end;

以下是我用来在 Memo 中显示十六进制值的代码:
procedure ReadFileAsHex(const AFileName: string; ADestination: TStrings);
var
  fs: TFileStream;
  buff: Byte;
  linecount: Byte;
  line: string;
begin
  linecount := 0;
  line := '';
  fs := TFileStream.Create(AFileName, fmOpenRead);

  try
    ADestination.BeginUpdate;
    try
      while fs.Position < fs.Size do
      begin
        fs.Read(buff, 1);
        line := line + IntToHex(buff, 2) + ' ';
        Inc(linecount);
        if linecount = 16 then
        begin
          ADestination.Add(line);
          line := '';
          linecount := 0;
        end;
      end;
      if Length(line) <> 0 then
        ADestination.Add(line);
    finally
      ADestination.EndUpdate;
    end;
  finally
    fs.Free;
  end;
end;

(2)

如果我点击便条并且鼠标光标下方有文本块,我怎样才能知道选定的文本块是所有其他文本块中的第几个?

因此,使用同样的第一张图片,插入符位于68旁边的顶部行,因此结果将为3,因为它是28个文本块中的第三个。

这应该很容易,但我无法清晰地思考,我还没有正确的编程思维,所以很难解决基本的逻辑和问题!

(3)

最后,我想通过传递块号值在运行时选择一个块。我尝试过,但没有取得太大的成功:

procedure FindBlock(Memo: TMemo; BlockNumber: Integer);
var
  i: Integer;
  txt: string;
  ThisWhite, PrevWhite: boolean;
  vRead: Integer;
begin
  txt := Memo.Text;
  vRead:= 0;
  PrevWhite := True;
  for i := 1 to Length(txt) do
  begin
    ThisWhite := Character.IsWhiteSpace(txt[i]);
    if PrevWhite and not ThisWhite then
    begin
      Inc(vRead);
      PrevWhite := False;
    end;
    PrevWhite := ThisWhite;

    if vRead = BlockNumber then
    begin
      Memo.SelStart := vRead;
      Memo.SetFocus;
      Exit;
    end;
  end;
end;
2个回答

6

(1)

这个可以正常工作:

function TForm1.CountBlocks: integer;
var
  i: Integer;
  txt: string;
  ThisWhite, PrevWhite: boolean;
begin
  txt := Memo1.Text;
  result:= 0;
  PrevWhite := true;
  for i := 1 to Length(txt) do
  begin
    ThisWhite := Character.IsWhiteSpace(txt[i]);
    if PrevWhite and not ThisWhite then
    begin
      inc(result);
      PrevWhite := false;
    end;
    PrevWhite := ThisWhite;
  end;
end;

然而,如果有更详细的备忘录内容信息可用,则可以进行优化。例如,如果您知道每行由四个块组成,则块数就是简单的4 * Memo1.Lines.Count。即使是不同宽度的块,我的上面的代码也能接受。

(2)

只需替换

for i := 1 to Length(txt) do

by

for i := 1 to Memo1.SelStart + 1 do

什么是Character.IsWhiteSpace?我得到了未声明的标识符错误。我不知道每行有多少块,它根据TMemo的宽度而定。我将编辑问题以包括获取Hex的代码。 - user1175743
1
@Blobby:在你的uses子句中添加Character - Andreas Rejbrand
它们都很好,谢谢Andreas。如果我理解正确的话,你的代码实际上是在寻找空格以获取结果,而不是专注于实际文本数据。 - user1175743
是的,你几乎可以使用代码来计算一部小说中单词的数量。 - Andreas Rejbrand
Andreas,我怎样才能通过代码查找和选择备忘录中的一个块?我编辑了原来的问题以展示我的尝试,但似乎无法正确地工作。 - user1175743

2

由于您可以控制行的格式,并且行具有固定的格式,因此非常容易计算显示的字节数,而无需通过逐个遍历各个行来实现。每行显示3个字符每字节,除了最后一行之外的每行均显示16个字节,因此每个完整的16字节行显示48个字符。利用这些事实来计算基于存在的完整16字节行数的字节数,然后您可以添加仅从最后一行剩余的字节数:

function CountWordBlocks(Memo: TMemo): Integer; 
var 
  Count: Integer; 
begin 
  Count := Memo.Lines.Count;
  if Count > 0 then
    Result := (16 * (Count-1)) + (Length(Memo.Lines[Count-1]) div 3);
  else
    Result := 0;
end;

你可以类似地做一些事情,将Memo中的字符偏移量转换为工作块编号:
function GetCurrentWordBlock(Memo: TMemo): Integer;
var
  SelStart, LineStart, LineNum: Integer
begin
  Result := 0;
  SelStart := Memo.SelStart;
  if SelStart < 0 then Exit;
  LineStart := Memo.Perform(EM_LINEINDEX, SelStart, 0);
  if LineStart < 0 then Exit;
  LineNum := Memo.Perform(EM_LINEFROMCHAR, LineStart, 0);
  Result := (16 * LineNum) + ((SelStart - LineStart) div 3) + 1;
end;

要选择给定的块编号,您可以执行以下操作:

procedure FindBlock(Memo: TMemo; BlockNumber: Integer); 
var
  LineNum, LineStart: Integer;
begin 
  if BlockNumber < 1 then Exit;
  LineNum = (BlockNumber - 1) div 16;
  LineStart = Memo.Perform(EM_LINEINDEX, LineNum, 0);
  if LineStart < 0 then Exit;
  Memo.SelStart = LineStart + (((BlockNumber - 1) - (16 * LineNum)) * 3);
  Memo.SelLength := 2;
  Memo.SetFocus; 
end; 

谢谢你的回复,Remy。使用你的答案计算块数似乎更容易。两个都是非常好的解决方案。 - user1175743
我不明白这个。你肯定是指 4 * (Count - 1) 吧?无论如何,OP在评论中说,每行块数可能不总是等于四,而是可能会变化。更新好的,我现在看到OP的问题已经更新了... - Andreas Rejbrand
Blobby的屏幕截图仅显示每行4个块,但他的ReadFileAsHex()函数中的代码可显示每行16个块。我的所有代码片段都是基于此的。如果实际上每行块数基于TMemo的宽度,则只需使用第一行的长度除以3来动态确定每行可以容纳的块数,并将16替换为该数字。 - Remy Lebeau
抱歉没有解释得更清楚,截图只显示了4个块以使图像更小。Remy在识别每行有16个块方面是正确的。我的备忘录宽度没有超过这个范围,所以我甚至没有注意到,文字换行的方式让我认为每行没有预定义的块数。感谢Andreas和Remy,对于没有表达得更清楚,我表示歉意。 - user1175743

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