Заказ нескольких имен файлов по номеру в списке [дубликат]

Исключение нулевого указателя - это индикатор того, что вы используете объект, не инициализируя его.

Например, ниже - класс ученика, который будет использовать его в нашем коде.

public class Student {

    private int id;

    public int getId() {
        return this.id;
    }

    public setId(int newId) {
        this.id = newId;
    }
}

Приведенный ниже код дает вам исключение с нулевым указателем.

public class School {

    Student obj_Student;

    public School() {
        try {
            obj_Student.getId();
        }
        catch(Exception e) {
            System.out.println("Null Pointer ");
        }
    }
}

Поскольку вы используете Obj_Student, но вы забыли инициализировать его, как в правильном коде, показанном ниже:

public class School {

    Student obj_Student;

    public School() {
        try {
            obj_Student = new Student();
            obj_Student.setId(12);
            obj_Student.getId();
        }
        catch(Exception e) {
            System.out.println("Null Pointer ");
        }
    }
}
184
задан rp. 26 June 2014 в 23:29
поделиться

15 ответов

Для PyPI существует сторонняя библиотека под названием natsort (полное раскрытие, я автор пакета). Для вашего случая вы можете сделать одно из следующих действий:

>>> from natsort import natsorted, ns
>>> x = ['Elm11', 'Elm12', 'Elm2', 'elm0', 'elm1', 'elm10', 'elm13', 'elm9']
>>> natsorted(x, key=lambda y: y.lower())
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
>>> natsorted(x, alg=ns.IGNORECASE)  # or alg=ns.IC
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']

Вы должны заметить, что natsort использует общий алгоритм, поэтому он должен работать практически для любого ввода, который вы бросаете на него. Если вы хотите получить более подробную информацию о том, почему вы можете выбрать библиотеку для этого, а не выполнять свою собственную функцию, просмотрите страницу «Работает»

>>> from natsort import natsorted, ns
>>> x = ['Elm11', 'Elm12', 'Elm2', 'elm0', 'elm1', 'elm10', 'elm13', 'elm9']
>>> natsorted(x, key=lambda y: y.lower())
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
>>> natsorted(x, alg=ns.IGNORECASE)  # or alg=ns.IC
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
документации
>>> from natsort import natsorted, ns
>>> x = ['Elm11', 'Elm12', 'Elm2', 'elm0', 'elm1', 'elm10', 'elm13', 'elm9']
>>> natsorted(x, key=lambda y: y.lower())
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
>>> natsorted(x, alg=ns.IGNORECASE)  # or alg=ns.IC
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
, в частности, специальные предложения
Everywhere Everywhere ! .


Если вам нужна опция сортировки вместо функции сортировки, используйте любую из приведенных ниже формул.

