获取当前文化中“属于”的字符列表

3
我想创建一个包含当前用户可以在键盘上输入的所有字符的字符串。对于说英语的用户来说,这将是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

但是生成的(非常长的)字符串包含任何文化中有效的字符。有没有一种方法只检查当前文化中的字母、数字或标点符号?


不确定您在这里的意图是什么,但对于那些在英语字典中出现但并不使用所有“英语”字符的单词,比如“fiancé”,您会怎么处理呢? - DLeh
@DLeh:我不想包含那些用户可能不知道如何在键盘上输入的字符。这意味着在英语中不包括带重音符号的字符(即使它们可能出现在字典中)。 - Blackwood
如果他们从其他地方复制了文本,您会禁止他们在任何地方输入这些字符吗?那里的目标是什么? - DLeh
3
伙计,很多人都不知道如何使用他们已经拥有的字符... ;) - The Blue Dog
2
“我猜测英国的£符号在ASCII字符集中占据了相同的位置”-不,你猜错了。£符号不在ASCII字符集中:它在扩展字符集中,例如CP1252(0xA3)。 - Joe
显示剩余13条评论
1个回答

4

好的,这可能有点反常,但是在键盘布局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
{|}~§°²³´µÄÖÜßäöüẞ€

Polish:

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

中文:

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

美式国际键盘 ;-):

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

我相信一定有一种方法可以实际获取给定键盘布局可以生成的字符,因为上面的列表没有考虑死键(例如,美国国际标准键盘实际上可以产生ÿ、õ或ï,但它们不在列表中,因为使用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

我遇到了一些函数上的问题,它为西班牙语返回了这个字符集ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzªºÇÑçñ,然而字符 ªº 是特殊的符号,但是 Char.IsLetter() 函数却认定它们是字母,这是为什么呢? - ElektroStudios
@ElektroStudios:ªº都属于Unicode类别Letter, other,因此在Unicode中它们是字母。正如我之前提到的,我编写的方法只能处理没有死键的字符。我还没有成功地让ToUnicodeEx起作用。另一个选择可能是查询每种可能的键盘状态,包括将组合死键与其他所有键组合。我担心这样会带来很多问题。 - Joey
我认为有一些字母丢失了。我使用tr-TR键盘和布局在我的计算机上尝试了这段代码。例如,输出不包含"I"和"ı"。 - Kemal Kefeli
@Joey:我在控制台输出(Console.WriteLine)中看不到,但如果我将输出写入txt文件中,那么我就可以看到这些字母 :) 所以,代码是有效的,谢谢。 - Kemal Kefeli
1
@Kemal:在这种情况下,您可能希望将控制台设置为TrueType字体(例如Lucida Console或Consolas)。如果它设置为光栅字体,则无论内部运行的应用程序如何,控制台都仅限于OEM字符集。 - Joey
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接