lazarus:對字符串列表進行自然排序

字符串列表內容如下:

8啊啊啊.txt                
11.txt                          
11啊啊啊.txt  
這樣的3風景線在美國越多越好.txt 
1啊啊啊.txt                     
2啊啊啊.txt      
1.1 啊啊啊.txt                  
1.2 啊啊啊.txt    
7 2019年度.txt                  
這樣的2風景線在美國越多越好.txt 
21啊啊啊.txt

怎樣進行自然排名,成爲按這樣排序:

1.1 啊啊啊.txt                 
1.2 啊啊啊.txt                 
11.txt                         
11啊啊啊.txt                   
1啊啊啊.txt                    
21啊啊啊.txt                   
2啊啊啊.txt                    
7 2019年度.txt                 
8啊啊啊.txt                    
這樣的2風景線在美國越多越好.txt
這樣的3風景線在美國越多越好.txt

解決辦法:

引入一個新單元:

unit natural;

{$MODE OBJFPC}{$H+}

// Natural Order String Comparison by Martin Pool

(* -*- mode: c; c-file-style: "k&r" -*-

  strnatcmp.c -- Perform 'natural order' comparisons of strings in C.
  Copyright (C) 2000, 2004 by Martin Pool <mbp sourcefrog net>

  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
*)


interface

(* CUSTOMIZATION SECTION
 *
 * You can change this typedef, but must then also change the inline
 * functions in strnatcmp.c *)

type
  nat_char = char;
  pnat_char = ^nat_char;  


  function strnatcmp(const a: pnat_char; const b: pnat_char): integer;
  function strnatcasecmp(const a: pnat_char; const b: pnat_char): integer;


implementation


(*
  FreePascal IsDigits and IsSpace
*)

function IsDigit(ch: Char): Boolean; 
begin 
  Result := ch In ['0'..'9']; 
end;


function IsSpace(ch: Char): Boolean;
begin
  Result := ch in [' ', #9, #10, #11, #12, #13];
end;


(* partial change history:
 *
 * 2004-10-10 mbp: Lift out character type dependencies into macros.
 *
 * Eric Sosman pointed out that ctype functions take a parameter whose
 * value must be that of an unsigned int, even on platforms that have
 * negative chars in their default char type.
 *)


(* These are defined as macros to make it easier to adapt this code to
 * different characters types or comparison functions. *)

function nat_isdigit(a: nat_char): boolean; inline;
begin
  result := IsDigit(char(a));
end;


function nat_isspace(a: nat_char): boolean; inline;
begin
  result := IsSpace(char(a));
end;

function nat_toupper(a: nat_char): nat_char; inline;
begin
  result := UpCase(char(a));
end;



function compare_right(a: pnat_char; b: pnat_char): integer;
var
  bias : integer = 0;
begin
  (* The longest run of digits wins.  That aside, the greatest
	 value wins, but we can't know that it will until we've scanned
	  both numbers to know that they have the same magnitude, so we
	  remember it in BIAS. *)

  while true do  
  begin
    if (not nat_isdigit(a^) and not nat_isdigit(b^))
      then exit(bias)
    else if (not nat_isdigit(a^))
      then exit(-1)
    else if (not nat_isdigit(b^))
      then exit(1)
    else if (a^ < b^) then
    begin
      if bias <> 0 then bias := -1;
    end
    else if (a^ > b^) then
    begin
      if bias <> 0 then bias := 1;
    end
    else if (a^ = #0) and( b^ = #0)
      then exit(bias);
    inc(a);
    inc(b);
  end;
  result := 0;
end;


function compare_left(a: pnat_char; b: pnat_char): integer;
begin
  (* Compare two left-aligned numbers: the first to have a
     different value wins. *)
  while true do
  begin
    if ( not nat_isdigit(a^) and not nat_isdigit(b^) )
      then exit(0)
    else if (not nat_isdigit(a^))
      then exit(-1)
    else if (not nat_isdigit(b^))
      then exit(1)
    else if (a^ < b^)
      then exit(-1)
    else if (a^ > b^)
      then exit(1);

    inc(a);
    inc(b);  
  end;
  result := 0;
end;



function strnatcmp0(const a: pnat_char; const b: pnat_char; fold_case: integer): integer;
var
  ai, bi: integer;
  ca, cb: char;
  fractional : boolean;
begin
  assert( (a <> nil) and (b <> nil));
  ai := 0; bi := 0;
  while true do
  begin
    ca := a[ai];
    cb := b[bi];
    
    // skip over leading spaces or zeros
    while nat_isspace(ca) do
    begin
      inc(ai);
      ca := a[ai];
    end;

    while nat_isspace(cb) do
    begin
      inc(bi);
      cb := b[bi];
    end;

    // process run of digits
    if (nat_isdigit(ca) and nat_isdigit(cb)) then
    begin
      fractional := ((ca = '0') or (cb = '0'));

      if fractional then 
      begin
        result := compare_left(a+ai, b+bi);
        if result <> 0 then exit;
      end
      else
      begin
        result := compare_right(a+ai, b+bi);
        if result <> 0 then exit;
      end;
    end;

    if (ca=#0) and (cb=#0) then
    begin
      (* The strings compare the same.  Perhaps the caller
         will want to call strcmp to break the tie. *)
      exit(0);
    end;

    if fold_case <> 0 then
    begin
      ca := nat_toupper(ca);
      cb := nat_toupper(cb);
    end;

    if (ca < cb)
      then exit(-1)
    else if (ca > cb)
      then exit(1);

    inc(ai); 
    inc(bi);
  end;  
end;


function strnatcmp(const a: pnat_char; const b: pnat_char): integer;
begin
  result := strnatcmp0(a, b, 0);
end;


(* Compare, recognizing numeric string and ignoring case. *)
function strnatcasecmp(const a: pnat_char; const b: pnat_char): integer;
begin
  result := strnatcmp0(a, b, 1);
end;

end.

函數定義:

function CompareStr(List: TStringList; Index1, Index2: Integer): Integer;
var
  a, b: pnat_char;
begin
  //a := pnat_char(List[index1]);
  //b := pnat_char(List[index2]);

  a := pnat_char(ExtractFileNameOnly(List[Index1]));
  b := pnat_char(ExtractFileNameOnly(List[Index2]));

  if List.CaseSensitive then
    Result := strnatcmp(a, b)
  else
    Result := strnatcasecmp(a, b);
end;   

函數使用:

listOnlyFileName.CustomSort(@CompareStr);

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章