【问题标题】:Word blocks in TMemoTMemo 中的字块
【发布时间】:2012-03-08 23:10:55
【问题描述】:

我正在尝试用 TMemo 制作一个基本的 Hex 查看器,我知道这可能并不理想,但只有我个人使用它,所以这并不重要。

(1)

首先,假设一个备忘录是这样填充十六进制信息的:

我怎样才能得到所有显示的文本块的计数,忽略空白?因此,在这种情况下,使用该图像的结果将是 28。

这是我尝试过的,我知道这是完全错误的,因为我正在访问备忘录行,但我不知道如何访问每个字符。

我似乎无法解决这个简单的问题:(

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;

这是我用来在备忘录中显示十六进制值的代码:

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;

【问题讨论】:

    标签: delphi delphi-xe


    【解决方案1】:

    (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
    

    通过

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

    【讨论】:

    • 什么是 Character.IsWhiteSpace?我收到未声明的标识符错误。我不知道每行的块数,它取决于 TMemo 的宽度。我将编辑问题以包含获取十六进制的代码..
    • @Blobby:将Character 添加到您的uses 子句中。
    • 他们都工作得很好,谢谢安德烈亚斯。如果我理解正确,您的代码实际上是在寻找空格来获得结果,而不是关注实际的文本数据。
    • 是的,你几乎可以用代码来数小说的字数了。
    • Andreas,我如何通过代码在备忘录中找到并选择一个块?我编辑了我的原始问题以显示我尝试过的内容,但我似乎无法让它正常工作。
    【解决方案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;
    

    您可以执行类似的操作,将备忘录中的字符偏移量转换为工作块编号:

    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; 
    

    【讨论】:

    • 感谢您的意见雷米。使用您的答案计算积木似乎更容易。两个非常好的解决方案。
    • 我不明白。你的意思是4 * (Count - 1)?无论如何,OP 说(在对我的回答的评论中),每行的块数可能并不总是等于四个,但可能会有所不同。 更新好的,我现在看到 OP 的 Q 已经更新了...
    • Blobby 的屏幕截图仅显示每行显示4 块,但他的ReadFileAsHex() 函数中的代码上升到每行16 块。我所有的代码 sn-ps 都是基于此。如果实际上每行的块数基于TMemo 宽度,那么只需将16 替换为实际适合一行的块数,这可以通过除以第一行的长度来动态确定3.
    • 很抱歉没有解释得更清楚,截图只显示了4个块以使图像更小。雷米,您确定每行有 16 个块是正确的。我的备忘录宽度不大于那个,所以我什至没有注意到,我认为每行没有预先定义的块的包装方式。感谢 Andreas 和 Remy,并为没有更清楚地道歉。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-02
    • 1970-01-01
    • 2021-11-24
    相关资源
    最近更新 更多