Если вы хотите, чтобы значения в результирующем List<Value>
находились в порядке ввода ключа Map<Key, Value>
, вам нужно как-то «пройти через» SortedMap
.
Либо запустите с конкретной реализацией SortedMap
(например TreeMap
) или вставьте ваш вход Map
в SortedMap
, прежде чем преобразовать его в List
. например:
Map<Key,Value> map;
List<Value> list = new ArrayList<Value>( new TreeMap<Key Value>( map ));
В противном случае вы получите любой нативный порядок, предоставляемый реализацией Map
, который часто может быть чем-то иным, чем упорядочение естественного ключа (Try Hashtable
или ConcurrentHashMap
, для разнообразия ).
Все функции преобразования чисел в VBA осведомлены о локали, поэтому они будут игнорировать как тысячи разделителей, так и символы валюты. Функция IsNumeric
ведет себя таким же образом:
Public Sub Example()
'en-US locale
Debug.Print IsNumeric("$1,1,1,1,1,") 'True
Debug.Print CLng("$1,1,1,1,1,") '11111
End Sub
Единственный обходной путь, не связанный с хостом, о котором я знаю (если не хранить числа в виде данных String
), это полностью обходить встроенные приведения VBA и вызывать базовые функции преобразования в oleaut32.dll напрямую с жестко заданным идентификатором локали. Например, чтобы получить Double
из локализованной строки в США:
Public Declare PtrSafe Function VarR8FromStr Lib "oleaut32" _
(ByVal strIn As LongPtr, ByVal lcid As Long, ByVal dwFlags As Long, ByRef pdblOut As Double) As Long
Public Const EN_US As Long = 1033
Public Function DoubleFromEnUsString(converting As String) As Double
Dim converted As Double, result As Long
result = VarR8FromStr(StrPtr(converting), EN_US, 0, converted)
If (result = 0) Then
DoubleFromEnUsString = converted
Else
Err.Raise 5
End If
End Function
... или Long
:
Public Declare PtrSafe Function VarI4FromStr Lib "oleaut32" _
(ByVal strIn As LongPtr, ByVal lcid As Long, ByVal dwFlags As Long, ByRef plOut As Long) As Long
Public Const EN_US As Long = 1033
Public Function LongFromEnUsString(converting As String) As Long
Dim converted As Long, result As Long
result = VarI4FromStr(StrPtr(converting), EN_US, 0, converted)
If (result = 0) Then
LongFromEnUsString = converted
Else
Err.Raise 5
End If
End Function
Пример использования:
Public Sub Sample()
Debug.Print LongFromEnUsString("12.34") '12
Debug.Print DoubleFromEnUsString("12.34") '12.34
End Sub
Двумя наиболее распространенными десятичными разделителями являются "."
(США, Великобритания ...) и ","
(Италия, Франция ...). Вы можете легко получить доступ к этому свойству из VBA, используя Application.DecimalSeparator
(возвращая разделитель ОС).
Правильное решение состояло бы не в том, чтобы "22.14"
трактовалось как String
, а как действительное число, так что такая проблема интернационализации была разрешена для операционной системы.
Если вы действительно не можете иметь этот номер иначе, чем String
(например, вы получаете эти значения из англо-американского API, который отправляет вам десериализованные числа), тогда Вы можете использовать аналогичную функцию:
Function InternationalCLng(ByVal st As String)
Dim EnglishSep As Boolean: EnglishSep = ("." = Application.DecimalSeparator)
If Not EnglishSep Then
st = Replace(st, ".", Application.DecimalSeparator)
End If
InternationalCLng = CLng(st)
End Function
... которая заменяет .
на правую DecimalSeparator
и использовать вместо нее InternationalCLng("22.14")
. Но, честно говоря, это взломать.