Может ли Excel выполнять сортировку иначе, чем набор символов США по умолчанию? - PullRequest
4 голосов
/ 08 мая 2020

Мой вопрос в основном противоположен ЭТОМ (у которого было решение на основе базы данных, которое я не могу здесь использовать).

Я использую SAP, который сортирует символы следующим образом:

0-9, AZ, _

, но я загружаю данные в Excel и управляю диапазонами в зависимости от правильного порядка сортировки набора символов SAP.

Как заставить Excel выполнять сортировку так же, как SAP, с последним подчеркиванием.

После попытки создания настраиваемого списка сортировки отдельных символов в функции сортировки Excel Excel по-прежнему / всегда выполняет сортировку следующим образом:

_, 0-9, AZ

Есть ли способ заставить Excel сортировать как SAP? При необходимости я могу использовать макросы Excel.

В качестве альтернативы, если кто-нибудь знает, как заставить собственные таблицы SAP сортировать, как Excel, в интерфейсе SAP, это тоже решит эту проблему.

Ответы [ 2 ]

1 голос
/ 11 мая 2020

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

Если вы сортируете в этом новом столбце строки будут отсортированы в порядке ASCII (0-9, A-Z, _).

Он должен иметь возможность обрабатывать любое количество строк. На моем ноутбуке вычисление ячеек занимает 1 минуту для 130 000 строк. Есть две функции VBA: одна для ASCII и одна для EBCDI C. Другие наборы символов очень легко определить.

Шаги:

  • Создайте модуль в своей книге Excel и поместите код ниже.
  • Закройте редактор VB, иначе он будет работать медленно .
  • На листе, который вы хотите отсортировать, вставьте по одному столбцу для каждого столбца, который вы хотите отсортировать, например, скажем, сортировка должна быть выполнена для столбца A, создайте новый столбец B в ячейка B1 вставьте формулу =SortableCodeASCII(A1) и сделайте то же самое для всех ячеек столбца B (до последней строки столбца A).
  • Убедитесь, что расчет формул завершен (требуется 1 минуту для 130 000 строк на моем ноутбуке), иначе при сортировке порядок будет неправильным, потому что формулы еще не рассчитаны. Вы видите индикатор выполнения (в процентах) в строке состояния внизу окна Excel. Если вы его не видите, нажмите Ctrl + Alt + F9 .
  • Сортировать по столбцу B. Значения в столбце A должны быть отсортировано в соответствии с порядком ASCII (0-9, A-Z, _)

Удачи!

Option Compare Text 'to make true "a" = "A", "_" < "0", etc.
Option Base 0 'to start arrays at index 0 (LBound(array) = 0)
Dim SortableCharactersASCII() As String
Dim SortableCharactersEBCDIC() As String
Dim SortableCharactersTEST() As String

Sub ResetSortableCode()
    'Run this subroutine if you change anything in the code of this module
    'to regenerate the arrays SortableCharacters*
    Erase SortableCharactersASCII
    Erase SortableCharactersEBCDIC
    Erase SortableCharactersTEST
    Call SortableCodeASCII("")
    Call SortableCodeEBCDIC("")
    Call SortableCodeTEST("")
End Sub

Function SortableCodeASCII(text As String)
    If (Not Not SortableCharactersASCII) = 0 Then
        SortableCharactersASCII = getSortableCharacters( _
            orderedCharacters:=" !""#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}" & ChrW(126) & ChrW(127))
    End If
    SortableCodeASCII = getSortableCode(text, SortableCharactersASCII)
End Function

Function SortableCodeEBCDIC(text As String)
    If (Not Not SortableCharactersEBCDIC) = 0 Then
        SortableCharactersEBCDIC = getSortableCharacters( _
            orderedCharacters:=" ¢.<(+|&!$*);-/¦,%_>?`:#@'=""abcdefghi±jklmnopqr~stuvwxyz^[]{ABCDEFGHI}JKLMNOPQR\STUVWXYZ0123456789")
    End If
    SortableCodeEBCDIC = getSortableCode(text, SortableCharactersEBCDIC)
End Function

Function SortableCodeTEST(text As String)
    If (Not Not SortableCharactersTEST) = 0 Then
        SortableCharactersTEST = getSortableCharacters( _
            orderedCharacters:="ABCDEF 0123456789_")
    End If
    SortableCodeTEST = getSortableCode(text, SortableCharactersTEST)
End Function

