Получить список персонажей, которые "принадлежат" к текущей культуре

Я хотел бы создать строку, содержащую все символы, которые текущий пользователь сможет вводить на клавиатуре. Для пользователя в англоязычной культуре это будет 26 прописных и 26 строчных букв, 10 десятичных цифр и более 30 символов. Пользователи из других стран будут иметь несколько разных персонажей.

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

Я могу быстро получить набор символов английского языка США, либо жестко запрограммировав его, либо используя такую ​​функцию:

Function GetCharacterSet() As String
    Return Enumerable.Range(32, 95).Select(Function(i) Chr(i)).ToArray
End Function

Я не уверен, как сделать это надежно для других культур. Я могу закодировать функцию следующим образом:

Function GetCharacterSet() As String
    Dim chars As New List(Of Char) 
    For i As Integer = 0 To UInt16.MaxValue 
        Dim ch As Char = ChrW(i)
        If Char.IsLetterOrDigit(ch) OrElse Char.IsPunctuation(ch) OrElse ch = " "c Then 
            chars.Add(ch)
        End If
    Next
    Return chars.ToArray 
End Function

Но получающаяся (очень длинная) строка содержит символы, допустимые в любой культуре. Есть ли способ проверить, является ли символ буквой, цифрой или пунктуацией только в текущей культуре?

1 ответ

Решение

Ладно, это немного задом наперед, но пока я смог справиться с API-интерфейсами раскладки клавиатуры:

public class Api
{
    [DllImport("kernel32.dll")]
    public static extern uint GetCurrentThreadId();

    [DllImport("user32.dll")]
    public static extern IntPtr GetKeyboardLayout(uint idThread);

    [DllImport("user32.dll", CharSet = CharSet.Unicode)]
    public static extern short VkKeyScanEx(char ch, IntPtr dwhkl);
}

class Program
{
    static bool IsRepresentable(char c, IntPtr keyboardLayout)
    {
        var x = Api.VkKeyScanEx(c, keyboardLayout);
        return x != -1;
    }

    static IEnumerable<char> GetKeyboardLayoutCharacters(IntPtr keyboardLayout)
    {
        return
            Enumerable.Range(32, char.MaxValue - 32)
                .Select(n => (char)n)
                .Where(c => IsRepresentable(c, keyboardLayout));
    }

    static void Main(string[] args)
    {
        Console.OutputEncoding = Encoding.UTF8;
        var layout = Api.GetKeyboardLayout(Api.GetCurrentThreadId());
        Console.WriteLine(string.Concat(GetKeyboardLayoutCharacters(layout)));
    }
}

Это фактически ищет весь BMP и спрашивает, может ли каждый символ быть представлен данной раскладкой клавиатуры. Не идеально, но возвращает следующее:

Немецкий:

 !"#$%&'()*+,-./0123456789:;<=>?@
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
abcdefghijklmnopqrstuvwxyz
{|}~§°²³´µÄÖÜßäöüẞ€

Польский:

 !"#$%&'()*+,-./0123456789:;<=>?@
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
abcdefghijklmnopqrstuvwxyz
{|}~ÓóĄąĆćĘꣳŃńŚśŹźŻż€

Американский английский:

 !"#$%&'()*+,-./0123456789:;<=>?@
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
abcdefghijklmnopqrstuvwxyz{|}~

США Интернешнл;-):

 !"#$%&'()*+,-./0123456789:;<=>?@
ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`
abcdefghijklmnopqrstuvwxyz
{|}~¡¢£¤¥¦§¨©«¬®°²³´µ¶¹»¼½¾¿ÁÄÅÆÇÉÍÐÑÓÖ×ØÚÜÞßáäåæçéíðñóö÷øúüþ‘’€

Я вполне уверен, что должен быть способ на самом деле захватить символы, которые может произвести данная раскладка клавиатуры, потому что в приведенном выше списке не учитываются мертвые клавиши (например, US International может на самом деле производить ÿ, õ, или же ï но их нет в списке, потому что их невозможно создать с помощью Shift, Ctrl или Alt - вы должны использовать мертвые клавиши). Но в первом приближении это может быть уже полезно. Кроме того, есть несколько странных вещей, не столько включение U+007F (что является Del), но скорее U+F000 и U+F001. Может потребоваться дополнительная фильтрация возвращаемого списка.

Этот метод также предполагает, что раскладка клавиатуры активна для пользователя, который представляет его язык. Тем не менее, это, вероятно, наиболее часто, если речь идет только о языке текущего пользователя.

РЕДАКТИРОВАТЬ:

Версия Vb.Net

Private NotInheritable Class NativeMethods
    <DllImport("user32.dll", CharSet:=CharSet.Unicode)>
    Public Shared Function VkKeyScanEx(ByVal ch As Char, ByVal dwhkl As IntPtr) As Short
    End Function
End Class

<Extension>
Public Function IsAlphabetic(ByVal sender As String,
                             ByVal culture As CultureInfo) As Boolean

    If Not CultureInfo.GetCultures(CultureTypes.InstalledWin32Cultures).Contains(culture) Then
        Throw New CultureNotFoundException(paramName:="culture", message:="Culture not installed.")

    Else
        ' Keyboard Layout Handle (HKL)
        Dim hkl As IntPtr = InputLanguage.FromCulture(culture).Handle

        Dim charList As New List(Of Char)
        For index As UShort = 0US To (UShort.MaxValue - 1US)

            Dim c As Char = Convert.ToChar(index)

            ' The check for being a letter can always be removed if symbols or numbers should be allowed, too.
            If (NativeMethods.VkKeyScanEx(c, hkl) <> -1S) AndAlso Char.IsLetterOrDigit(c) Then
                charList.Add(c)
            End If

        Next index

        For Each c As Char In sender
            If Not charList.Contains(c) Then
                Return False
            End If
        Next

        Return True

    End If

End Function
Другие вопросы по тегам