Guest User

port of BLI_strcasecmp_natural from C++ to Python, not tested very much

a guest
Jun 14th, 2024
85
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 2.85 KB | Fixit | 0 0
  1. from functools import cmp_to_key
  2. #from source/blender/blenlib/intern/string.c/BLI_strcasecmp_natural for Blender 4.1.1
  3. def BLI_strcasecmp_natural(s1, s2):
  4.   #there's no dedicated str.compare function in Python
  5.   def strcmp(s1, s2):
  6.     if s1 < s2:
  7.       return -1
  8.     elif s1 > s2:
  9.       return 1
  10.     else:
  11.       return 0
  12.   #compare number on the left size of the string
  13.   def left_number_strcmp(s1, s2, tiebreaker2):
  14.     p1 = 0
  15.     p2 = 0
  16.     numdigit = 0
  17.     numzero1 = 0
  18.     numzero2 = 0
  19.     #count and skip leading zeros
  20.     while s1[p1] == '0':
  21.       p1 += 1
  22.       numzero1 += 1
  23.     while s2[p2] == '0':
  24.       p2 += 1
  25.       numzero2 += 1
  26.     #find number of consecutive digits
  27.     while True:
  28.       if s1[p1+numdigit].isdigit() and s2[p2+numdigit].isdigit():
  29.         numdigit += 1
  30.         continue
  31.       if s1[p1+numdigit].isdigit():
  32.         return 1 #s2 is bigger
  33.       if s2[p2+numdigit].isdigit():
  34.         return -1 #s1 is bigger
  35.       break
  36.     #same number of digits, compare size of number
  37.     if numdigit > 0:
  38.       compare = strcmp(s1[p1:p1+numdigit], s2[p2:p1+numdigit])
  39.       if compare != 0:
  40.         return compare
  41.     #use number of leading zeros as tie breaker if still equal
  42.     if tiebreaker2[0] == 0:
  43.       if numzero1 > numzero2:
  44.         tiebreaker2[0] = 1
  45.       elif numzero1 < numzero2:
  46.         tiebreaker2[0] = -1
  47.     return 0
  48.   s1 += '\0'
  49.   s2 += '\0'
  50.   d1 = 0
  51.   d2 = 0
  52.   c1 = None
  53.   c2 = None
  54.   tiebreaker = 0
  55.   #if both chars are numeric, to a left_number_strcmp().
  56.   #then increase string deltas as long they are
  57.   #numeric, else do a tolower and char compare
  58.   while True:
  59.     if s1[d1].isdigit() and s2[d2].isdigit():
  60.       tiebreaker2 = [tiebreaker]
  61.       numcompare = left_number_strcmp(s1[d1:], s2[d2:], tiebreaker2)
  62.       tiebreaker = tiebreaker2[0]
  63.       if numcompare != 0:
  64.         return numcompare
  65.       #Some wasted work here, left_number_strcmp already consumes at least some digits.
  66.       d1 += 1
  67.       while s1[d1].isdigit():
  68.         d1 += 1
  69.       d2 += 1
  70.       while s2[d2].isdigit():
  71.         d2 += 1
  72.     #Test for end of strings first so that shorter strings are ordered in front.
  73.     if '\0' in [s1[d1], s2[d2]]:
  74.       break
  75.     c1 = s1[d1].lower()
  76.     c2 = s2[d2].lower()
  77.     if c1 == c2:
  78.       #Continue iteration
  79.       pass
  80.     #Check for '.' so "foo.bar" comes before "foo 1.bar".
  81.     elif c1 == '.':
  82.       return -1
  83.     elif c2 == '.':
  84.       return 1
  85.     elif c1 < c2:
  86.       return -1
  87.     elif c1 > c2:
  88.       return 1
  89.     d1 += 1
  90.     d2 += 1
  91.   if tiebreaker != 0:
  92.     return tiebreaker
  93.   #we might still have a different string because of lower/upper case, in
  94.   #that case fall back to regular string comparison
  95.   return strcmp(s1, s2)
  96. print(sorted(["a", "A", "c", "b", "d", "foo.bar", "foo 1.bar", "a.00", "a.001", "a.002", "a.000"], key=cmp_to_key(BLI_strcasecmp_natural)))
Tags: blender
Advertisement
Add Comment
Please, Sign In to add comment