Function getSortableCharacters(orderedCharacters As String) As String()

    'Each character X is assigned another character Y so that sort by character Y will
    'sort character X in the desired order.

    maxAscW = 0
    For i = 1 To Len(orderedCharacters)
         If AscW(Mid(orderedCharacters, i, 1)) > maxAscW Then
            maxAscW = AscW(Mid(orderedCharacters, i, 1))
         End If
    Next

    Dim aTemp() As String
    ReDim aTemp(maxAscW)
    j = 0
    For i = 1 To Len(orderedCharacters)
        'Was a character with same "sort weight" previously processed ("a" = "A")
        For i2 = 1 To i - 1
            If AscW(Mid(orderedCharacters, i, 1)) <> AscW(Mid(orderedCharacters, i2, 1)) _
                And Mid(orderedCharacters, i, 1) = Mid(orderedCharacters, i2, 1) Then
                'If two distinct characters are equal when case is ignored (e.g. "a" and "A")
                '(this is possible only because directive "Option Compare Text" is defined at top of module)
                'then only one should be used (either "a" or "A" but not both), so that the Excel sorting
                'does not vary depending on sorting option "Ignore case".
                Exit For
            End If
        Next
        If i2 = i Then
            'NO
            aTemp(AscW(Mid(orderedCharacters, i, 1))) = Format(j, "000")
            j = j + 1
        Else
            'YES "a" has same weight as "A"
            aTemp(AscW(Mid(orderedCharacters, i, 1))) = aTemp(AscW(Mid(orderedCharacters, i2, 1)))
        End If
    Next
    'Last character is for any character of input text which is not in orderedCharacters
    aTemp(maxAscW) = Format(j, "000")

    getSortableCharacters = aTemp

End Function

Function getOrderedCharactersCurrentLocale(numOfChars As Integer) As String

    'Build a string of characters, ordered according to the LOCALE order.
    '    (NB: to order by LOCALE, the directive "Option Compare Text" must be at the beginning of the module)
    'Before sorting, the placed characters are: ChrW(0), ChrW(1), ..., ChrW(numOfChars-1), ChrW(numOfChars).
    'Note that some characters are not used: for those characters which have the same sort weight
    '    like "a" and "A", only the first one is kept.
    'For debug, you may define constdebug=48 so that to use "printable" characters in sOrder:
    '    ChrW(48) ("0"), ChrW(49) ("1"), ..., ChrW(numOfChars+47), ChrW(numOfChars+48).

    sOrder = ""
    constdebug = 0 'Use 48 to help debugging (ChrW(48) = "0")
    i = 34
    Do Until Len(sOrder) = numOfChars
        Select Case constdebug + i
            Case 0, 7, 14, 15: i = i + 1
        End Select
        sCharacter = ChrW(constdebug + i)
        'Search order of character in current locale
        iOrder = 0
        For j = 1 To Len(sOrder)
            If AscW(sCharacter) <> AscW(Mid(sOrder, j, 1)) And sCharacter = Mid(sOrder, j, 1) Then
                'If two distinct characters are equal when case is ignored (e.g. "a" and "A")
                '("a" = "A" can be true only because directive "Option Compare Text" is defined at top of module)
                'then only one should be used (either "a" or "A" but not both), so that the Excel sorting
                'does not vary depending on sorting option "Ignore case".
                iOrder = -1
                Exit For
            ElseIf Mid(sOrder, j, 1) <= sCharacter Then
                'Compare characters based on the LOCALE order, that's possible because
                'the directive "Option Compare Text" has been defined.
                iOrder = j
            End If
        Next
        If iOrder = 0 Then
            sOrder = ChrW(constdebug + i) & sOrder
        ElseIf iOrder = Len(sOrder) Then
            sOrder = sOrder & ChrW(constdebug + i)
        ElseIf iOrder >= 1 Then
            sOrder = Left(sOrder, iOrder) & ChrW(constdebug + i) & Mid(sOrder, iOrder + 1)
        End If
        i = i + 1
    Loop
    'Last character is for any character of input text which is not in orderedCharacters
    sOrder = sOrder & ChrW(constdebug + numOfChars)

    getOrderedCharactersCurrentLocale = sOrder

End Function

Function getSortableCode(text As String, SortableCharacters() As String) As String

    'Used to calculate a sortable text such a way it fits a given order of characters.
    'Example: instead of order _, 0-9, Aa-Zz you may want 0-9, Aa-Zz, _
    'Will work only if Option Compare Text is defined at the beginning of the module.

    getSortableCode = ""
    For i = 1 To Len(text)
        If AscW(Mid(text, i, 1)) < UBound(SortableCharacters) Then
            If SortableCharacters(AscW(Mid(text, i, 1))) <> "" Then
                getSortableCode = getSortableCode & SortableCharacters(AscW(Mid(text, i, 1)))
            Else
                'Character has not an order sequence defined -> last in order
                getSortableCode = getSortableCode & SortableCharacters(UBound(SortableCharacters))
            End If
        Else
            'Character has not an order sequence defined -> last in order
            getSortableCode = getSortableCode & SortableCharacters(UBound(SortableCharacters))
        End If
    Next

    'For two texts "a1" and "A1" having the same sortable code, appending the original text allows using the sort option "Ignore Case"/"Respecter la casse"
    getSortableCode = getSortableCode & " " & text

End Function
0 голосов
/ 08 мая 2020

РЕДАКТИРОВАТЬ: это решение основано на автоматическом c вычислении настраиваемого списка заказов, но оно не работает, если существует слишком много различных значений. В моем случае он работал с настраиваемым списком порядка, возможно, в общей сложности 35000 символов, но не работал для большого списка исходного плаката.


Следующий код сортирует запрошенные столбцы по ASCII значение, которое имеет такой порядок:

0-9, AZ, _, az

Я думаю, что нижний регистр разделяется в верхнем регистре не является проблемой, поскольку SAP определяет значения в основном в верхнем регистре. При необходимости код можно легко адаптировать для получения пользовательского порядка 0-9, Aa-Zz, _ (используя UCase и workheet.Sort.MatchCase = False).

Этот порядок отличается от встроенного в Excel порядка сортировки, который зависит от региона. Например, на английском языке sh это будет:

_, 0-9, Aa-Zz

Принцип состоит в том, чтобы использовать "настраиваемый список заказов". "значения которого взяты из столбца Excel, сделаны уникальными и отсортированы с помощью алгоритма QuickSort3 (подпрограмма MedianThreeQuickSort1 предоставлена ​​Эллисом Ди по адресу http://www.vbforums.com/showthread.php?473677-VB6-Sorting-algorithms- (массив-сортировка-сортировка-массивы) ).

Замечания по производительности при сортировке Excel с помощью настраиваемого списка (я не говорю о QuickSort3):

  • Чем больше различных значений в списке настраиваемого порядка, тем ниже производительность. 4000 строк, содержащих 20 различных значений, сортируются немедленно, но для сортировки 4000 строк с 4000 различных значений требуется 8 секунд!
  • Для того же количества различных значений производительность не сильно меняется, если есть много строк для Сортировать. Для сортировки 300 000 строк с 6 различными значениями требуется 3 секунды.
Sub SortByAsciiValue()
  With ActiveSheet.Sort
    .SortFields.Clear
    .SetRange Range("A:A").CurrentRegion
    .SortFields.Add Key:=Columns("A"), Order:=xlAscending, _
        CustomOrder:=DistinctValuesInAsciiOrder(iRange:=Columns("A"), Header:=True)
    .Header = xlYes
    .Apply
  End With
End Sub

Function DistinctValuesInAsciiOrder(iRange As Range, Header As Boolean) As String
    Dim oCell As Range
    Dim oColl As New Collection

    On Error Resume Next
    For Each oCell In iRange.Cells
        Err.Clear
        If Header = True And oCell.Row = iRange.Row Then
        ElseIf oCell.Row > iRange.Worksheet.UsedRange.Rows.Count Then
        Exit For
        Else
        dummy = oColl.Item(oCell.Text)
        If Err.Number <> 0 Then
            oColl.Add oCell.Text, oCell.Text
            totalLength = totalLength + Len(oCell.Text) + 1
        End If
        End If
    Next
    On Error GoTo 0

    If oColl.Count = 0 Then
        Exit Function
    End If

    Dim values() As String
    ReDim values(1)
    ReDim values(oColl.Count - 1 + LBound(values))
    For i = 1 To oColl.Count
        values(i - 1 + LBound(values)) = oColl(i)
    Next
    Call MedianThreeQuickSort1(values)

    ' String concatenation is complex just for better performance (allocate space once)
    DistinctValuesInAsciiOrder = Space(totalLength - 1)
    Mid(DistinctValuesInAsciiOrder, 1, Len(values(LBound(values)))) = values(LBound(values))
    off = 1 + Len(values(LBound(values)))
    For i = LBound(values) + 1 To UBound(values)
        Mid(DistinctValuesInAsciiOrder, off, 1 + Len(values(i))) = "," & values(i)
        off = off + 1 + Len(values(i))
    Next
End Function
...