Чтение файлов в Python на основе их семантической нумерации [duplicate]

Исключение нулевого указателя генерируется, когда приложение пытается использовать null в случае, когда требуется объект. К ним относятся:

  1. Вызов метода экземпляра объекта null.
  2. Доступ или изменение поля объекта null.
  3. Принимая длину null, как если бы это был массив.
  4. Доступ или изменение слотов null, как если бы это был массив.
  5. Бросок null как будто это было значение Throwable.

Приложения должны бросать экземпляры этого класса, чтобы указать на другие незаконные использования объекта null.

Ссылка: http://docs.oracle.com/javase/8/docs/api/java/lang/NullPointerException.html

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 17 August 2018 в 23:10
поделиться
  • 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 17 August 2018 в 23:10
поделиться
  • 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 17 August 2018 в 23:10
поделиться

Учитывая:

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 17 August 2018 в 23:10
поделиться

Скорее всего, 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 17 August 2018 в 23:10
поделиться
И теперь для чего-то большего * элегантного (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 17 August 2018 в 23:10
    поделиться

    Я предлагаю вам просто использовать аргумент ключевого слова 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 17 August 2018 в 23:10
    поделиться

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

    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 17 August 2018 в 23:10
    поделиться
    • 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 17 August 2018 в 23:10
    поделиться

    Один из вариантов состоит в том, чтобы превратить строку в кортеж и заменить цифры с помощью расширенной формы 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 17 August 2018 в 23:10
    поделиться
    • 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 17 August 2018 в 23:10
    поделиться
    • 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 17 August 2018 в 23:10
    поделиться
    • 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 17 August 2018 в 23:10
    поделиться
    • 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 17 August 2018 в 23:10
    поделиться

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

    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 17 August 2018 в 23:10
    поделиться
    • 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
    Другие вопросы по тегам:

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