我想在TMemo控件中单击鼠标,查找鼠标单击位置是否在[和]之间,如果在则选中[和]之间的内容,于是写了以下代码(注:两个查找函数是正确的),可是问题出现了,如果TMemo控件中的内容是手动从键盘输入的,则能正确执行,如果是从别的TMemo控件中复制过来的,则不能正确执行,主要是选择的位置出现了随机偏差,何故?100分奉上,敬请高手解答!
procedure TForm1.TimeEditMemoClick(Sender: TObject);
var    //测试中...
startPos,endPos,i:integer;
begin
if TimeEditMemo.Text='' then exit;
i:=TimeEditMemo.SelStart;
startPos:=FastPosBackNoCase(TimeEditMemo.Lines.Text,'[',Length(TimeEditMemo.Lines.Text),Length('['),TimeEditMemo.SelStart);
endPos:=FastPosNoCase(TimeEditMemo.Lines.Text,']',Length(TimeEditMemo.Lines.Text),Length(']'),i);
//showmessage(inttostr(startPos)+'    '+inttostr(endPos));TimeEditMemo.SelStart:=startPos-1;
TimeEditMemo.SelLength:=endPos-startPos+1;
//ShowMessage(inttostr(TimeEditMemo.CaretPos.Y));
//ShowMessage(inttostr(TimeEditMemo.CaretPos.X));
end;从别的TMemo控件中复制代码如下:
procedure TForm1.RightToLeftButtonClick(Sender: TObject);
begin
TimeEditMemo.Lines.Clear;
TimeEditMemo.Lines.AddStrings(LyricMemo.Lines);
LyricMemo.Clear;
TimeEditMemo.SelStart:=0;
TimeEditMemo.SelLength:=0;
end;

解决方案 »

  1.   

    测试后没有问题。
    var    //²âÊÔÖÐ...
      startPos,endPos,i:integer;
    begin
      if TimeEditMemo.Text='' then exit;
      i:=TimeEditMemo.SelStart;
    //  startPos:=FastPosBackNoCase(TimeEditMemo.Lines.Text,'[',Length(TimeEditMemo.Lines.Text),Length('['),TimeEditMemo.SelStart);
    //  endPos:=FastPosNoCase(TimeEditMemo.Lines.Text,']',Length(TimeEditMemo.Lines.Text),Length(']'),i);
      startPos:=1;
      endPos:=length(TimeEditMemo.Text);
      //showmessage(inttostr(startPos)+'    '+inttostr(endPos));  TimeEditMemo.SelStart:=startPos-1;
      TimeEditMemo.SelLength:=endPos-startPos+1;
      //ShowMessage(inttostr(TimeEditMemo.CaretPos.Y));
      //ShowMessage(inttostr(TimeEditMemo.CaretPos.X));
    end;
    请帖出FastPosNoCase的过程。
      

  2.   

    问题确实存在,如果编辑框中的内容是从键盘上手动输入的,则正确,如果是从别的TMemo中复制过来的(用代码实现),则出现错误!
    unit FastStrings;interfaceuses
       {$IFNDEF LINUX}
         Windows,
       {$ENDIF}
       SysUtils;//This TYPE declaration will become apparent later
    type
      TBMJumpTable = array[0..255] of Integer;
      TFastPosProc = function (const aSource, aFind: Pointer; const aSourceLen, aFindLen: Integer; var JumpTable: TBMJumpTable): Pointer;
      TFastPosIndexProc = function (const aSourceString, aFindString: string; const aSourceLen, aFindLen, StartPos: Integer; var JumpTable: TBMJumpTable): Integer;
      TFastTagReplaceProc = procedure (var Tag: string; const UserData: Integer);
    //Boyer-Moore routines
    procedure MakeBMTable(Buffer: PChar; BufferLen: Integer; var JumpTable: TBMJumpTable);
    procedure MakeBMTableNoCase(Buffer: PChar; BufferLen: Integer; var JumpTable: TBMJumpTable);
    function BMPos(const aSource, aFind: Pointer; const aSourceLen, aFindLen: Integer; var JumpTable: TBMJumpTable): Pointer;
    function BMPosNoCase(const aSource, aFind: Pointer; const aSourceLen, aFindLen: Integer; var JumpTable: TBMJumpTable): Pointer;function FastAnsiReplace(const S, OldPattern, NewPattern: string; Flags: TReplaceFlags): string;
    procedure FastCharMove(const Source; var Dest; Count : Integer);
    function FastCharPos(const aSource : string; const C: Char; StartPos : Integer): Integer;
    function FastCharPosNoCase(const aSource : string; C: Char; StartPos : Integer): Integer;
    function FastPos(const aSourceString, aFindString : string; const aSourceLen, aFindLen, StartPos : Integer) : Integer;
    function FastPosNoCase(const aSourceString, aFindString : string; const aSourceLen, aFindLen, StartPos : Integer) : Integer;
    function FastPosBack(const aSourceString, aFindString : string; const aSourceLen, aFindLen, StartPos : Integer) : Integer;
    function FastPosBackNoCase(const aSourceString, aFindString : string; const aSourceLen, aFindLen, StartPos : Integer) : Integer;
    function FastReplace(const aSourceString : string; const aFindString, aReplaceString : string;
      CaseSensitive : Boolean = False) : string;
    function FastTagReplace(const SourceString, TagStart, TagEnd: string;
      FastTagReplaceProc: TFastTagReplaceProc; const UserData: Integer): string;
    function SmartPos(const SearchStr,SourceStr : string;
                      const CaseSensitive : Boolean = TRUE;
                      const StartPos : Integer = 1;
                      const ForwardSearch : Boolean = TRUE) : Integer;
      

  3.   

    implementationconst
      cDeltaSize = 1.5;var
      GUpcaseTable : array[0..255] of char;
      GUpcaseLUT: Pointer;//MakeBMJumpTable takes a FindString and makes a JumpTable
    procedure MakeBMTable(Buffer: PChar; BufferLen: Integer; var JumpTable: TBMJumpTable);
    begin
      if BufferLen = 0 then raise Exception.Create('BufferLen is 0');
      asm
            push    EDI
            push    ESI        mov     EDI, JumpTable
            mov     EAX, BufferLen
            mov     ECX, $100
            REPNE   STOSD        mov     ECX, BufferLen
            mov     EDI, JumpTable
            mov     ESI, Buffer
            dec     ECX
            xor     EAX, EAX
    @@loop:
            mov     AL, [ESI]
            lea     ESI, ESI + 1
            mov     [EDI + EAX * 4], ECX
            dec     ECX
            jg      @@loop        pop     ESI
            pop     EDI
      end;
    end;procedure MakeBMTableNoCase(Buffer: PChar; BufferLen: Integer; var JumpTable: TBMJumpTable);
    begin
      if BufferLen = 0 then raise Exception.Create('BufferLen is 0');
      asm
            push    EDI
            push    ESI        mov     EDI, JumpTable
            mov     EAX, BufferLen
            mov     ECX, $100
            REPNE   STOSD        mov     EDX, GUpcaseLUT
            mov     ECX, BufferLen
            mov     EDI, JumpTable
            mov     ESI, Buffer
            dec     ECX
            xor     EAX, EAX
    @@loop:
            mov     AL, [ESI]
            lea     ESI, ESI + 1
            mov     AL, [EDX + EAX]
            mov     [EDI + EAX * 4], ECX
            dec     ECX
            jg      @@loop        pop     ESI
            pop     EDI
      end;
    end;function BMPos(const aSource, aFind: Pointer; const aSourceLen, aFindLen: Integer; var JumpTable: TBMJumpTable): Pointer;
    var
      LastPos: Pointer;
    begin
      LastPos := Pointer(Integer(aSource) + aSourceLen - 1);
      asm
            push    ESI
            push    EDI
            push    EBX        mov     EAX, aFindLen
            mov     ESI, aSource
            lea     ESI, ESI + EAX - 1
            std
            mov     EBX, JumpTable@@comparetext:
            cmp     ESI, LastPos
            jg      @@NotFound
            mov     EAX, aFindLen
            mov     EDI, aFind
            mov     ECX, EAX
            push    ESI //Remember where we are
            lea     EDI, EDI + EAX - 1
            xor     EAX, EAX
    @@CompareNext:
            mov     al, [ESI]
            cmp     al, [EDI]
            jne     @@LookAhead
            lea     ESI, ESI - 1
            lea     EDI, EDI - 1
            dec     ECX
            jz      @@Found
            jmp     @@CompareNext@@LookAhead:
            //Look up the char in our Jump Table
            pop     ESI
            mov     al, [ESI]
            mov     EAX, [EBX + EAX * 4]
            lea     ESI, ESI + EAX
            jmp     @@CompareText@@NotFound:
            mov     Result, 0
            jmp     @@TheEnd
    @@Found:
            pop     EDI //We are just popping, we don't need the value
            inc     ESI
            mov     Result, ESI
    @@TheEnd:
            cld
            pop     EBX
            pop     EDI
            pop     ESI
      end;
    end;
      

  4.   

    function BMPosNoCase(const aSource, aFind: Pointer; const aSourceLen, aFindLen: Integer; var JumpTable: TBMJumpTable): Pointer;
    var
      LastPos: Pointer;
    begin
      LastPos := Pointer(Integer(aSource) + aSourceLen - 1);
      asm
            push    ESI
            push    EDI
            push    EBX        mov     EAX, aFindLen
            mov     ESI, aSource
            lea     ESI, ESI + EAX - 1
            std
            mov     EDX, GUpcaseLUT@@comparetext:
            cmp     ESI, LastPos
            jg      @@NotFound
            mov     EAX, aFindLen
            mov     EDI, aFind
            push    ESI //Remember where we are
            mov     ECX, EAX
            lea     EDI, EDI + EAX - 1
            xor     EAX, EAX
    @@CompareNext:
            mov     al, [ESI]
            mov     bl, [EDX + EAX]
            mov     al, [EDI]
            cmp     bl, [EDX + EAX]
            jne     @@LookAhead
            lea     ESI, ESI - 1
            lea     EDI, EDI - 1
            dec     ECX
            jz      @@Found
            jmp     @@CompareNext@@LookAhead:
            //Look up the char in our Jump Table
            pop     ESI
            mov     EBX, JumpTable
            mov     al, [ESI]
            mov     al, [EDX + EAX]
            mov     EAX, [EBX + EAX * 4]
            lea     ESI, ESI + EAX
            jmp     @@CompareText@@NotFound:
            mov     Result, 0
            jmp     @@TheEnd
    @@Found:
            pop     EDI //We are just popping, we don't need the value
            inc     ESI
            mov     Result, ESI
    @@TheEnd:
            cld
            pop     EBX
            pop     EDI
            pop     ESI
      end;
    end;
    //NOTE : FastCharPos and FastCharPosNoCase do not require you to pass the length
    //       of the string, this was only done in FastPos and FastPosNoCase because
    //       they are used by FastReplace many times over, thus saving a LENGTH()
    //       operation each time.  I can't see you using these two routines for the
    //       same purposes so I didn't do that this time !
    function FastCharPos(const aSource : string; const C: Char; StartPos : Integer) : Integer;
    var
      L                           : Integer;
    begin
      //If this assert failed, it is because you passed 0 for StartPos, lowest value is 1 !!
      Assert(StartPos > 0);  Result := 0;
      L := Length(aSource);
      if L = 0 then exit;
      if StartPos > L then exit;
      Dec(StartPos);
      asm
          PUSH EDI                 //Preserve this register      mov  EDI, aSource        //Point EDI at aSource
          add  EDI, StartPos
          mov  ECX, L              //Make a note of how many chars to search through
          sub  ECX, StartPos
          mov  AL,  C              //and which char we want
        @Loop:
          cmp  Al, [EDI]           //compare it against the SourceString
          jz   @Found
          inc  EDI
          dec  ECX
          jnz  @Loop
          jmp  @NotFound
        @Found:
          sub  EDI, aSource        //EDI has been incremented, so EDI-OrigAdress = Char pos !
          inc  EDI
          mov  Result,   EDI
        @NotFound:      POP  EDI
      end;
    end;function FastCharPosNoCase(const aSource : string; C: Char; StartPos : Integer) : Integer;
    var
      L                           : Integer;
    begin
      Result := 0;
      L := Length(aSource);
      if L = 0 then exit;
      if StartPos > L then exit;
      Dec(StartPos);
      if StartPos < 0 then StartPos := 0;  asm
          PUSH EDI                 //Preserve this register
          PUSH EBX
          mov  EDX, GUpcaseLUT      mov  EDI, aSource        //Point EDI at aSource
          add  EDI, StartPos
          mov  ECX, L              //Make a note of how many chars to search through
          sub  ECX, StartPos      xor  EBX, EBX
          mov  BL,  C
          mov  AL, [EDX+EBX]
        @Loop:
          mov  BL, [EDI]
          inc  EDI
          cmp  Al, [EDX+EBX]
          jz   @Found
          dec  ECX
          jnz  @Loop
          jmp  @NotFound
        @Found:
          sub  EDI, aSource        //EDI has been incremented, so EDI-OrigAdress = Char pos !
          mov  Result,   EDI
        @NotFound:      POP  EBX
          POP  EDI
      end;
    end;//The first thing to note here is that I am passing the SourceLength and FindLength
    //As neither Source or Find will alter at any point during FastReplace there is
    //no need to call the LENGTH subroutine each time !
    function FastPos(const aSourceString, aFindString : string; const aSourceLen, aFindLen, StartPos : Integer) : Integer;
    var
      JumpTable: TBMJumpTable;
    begin
      //If this assert failed, it is because you passed 0 for StartPos, lowest value is 1 !!
      Assert(StartPos > 0);
      if aFindLen < 1 then begin
        Result := 0;
        exit;
      end;
      if aFindLen > aSourceLen then begin
        Result := 0;
        exit;
      end;  MakeBMTable(PChar(aFindString), aFindLen, JumpTable);
      Result := Integer(BMPos(PChar(aSourceString) + (StartPos - 1), PChar(aFindString),aSourceLen - (StartPos-1), aFindLen, JumpTable));
      if Result > 0 then
        Result := Result - Integer(@aSourceString[1]) +1;
    end;function FastPosNoCase(const aSourceString, aFindString : string; const aSourceLen, aFindLen, StartPos : Integer) : Integer;
    var
      JumpTable: TBMJumpTable;
    begin
      //If this assert failed, it is because you passed 0 for StartPos, lowest value is 1 !!
      Assert(StartPos > 0);
      if aFindLen < 1 then begin
        Result := 0;
        exit;
      end;
      if aFindLen > aSourceLen then begin
        Result := 0;
        exit;
      end;
      

  5.   

    妈的,通过发送消息终于解决了问题
    //单击Memo,自动选中时间标签或标识标签,如果有的话
    procedure TForm1.TimeEditMemoClick(Sender: TObject);
    //测试中...   通过发送消息模拟键盘按钮SHIFT+方向键 实现选定内容
    //终于搞定了一些未知错误
    var
    CurrPos:TPoint;
    str:string;
    i,l,startPos,endPos:integer;
    finded:bool;
    KeyState: TKeyboardState;
    OldCtrl: byte;
    begin
    if TimeEditMemo.Text='' then exit;
    CurrPos:=TimeEditMemo.CaretPos;
    startPos:=-1;endPos:=-1;
    finded:=false;
    str:=TimeEditMemo.Lines.Strings[CurrPos.Y];
    //showmessage(str);
    for i:=0 to CurrPos.X do
    begin
    if str[CurrPos.X-i]=']' then exit;
       if str[CurrPos.X-i]='[' then
       begin
        startPos:=CurrPos.X-i;//找到左[符号
          for l:=CurrPos.X+1 to Length(str) do
          begin
           if str[l]='[' then exit;
             if str[l]=']' then
             begin
              endPos:=l;  //找到右]符号
                finded:=true;
                break;
             end;
          end;
          break;//可别漏了此语句哟
       end;
    end;
    //找到后通过模拟键盘SHIFT+->的方式实现选中内容
    if finded then
    begin
    for i:=0 to CurrPos.X-startPos do
    SendMessage(TimeEditMemo.Handle,WM_KEYDOWN,VK_LEFT,0);//发送消息移动光标
       GetKeyboardState(KeyState);
    OldCtrl := KeyState[VK_SHIFT];
    KeyState[VK_SHIFT]:=$80;
    SetKeyboardState(KeyState);//按下SHIFT键
    for i:=0 to endPos-startPos do
    SendMessage(TimeEditMemo.Handle,WM_KEYDOWN,VK_RIGHT,0);
    KeyState[VK_SHIFT]:=OldCtrl;
    SetKeyboardState(KeyState); //恢复SHIFT键状态
    end;
    end;
    感谢各位回复者