>>> from natsort import natsort_keygen, ns
>>> l1 = ['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
>>> l2 = l1[:]
>>> natsort_key1 = natsort_keygen(key=lambda y: y.lower())
>>> l1.sort(key=natsort_key1)
>>> l1
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
>>> natsort_key2 = natsort_keygen(alg=ns.IGNORECASE)
>>> l2.sort(key=natsort_key2)
>>> l2
['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
147
ответ дан SethMMorton 18 August 2018 в 17:51
поделиться
  • 1
    Я также думаю, что довольно интересно, что natsort также сортирует правильно, когда число не заканчивается: как это часто бывает для имен файлов. Не стесняйтесь включать следующий пример: pastebin.com/9cwCLdEK – Martin Thoma 17 July 2014 в 19:51

Я написал функцию, основанную на http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html , которая добавляет возможность все еще передаются в ваш собственный параметр «ключ». Мне нужно это, чтобы выполнить естественный вид списков, содержащих более сложные объекты (а не только строки).

import re

def natural_sort(list, key=lambda s:s):
    """
    Sort the list into natural alphanumeric order.
    """
    def get_alphanum_key_func(key):
        convert = lambda text: int(text) if text.isdigit() else text 
        return lambda s: [convert(c) for c in re.split('([0-9]+)', key(s))]
    sort_key = get_alphanum_key_func(key)
    list.sort(key=sort_key)

Например:

my_list = [{'name':'b'}, {'name':'10'}, {'name':'a'}, {'name':'1'}, {'name':'9'}]
natural_sort(my_list, key=lambda x: x['name'])
print my_list
[{'name': '1'}, {'name': '9'}, {'name': '10'}, {'name': 'a'}, {'name': 'b'}]
19
ответ дан beauburrier 18 August 2018 в 17:51
поделиться
  • 1
    более простой способ сделать это - определить natural_sort_key, а затем при сортировке списка вы можете сделать цепочку своих ключей, например: list.sort(key=lambda el: natural_sort_key(el['name'])) – Claudiu 18 April 2013 в 19:49

На основании ответов здесь я написал функцию natural_sorted, которая ведет себя как встроенная функция sorted:

# Copyright (C) 2018, Benjamin Drung <bdrung@posteo.de>
#
# Permission to use, copy, modify, and/or distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

import re

def natural_sorted(iterable, key=None, reverse=False):
    """Return a new naturally sorted list from the items in *iterable*.

    The returned list is in natural sort order. The string is ordered
    lexicographically (using the Unicode code point number to order individual
    characters), except that multi-digit numbers are ordered as a single
    character.

    Has two optional arguments which must be specified as keyword arguments.

    *key* specifies a function of one argument that is used to extract a
    comparison key from each list element: ``key=str.lower``.  The default value
    is ``None`` (compare the elements directly).

    *reverse* is a boolean value.  If set to ``True``, then the list elements are
    sorted as if each comparison were reversed.

    The :func:`natural_sorted` function is guaranteed to be stable. A sort is
    stable if it guarantees not to change the relative order of elements that
    compare equal --- this is helpful for sorting in multiple passes (for
    example, sort by department, then by salary grade).
    """
    prog = re.compile(r"(\d+)")

    def alphanum_key(element):
        """Split given key in list of strings and digits"""
        return [int(c) if c.isdigit() else c for c in prog.split(key(element)
                if key else element)]

    return sorted(iterable, key=alphanum_key, reverse=reverse)

Исходный код также доступен в моем репозитории фрагментов GitHub: https://github.com/bdrung/snippets/blob/master/natural_sorted.py

1
ответ дан Benjamin Drung 18 August 2018 в 17:51
поделиться

Учитывая:

data=['Elm11', 'Elm12', 'Elm2', 'elm0', 'elm1', 'elm10', 'elm13', 'elm9']

Подобно решению SergO, 1-линейный без внешних библиотек будет:

data.sort(key=lambda x : int(x[3:]))

или

sorted_data=sorted(data, key=lambda x : int(x[3:]))

Объяснение:

Это решение использует функцию функции sort для определения функции, которая будет использоваться для сортировки. Поскольку нам известно, что каждой записи данных предшествует «elm», функция сортировки преобразует в целую часть строки после третьего символа (т. Е. Int (x [3:])). Если числовая часть данных находится в другом месте, то эта часть функции должна измениться.

Приветствия

7
ответ дан Camilo 18 August 2018 в 17:51
поделиться

Скорее всего, functools.cmp_to_key() тесно связан с базовой реализацией вида python. Кроме того, параметр cmp является устаревшим. Современный способ состоит в том, чтобы преобразовать входные элементы в объекты, которые поддерживают требуемые богатые операции сравнения.

В CPython 2.x объекты разрозненных типов можно упорядочить, даже если соответствующие богатые операторы сравнения не были реализованы. В CPython 3.x объекты разных типов должны явно поддерживать сравнение. См. . Как Python сравнивает строку и int? , которая ссылается на официальную документацию . Большинство ответов зависят от этого неявного упорядочения. Переключение на Python 3.x потребует нового типа для реализации и унификации сравнений между числами и строками.

Python 2.7.12 (default, Sep 29 2016, 13:30:34) 
>>> (0,"foo") < ("foo",0)
True  
Python 3.5.2 (default, Oct 14 2016, 12:54:53) 
>>> (0,"foo") < ("foo",0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  TypeError: unorderable types: int() < str()

Существует три разных подхода. Первый использует вложенные классы, чтобы воспользоваться алгоритмом сравнения Python Iterable. Второй разворачивает это вложение в один класс. Третий приоритет подклассификации str, чтобы сосредоточиться на производительности. Все приурочены; второй - в два раза быстрее, а третий почти в шесть раз быстрее. Подкласс str не требуется, и, вероятно, это была, пожалуй, плохая идея, но в нем есть определенные удобства.

Символы сортировки дублируются для принудительного упорядочения в каждом конкретном случае и с заменой кода для принудительной сортировки буквы нижнего регистра; это типичное определение «естественного сорта». Я не мог определиться с типом группировки; некоторые из них могут предпочесть следующее, что также приносит значительные преимущества в производительности:

d = lambda s: s.lower()+s.swapcase()

Где используется, операторы сравнения устанавливаются в соответствии с object, поэтому они не будут игнорироваться functools.total_ordering .

import functools
import itertools


@functools.total_ordering
class NaturalStringA(str):
    def __repr__(self):
        return "{}({})".format\
            ( type(self).__name__
            , super().__repr__()
            )
    d = lambda c, s: [ c.NaturalStringPart("".join(v))
                        for k,v in
                       itertools.groupby(s, c.isdigit)
                     ]
    d = classmethod(d)
    @functools.total_ordering
    class NaturalStringPart(str):
        d = lambda s: "".join(c.lower()+c.swapcase() for c in s)
        d = staticmethod(d)
        def __lt__(self, other):
            if not isinstance(self, type(other)):
                return NotImplemented
            try:
                return int(self) < int(other)
            except ValueError:
                if self.isdigit():
                    return True
                elif other.isdigit():
                    return False
                else:
                    return self.d(self) < self.d(other)
        def __eq__(self, other):
            if not isinstance(self, type(other)):
                return NotImplemented
            try:
                return int(self) == int(other)
            except ValueError:
                if self.isdigit() or other.isdigit():
                    return False
                else:
                    return self.d(self) == self.d(other)
        __le__ = object.__le__
        __ne__ = object.__ne__
        __gt__ = object.__gt__
        __ge__ = object.__ge__
    def __lt__(self, other):
        return self.d(self) < self.d(other)
    def __eq__(self, other):
        return self.d(self) == self.d(other)
    __le__ = object.__le__
    __ne__ = object.__ne__
    __gt__ = object.__gt__
    __ge__ = object.__ge__
import functools
import itertools


@functools.total_ordering
class NaturalStringB(str):
    def __repr__(self):
        return "{}({})".format\
            ( type(self).__name__
            , super().__repr__()
            )
    d = lambda s: "".join(c.lower()+c.swapcase() for c in s)
    d = staticmethod(d)
    def __lt__(self, other):
        if not isinstance(self, type(other)):
            return NotImplemented
        groups = map(lambda i: itertools.groupby(i, type(self).isdigit), (self, other))
        zipped = itertools.zip_longest(*groups)
        for s,o in zipped:
            if s is None:
                return True
            if o is None:
                return False
            s_k, s_v = s[0], "".join(s[1])
            o_k, o_v = o[0], "".join(o[1])
            if s_k and o_k:
                s_v, o_v = int(s_v), int(o_v)
                if s_v == o_v:
                    continue
                return s_v < o_v
            elif s_k:
                return True
            elif o_k:
                return False
            else:
                s_v, o_v = self.d(s_v), self.d(o_v)
                if s_v == o_v:
                    continue
                return s_v < o_v
        return False
    def __eq__(self, other):
        if not isinstance(self, type(other)):
            return NotImplemented
        groups = map(lambda i: itertools.groupby(i, type(self).isdigit), (self, other))
        zipped = itertools.zip_longest(*groups)
        for s,o in zipped:
            if s is None or o is None:
                return False
            s_k, s_v = s[0], "".join(s[1])
            o_k, o_v = o[0], "".join(o[1])
            if s_k and o_k:
                s_v, o_v = int(s_v), int(o_v)
                if s_v == o_v:
                    continue
                return False
            elif s_k or o_k:
                return False
            else:
                s_v, o_v = self.d(s_v), self.d(o_v)
                if s_v == o_v:
                    continue
                return False
        return True
    __le__ = object.__le__
    __ne__ = object.__ne__
    __gt__ = object.__gt__
    __ge__ = object.__ge__
import functools
import itertools
import enum


class OrderingType(enum.Enum):
    PerWordSwapCase         = lambda s: s.lower()+s.swapcase()
    PerCharacterSwapCase    = lambda s: "".join(c.lower()+c.swapcase() for c in s)


class NaturalOrdering:
    @classmethod
    def by(cls, ordering):
        def wrapper(string):
            return cls(string, ordering)
        return wrapper
    def __init__(self, string, ordering=OrderingType.PerCharacterSwapCase):
        self.string = string
        self.groups = [ (k,int("".join(v)))
                            if k else
                        (k,ordering("".join(v)))
                            for k,v in
                        itertools.groupby(string, str.isdigit)
                      ]
    def __repr__(self):
        return "{}({})".format\
            ( type(self).__name__
            , self.string
            )
    def __lesser(self, other, default):
        if not isinstance(self, type(other)):
            return NotImplemented
        for s,o in itertools.zip_longest(self.groups, other.groups):
            if s is None:
                return True
            if o is None:
                return False
            s_k, s_v = s
            o_k, o_v = o
            if s_k and o_k:
                if s_v == o_v:
                    continue
                return s_v < o_v
            elif s_k:
                return True
            elif o_k:
                return False
            else:
                if s_v == o_v:
                    continue
                return s_v < o_v
        return default
    def __lt__(self, other):
        return self.__lesser(other, default=False)
    def __le__(self, other):
        return self.__lesser(other, default=True)
    def __eq__(self, other):
        if not isinstance(self, type(other)):
            return NotImplemented
        for s,o in itertools.zip_longest(self.groups, other.groups):
            if s is None or o is None:
                return False
            s_k, s_v = s
            o_k, o_v = o
            if s_k and o_k:
                if s_v == o_v:
                    continue
                return False
            elif s_k or o_k:
                return False
            else:
                if s_v == o_v:
                    continue
                return False
        return True
    # functools.total_ordering doesn't create single-call wrappers if both
    # __le__ and __lt__ exist, so do it manually.
    def __gt__(self, other):
        op_result = self.__le__(other)
        if op_result is NotImplemented:
            return op_result
        return not op_result
    def __ge__(self, other):
        op_result = self.__lt__(other)
        if op_result is NotImplemented:
            return op_result
        return not op_result
    # __ne__ is the only implied ordering relationship, it automatically
    # delegates to __eq__
>>> import natsort
>>> import timeit
>>> l1 = ['Apple', 'corn', 'apPlE', 'arbour', 'Corn', 'Banana', 'apple', 'banana']
>>> l2 = list(map(str, range(30)))
>>> l3 = ["{} {}".format(x,y) for x in l1 for y in l2]
>>> print(timeit.timeit('sorted(l3+["0"], key=NaturalStringA)', number=10000, globals=globals()))
362.4729259099986
>>> print(timeit.timeit('sorted(l3+["0"], key=NaturalStringB)', number=10000, globals=globals()))
189.7340817489967
>>> print(timeit.timeit('sorted(l3+["0"], key=NaturalOrdering.by(OrderingType.PerCharacterSwapCase))', number=10000, globals=globals()))
69.34636392899847
>>> print(timeit.timeit('natsort.natsorted(l3+["0"], alg=natsort.ns.GROUPLETTERS | natsort.ns.LOWERCASEFIRST)', number=10000, globals=globals()))
98.2531585780016

Естественная сортировка довольно сложна и неопределенно определена как проблема. Не забудьте запустить unicodedata.normalize(...) заранее и подумать о том, чтобы использовать str.casefold(), а не str.lower(). Вероятно, есть тонкие проблемы с кодированием, которые я не рассматривал. Поэтому я предварительно рекомендую библиотеку natsort . Я быстро взглянул на хранилище github; обслуживание кода было звездным.

Все алгоритмы, которые я видел, зависят от трюков, таких как дублирование и опускание символов, и обменный футляр. Хотя это удваивает время работы, альтернатива потребует полного естественного упорядочения на наборе символов ввода. Я не думаю, что это часть спецификации юникода, и поскольку число символов в формате unicode намного больше, чем [0-9], создание такой сортировки будет одинаково сложным. Если вы хотите сопоставлять языковые стандарты, подготовьте свои строки с помощью locale.strxfrm для каждой сортировки на Python .

2
ответ дан Community 18 August 2018 в 17:51
поделиться
И теперь для чего-то большего * элегантного (pythonic) - просто прикосновение

Есть много реализаций, а пока некоторые приближаются, никто не захватывает элегантность

    • Протестировано с использованием python (3.5.1)
    • Включил дополнительный список, чтобы продемонстрировать, что он работает, когда цифры находятся в средней строке
    • Не проверял, однако, я предполагаю, что если ваш список был значительным, было бы более эффективно компилировать регулярное выражение заранее . Я уверен, что кто-то исправит меня, если это ошибочное предположение

    Quicky
    from re import compile, split    
    dre = compile(r'(\d+)')
    mylist.sort(key=lambda l: [int(s) if s.isdigit() else s.lower() for s in split(dre, l)])
    
    Полный код
    #!/usr/bin/python3
    # coding=utf-8
    """
    Natural-Sort Test
    """
    
    from re import compile, split
    
    dre = compile(r'(\d+)')
    mylist = ['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13', 'elm']
    mylist2 = ['e0lm', 'e1lm', 'E2lm', 'e9lm', 'e10lm', 'E12lm', 'e13lm', 'elm', 'e01lm']
    
    mylist.sort(key=lambda l: [int(s) if s.isdigit() else s.lower() for s in split(dre, l)])
    mylist2.sort(key=lambda l: [int(s) if s.isdigit() else s.lower() for s in split(dre, l)])
    
    print(mylist)  
      # ['elm', 'elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
    print(mylist2)  
      # ['e0lm', 'e1lm', 'e01lm', 'E2lm', 'e9lm', 'e10lm', 'E12lm', 'e13lm', 'elm']
    

    Внимание при использовании

    • from os.path import split вам нужно будет различать импорт

    Вдохновение от

  • 5
    ответ дан Jerod 18 August 2018 в 17:51
    поделиться

    Я предлагаю вам просто использовать аргумент ключевого слова key в sorted для достижения желаемого списка. Например:

    to_order= [e2,E1,e5,E4,e3]
    ordered= sorted(to_order, key= lambda x: x.lower())
        # ordered should be [E1,e2,e3,E4,e5]
    
    0
    ответ дан Johny Vaknin 18 August 2018 в 17:51
    поделиться

    Попробуйте следующее:

    import re
    
    def natural_sort(l): 
        convert = lambda text: int(text) if text.isdigit() else text.lower() 
        alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ] 
        return sorted(l, key = alphanum_key)
    

    Выход:

    ['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
    

    См. его в режиме онлайн: ideone .

    Код отсюда: Сортировка для людей: естественный порядок сортировки .

    143
    ответ дан Mark Byers 18 August 2018 в 17:51
    поделиться
    • 1
      +1 Почему лямбды назначены на имена? – Apalala 30 January 2011 в 17:44
    • 2
      почему вы используете return sorted(l, key) вместо l.sort(key)? Это для любого повышения производительности или просто для более питонического? – jperelli 15 August 2012 в 17:33
    • 3
      @jperelli Я думаю, что лестница изменит исходный список в вызывающем. Но, скорее всего, звонящий хочет другую мелкую копию списка. – huggie 30 August 2012 в 06:00
    • 4
      Только для записи это не может обрабатывать все входы: разделители str / int должны выстраиваться в линию, иначе вы будете создавать сравнения, такие как [& quot; foo & quot ;, 0] & lt; [0, «foo»] для ввода [«foo0», «0foo»], который вызывает TypeError. – user19087 11 January 2017 в 01:25
    • 5
      @ user19087: На самом деле это работает, потому что re.split('([0-9]+)', '0foo') возвращает ['', '0', 'foo']. Из-за этого строки всегда будут на четных индексах и целых числах по нечетным индексам в массиве. – Florian Kusche 30 October 2017 в 10:13

    Значение этого сообщения

    Я хочу предложить решение без регулярных выражений, которое может применяться в целом. Я создам три функции:

    1. find_first_digit, которые я заимствовал из @AnuragUniyal . Он найдет положение первой цифры или не цифру в строке.
    2. split_digits, которая является генератором, который выделяет строку в цифру и нечетные фрагменты. Он также будет yield целыми числами, когда это цифра.
    3. natural_key просто обертывает split_digits в tuple. Это то, что мы используем в качестве ключа для sorted, max, min.

    Функции

    def find_first_digit(s, non=False):
        for i, x in enumerate(s):
            if x.isdigit() ^ non:
                return i
        return -1
    
    def split_digits(s, case=False):
        non = True
        while s:
            i = find_first_digit(s, non)
            if i == 0:
                non = not non
            elif i == -1:
                yield int(s) if s.isdigit() else s if case else s.lower()
                s = ''
            else:
                x, s = s[:i], s[i:]
                yield int(x) if x.isdigit() else x if case else x.lower()
    
    def natural_key(s, *args, **kwargs):
        return tuple(split_digits(s, *args, **kwargs))
    

    Мы видим, что это общий в том, что мы можем иметь несколько цифровых фрагментов:

    # Note that the key has lower case letters
    natural_key('asl;dkfDFKJ:sdlkfjdf809lkasdjfa_543_hh')
    
    ('asl;dkfdfkj:sdlkfjdf', 809, 'lkasdjfa_', 543, '_hh')
    

    Или оставить в качестве чувствительного к регистру:

    natural_key('asl;dkfDFKJ:sdlkfjdf809lkasdjfa_543_hh', True)
    
    ('asl;dkfDFKJ:sdlkfjdf', 809, 'lkasdjfa_', 543, '_hh')
    

    Мы видим, что он сортирует список OP в соответствующем order

    sorted(
        ['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13'],
        key=natural_key
    )
    
    ['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
    

    Но он может обрабатывать и более сложные списки:

    sorted(
        ['f_1', 'e_1', 'a_2', 'g_0', 'd_0_12:2', 'd_0_1_:2'],
        key=natural_key
    )
    
    ['a_2', 'd_0_1_:2', 'd_0_12:2', 'e_1', 'f_1', 'g_0']
    

    Мой эквивалент регулярного выражения будет

    def int_maybe(x):
        return int(x) if str(x).isdigit() else x
    
    def split_digits_re(s, case=False):
        parts = re.findall('\d+|\D+', s)
        if not case:
            return map(int_maybe, (x.lower() for x in parts))
        else:
            return map(int_maybe, parts)
    
    def natural_key_re(s, *args, **kwargs):
        return tuple(split_digits_re(s, *args, **kwargs))
    
    3
    ответ дан piRSquared 18 August 2018 в 17:51
    поделиться

    Один из вариантов состоит в том, чтобы превратить строку в кортеж и заменить цифры с помощью расширенной формы http://wiki.answers.com/Q/What_does_expanded_form_mean

    таким образом, чтобы a90 («a», 90,0), а a1 станет («a», 1)

    ниже, является некоторым примером кода (который не очень эффективен из-за того, что он удаляет ведущие 0 из чисел )

    alist=["something1",
        "something12",
        "something17",
        "something2",
        "something25and_then_33",
        "something25and_then_34",
        "something29",
        "beta1.1",
        "beta2.3.0",
        "beta2.33.1",
        "a001",
        "a2",
        "z002",
        "z1"]
    
    def key(k):
        nums=set(list("0123456789"))
            chars=set(list(k))
        chars=chars-nums
        for i in range(len(k)):
            for c in chars:
                k=k.replace(c+"0",c)
        l=list(k)
        base=10
        j=0
        for i in range(len(l)-1,-1,-1):
            try:
                l[i]=int(l[i])*base**j
                j+=1
            except:
                j=0
        l=tuple(l)
        print l
        return l
    
    print sorted(alist,key=key)
    

    вывод:

    ('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 1)
    ('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 10, 2)
    ('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 10, 7)
    ('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 2)
    ('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 20, 5, 'a', 'n', 'd', '_', 't', 'h', 'e', 'n', '_', 30, 3)
    ('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 20, 5, 'a', 'n', 'd', '_', 't', 'h', 'e', 'n', '_', 30, 4)
    ('s', 'o', 'm', 'e', 't', 'h', 'i', 'n', 'g', 20, 9)
    ('b', 'e', 't', 'a', 1, '.', 1)
    ('b', 'e', 't', 'a', 2, '.', 3, '.')
    ('b', 'e', 't', 'a', 2, '.', 30, 3, '.', 1)
    ('a', 1)
    ('a', 2)
    ('z', 2)
    ('z', 1)
    ['a001', 'a2', 'beta1.1', 'beta2.3.0', 'beta2.33.1', 'something1', 'something2', 'something12', 'something17', 'something25and_then_33', 'something25and_then_34', 'something29', 'z1', 'z002']
    
    3
    ответ дан Robert Siemer 18 August 2018 в 17:51
    поделиться
    • 1
      К сожалению, это решение работает только для Python 2.X. Для Python 3, ('b', 1) < ('b', 'e', 't', 'a', 1, '.', 1) вернет TypeError: unorderable types: int() < str() – SethMMorton 3 January 2017 в 07:35

    Вышеуказанные ответы хороши для конкретного примера, который был показан, но пропустите несколько полезных случаев для более общего вопроса о естественном роде. Я только что получил один из этих случаев, поэтому создал более тщательное решение:

    def natural_sort_key(string_or_number):
        """
        by Scott S. Lawton <scott@ProductArchitect.com> 2014-12-11; public domain and/or CC0 license
    
        handles cases where simple 'int' approach fails, e.g.
            ['0.501', '0.55'] floating point with different number of significant digits
            [0.01, 0.1, 1]    already numeric so regex and other string functions won't work (and aren't required)
            ['elm1', 'Elm2']  ASCII vs. letters (not case sensitive)
        """
    
        def try_float(astring):
            try:
                return float(astring)
            except:
                return astring
    
        if isinstance(string_or_number, basestring):
            string_or_number = string_or_number.lower()
    
            if len(re.findall('[.]\d', string_or_number)) <= 1:
                # assume a floating point value, e.g. to correctly sort ['0.501', '0.55']
                # '.' for decimal is locale-specific, e.g. correct for the Anglosphere and Asia but not continental Europe
                return [try_float(s) for s in re.split(r'([\d.]+)', string_or_number)]
            else:
                # assume distinct fields, e.g. IP address, phone number with '.', etc.
                # caveat: might want to first split by whitespace
                # TBD: for unicode, replace isdigit with isdecimal
                return [int(s) if s.isdigit() else s for s in re.split(r'(\d+)', string_or_number)]
        else:
            # consider: add code to recurse for lists/tuples and perhaps other iterables
            return string_or_number
    

    Тестовый код и несколько ссылок (вкл и выкл StackOverflow) находятся здесь: http: // productarchitect .com / code / better-natural-sort.py

    Обратная связь приветствуется. Это не должно быть окончательным решением; просто шаг вперед.

    2
    ответ дан Scott Lawton 18 August 2018 в 17:51
    поделиться
    • 1
      В вашем тестовом скрипте, к которому вы ссылаетесь, natsorted и humansorted терпят неудачу, потому что они были неправильно использованы ... вы пытались передать natsorted в качестве ключа, но фактически это функция сортировки. Вы должны были попробовать natsort_keygen(). – SethMMorton 18 November 2015 в 00:58
    data = ['elm13', 'elm9', 'elm0', 'elm1', 'Elm11', 'Elm2', 'elm10']
    

    Давайте проанализируем данные. Емкость цифр всех элементов равна 2. И есть 3 буквы в общей литеральной части 'elm'.

    Таким образом, максимальная длина элемента равна 5. Мы можем увеличить это значение, чтобы убедиться (например, до 8).

    Учитывая это, у нас есть однолинейное решение:

    data.sort(key=lambda x: '{0:0>8}'.format(x).lower())
    

    без регулярных выражений и внешних библиотек!

    print(data)
    
    >>> ['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'elm13']
    

    Объяснение:

    for elm in data:
        print('{0:0>8}'.format(elm).lower())
    
    >>>
    0000elm0
    0000elm1
    0000elm2
    0000elm9
    000elm10
    000elm11
    000elm13
    
    15
    ответ дан SergO 18 August 2018 в 17:51
    поделиться
    • 1
      @SergO: Спасибо. Некоторые из решений здесь не объясняются хорошо. – Bade 3 January 2017 в 22:05
    • 2
      Это не обрабатывает данные динамической / неизвестной длины. Он также сортируется по-разному, чем другие решения для данных, которые имеют номера в данных, противоположных в конце. * Это не обязательно нежелательно, но я считаю, что это хорошо. – Jerod 27 September 2017 в 13:46
    • 3
      Если вам нужно обрабатывать данные динамической длины, вы можете использовать width = max(data, key=len) для вычисления того, что для sub для 8 выше, а затем добавить его в строку формата с '{0:0>{width}}'.format(x, width=width) – Roganartu 27 November 2017 в 17:42
    >>> import re
    >>> sorted(lst, key=lambda x: int(re.findall(r'\d+$', x)[0]))
    ['elm0', 'elm1', 'Elm2', 'elm9', 'elm10', 'Elm11', 'Elm12', 'elm13']
    
    -2
    ответ дан SilentGhost 18 August 2018 в 17:51
    поделиться
    • 1
      Ваша реализация только решает проблему чисел. Реализация не выполняется, если строки в них не имеют чисел. Попробуйте, например, [«тихий», «призрак»] (индекс списка за пределами диапазона). – snakile 29 January 2011 в 13:26
    • 2
      @snaklie: ваш вопрос не может обеспечить достойный пример. Вы не объяснили, что вы пытаетесь сделать, и вы не обновили свой вопрос с помощью этой новой информации. Вы не отправили ничего, что вы пробовали, поэтому, пожалуйста, не пренебрегайте моей попыткой телепатии. – SilentGhost 29 January 2011 в 14:17
    • 3
      @SilentGhost: Во-первых, я дал вам преимущество, потому что я думаю, что ваш ответ полезен (хотя это не решает мою проблему). Во-вторых, я не могу охватить все возможные случаи примерами. Я думаю, что я дал довольно четкое определение естественному виду. Я не думаю, что это хорошая идея дать сложный пример или длинное определение для такой простой концепции. Вы можете отредактировать мой вопрос, если можете подумать о лучшей формулировке проблемы. – snakile 29 January 2011 в 14:43
    • 4
      @SilentGhost: Я бы хотел иметь дело с такими строками так же, как Windows имеет дело с такими именами файлов, когда сортирует файлы по имени (игнорировать случаи и т. Д.). Мне кажется ясным, но все, что я говорю, мне кажется ясным, поэтому я не должен судить, ясно это или нет. – snakile 29 January 2011 в 15:06
    • 5
      @snakile, вы нигде не приблизились к определению естественного поиска. Это было бы довольно сложно сделать и потребовало бы много деталей. Если вы хотите, чтобы порядок сортировки использовался проводником Windows, вы знаете, что есть простой вызов api, который предоставляет это? – David Heffernan 29 January 2011 в 17:02
    a = ['H1', 'H100', 'H10', 'H3', 'H2', 'H6', 'H11', 'H50', 'H5', 'H99', 'H8']
    b = ''
    c = []
    
    def bubble(bad_list):#bubble sort method
            length = len(bad_list) - 1
            sorted = False
    
            while not sorted:
                    sorted = True
                    for i in range(length):
                            if bad_list[i] > bad_list[i+1]:
                                    sorted = False
                                    bad_list[i], bad_list[i+1] = bad_list[i+1], bad_list[i] #sort the integer list 
                                    a[i], a[i+1] = a[i+1], a[i] #sort the main list based on the integer list index value
    
    for a_string in a: #extract the number in the string character by character
            for letter in a_string:
                    if letter.isdigit():
                            #print letter
                            b += letter
            c.append(b)
            b = ''
    
    print 'Before sorting....'
    print a
    c = map(int, c) #converting string list into number list
    print c
    bubble(c)
    
    print 'After sorting....'
    print c
    print a
    

    Благодарности:

    Bubble Sort Homework

    Как читать строку по одной букве за раз в python

    0
    ответ дан Varadaraju G 18 August 2018 в 17:51
    поделиться

    Вот еще более питонская версия ответа Марка Байера:

    import re
    
    def natural_sort_key(s, _nsre=re.compile('([0-9]+)')):
        return [int(text) if text.isdigit() else text.lower()
                for text in re.split(_nsre, s)]    
    

    Теперь эта функция может использоваться как ключ в любой функции, которая ее использует, например list.sort, sorted, max и т. д.

    В качестве лямбда:

    lambda s: [int(t) if t.isdigit() else t.lower() for t in re.split('(\d+)', s)]
    
    73
    ответ дан Zitrax 18 August 2018 в 17:51
    поделиться
    • 1
      Вероятно, вы захотите предварительно скомпоновать шаблон, хотя ... – Antti Haapala 5 September 2013 в 04:56
    • 2
      re модуль компилирует и кэширует регулярные выражения автоматически, поэтому нет необходимости прекомпилировать – wim 22 January 2014 в 19:17
    • 3
      @wim: он кэширует последние X использования, поэтому технически можно использовать регулярные выражения X + 5, а затем делать естественный сорт снова и снова, и в этот момент это не будет кэшироваться. но, возможно, в конечном итоге незначительно – Claudiu 22 January 2014 в 19:51
    • 4
      Я этого не делал, но, возможно, причина заключалась в том, что он не может обрабатывать кортежи, как обычный тип python. – The Unfun Cat 8 May 2015 в 18:51
    • 5
      Предположения X, упомянутые @Claudiu, кажутся 100 на Python 2.7 и 512 на Python 3.4. А также обратите внимание, что при достижении предела кеш полностью очищается (так что это не только самый старый из них выкинут). – Zitrax 23 November 2015 в 13:45
    Другие вопросы по тегам:

    Похожие вопросы: