我正在c#中制作一个Vigenere密码程序,但是我遇到了一个问题,我没有"Ñ"我想要像Vigenere密码那样加密但是用"Ñ"如何添加字母"Ñ "这个代码?这样,密钥和s都保持这种方式:a = 0 b = 1 ... n =13ñ= 14 ... z = 26之后的地方飞行
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Program
{
static void VigenereEncrypt(ref StringBuilder s, string key)
{
for (int i = 0; i < s.Length; i++) s[i] = Char.ToUpper(s[i]);
key = key.ToUpper();
int j = 0;
for (int i = 0; i < s.Length; i++)
{
if (Char.IsLetter(s[i]))
{
s[i] = (char)(s[i] + key[j] - 'A');
if (s[i] > 'Z') s[i] = (char)(s[i] - 'Z' + 'A' - 1);
}
j = j + 1 == key.Length ? 0 : j + 1;
}
}
static void VigenereDecrypt(ref StringBuilder s, string key)
{
for (int i = 0; i < s.Length; i++) s[i] = Char.ToUpper(s[i]);
key = key.ToUpper();
int j = 0;
for (int i = 0; i < s.Length; i++)
{
if (Char.IsLetter(s[i]))
{
s[i] = s[i] >= key[j] ?
(char)(s[i] - key[j] + 'A') :
(char)('A' + ('Z' - key[j] + s[i] - 'A') + 1);
}
j = j + 1 == key.Length ? 0 : j + 1;
}
}
public static void Main()
{
while (true)
{
StringBuilder s = new StringBuilder(Console.ReadLine());
string key = Console.ReadLine();
VigenereEncrypt(ref s, key);
Console.WriteLine(s);
VigenereDecrypt(ref s, key);
Console.WriteLine(s);
Console.ReadLine();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
你的代码:
if (Char.IsLetter(s[i]))
{
s[i] = (char)(s[i] + key[j] - 'A');
if (s[i] > 'Z') s[i] = (char)(s[i] - 'Z' + 'A' - 1);
}
Run Code Online (Sandbox Code Playgroud)
取决于U + 0041到U + 005A的字母恰好与某些语言的字母表字母匹配,例如英语*.(如果测试依赖于此而不仅仅是检查它是一个字母,那么你将Ñ保持不变而不是出错).还有一些其他语言的字母表是连续的,并且在UCS中按顺序排列,但大多数语言不是.
因此,您需要定义自己的字母表.对于大多数用途,字符串是一种简单的方法.
string spanishAlphabet = "ABCDEFGHIJKLMNÑOPQRSTUVWXYZ";
string englishAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string irishAlphabet = "ABCDEFGHILMNOPRSTU";
string danishAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ";
string norwegianAlphabet = danishAlphabet;
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用您关注的字母表,而不是依赖于字母表和UCS之间的重合:
static void VigenereEncrypt(StringBuilder s, string key, string alphabet)
{
for (int i = 0; i < s.Length; i++) s[i] = Char.ToUpper(s[i]);
key = key.ToUpper();
int j = 0;
for (int i = 0; i < s.Length; i++)
{
if(alphabet.Contains(s[i]))
s[i] = alphabet[(alphabet.IndexOf(s[i]) + alphabet.IndexOf(key[j])) % alphabet.Length];
j = (j + 1) % key.Length;
}
}
static void VigenereDecrypt(StringBuilder s, string key, string alphabet)
{
for (int i = 0; i < s.Length; i++) s[i] = Char.ToUpper(s[i]);
key = key.ToUpper();
int j = 0;
for (int i = 0; i < s.Length; i++)
{
if(alphabet.Contains(s[i]))
{
s[i] = alphabet[(alphabet.IndexOf(s[i]) - alphabet.IndexOf(key[j]) + alphabet.Length) % alphabet.Length];
j = (j + 1) % key.Length;
}
}
}
Run Code Online (Sandbox Code Playgroud)
(我假设密钥总是仅由相关的字母组成,一个更强大的解决方案不会做出这样的假设,但是在这种情况下,有一些不同的方法应该做什么,所以那里是不是一个正确的方法来解决这个问题,我忽略了这个问题).
我还取出了ref关键字,因为该StringBuilder签名表明其他参考文献没有更改,但是就地变异了.更惯用的方法是接收字符串并返回另一个字符串:
static string VigenereEncrypt(string s, string key, string alphabet)
{
s = s.ToUpper();
key = key.ToUpper();
int j = 0;
StringBuilder ret = new StringBuilder(s.Length);
for (int i = 0; i < s.Length; i++)
{
if(alphabet.Contains(s[i]))
ret.Append(alphabet[(alphabet.IndexOf(s[i]) + alphabet.IndexOf(key[j])) % alphabet.Length]);
else
ret.Append(s[i]);
j = (j + 1) % key.Length;
}
return ret.ToString();
}
static string VigenereDecrypt(string s, string key, string alphabet)
{
s = s.ToUpper();
key = key.ToUpper();
int j = 0;
StringBuilder ret = new StringBuilder(s.Length);
for (int i = 0; i < s.Length; i++)
{
if(alphabet.Contains(s[i]))
ret.Append(alphabet[(alphabet.IndexOf(s[i]) - alphabet.IndexOf(key[j]) + alphabet.Length) % alphabet.Length]);
else
ret.Append(s[i]);
j = (j + 1) % key.Length;
}
return ret.ToString();
}
Run Code Online (Sandbox Code Playgroud)
如果你想处理Unicode不认为单个字符作为字母的字符串,例如IJ荷兰语†这会变得更复杂.一种可能性是对这样的序列使用标记字符,然后在加密‡之前首先用它替换序列的每个情况,然后如果标记出现在输出中则再次替换.人们必须确保标记字符没有出现在输入中,这将使像U + FFFE这样的非字符在这里有用.
不被视为字母表中不同部分的变音符号(如Ñ西班牙语)是另一个复杂因素.在像Vigenère这样的密码实际使用的那些日子里,通常只是剥离变音符号并处理输出不应该具有变音符号这一事实.一种简单的方法是使用如下方法:
public static IEnumerable<char> RemoveDiacriticsEnum(string src, string alphabet)
{
foreach(char c in src.Normalize(NormalizationForm.FormD))
if(alphabet.Contains(c)) // Catch e.g. Ñ in Spanish, considered letter in own right
yield return c;
else
switch(CharUnicodeInfo.GetUnicodeCategory(c))
{
case UnicodeCategory.NonSpacingMark:
case UnicodeCategory.SpacingCombiningMark:
case UnicodeCategory.EnclosingMark:
//do nothing
break;
default:
yield return customFolding(c);
break;
}
}
Run Code Online (Sandbox Code Playgroud)
然后使用一个循环来执行foreach(char c in RemoveDiacriticsEnum(s, alphabet))并使用c上面代码使用的位置s[i].这不包括所有情况,请参阅/sf/answers/263899681/了解一些可能的并发症.
或者,可以在字母表中包含常见的重音组合:
string spanishAlphabet = "AÁBCDEÉFGHIÍJKLMNÑOÓPQRSTUÚÜVWXYZ";
Run Code Online (Sandbox Code Playgroud)
*严格来说,关于某些其他角色,特别是Ð,Ȝ和Þ的位置,如果使用的话,有各种各样的惯例,所以现代英语字母的一个版本是A,B,C,D,[Ð],E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,[?],Z,[Þ],一般不会列出Ð,但如果有的话在您的数据中以单词开头的单词,您将其置于D和之间E,依此类推.这是现代英语中一个不起眼的案例(我们不再使用这些字母),但在其他一些语言中可能更为重要; 例如,爱尔兰的字母表A,B,C,D,E,F,G,H,I,L,M,N,O,P,R,S,T,U,但V在几个拟声词被使用,并且J,K,Q,V,W,X,Y,Z在一些外来词的各个找到,所以我们可以列出爱尔兰字母表A,B,C,D,E,F,G,H,I,[J],[K],L,M,N,O,P,[Q],R,S,T,U,[V],[W],[X],[Y],[Z],一般不列出括号内的文字,但定位例如J之间I以及L如果一个字开头J是一组数据.这使像Vigenère这样的密码问题变得复杂,因为我们必须在计算中使用字母而不是严格的字母表部分,否则不加密V像vótaí这样的单词.
†虽然?UCS中的U + 0132处有一个字符,但这是为了与传统编码兼容.仍然使用?作为标记字符IJ将整齐地处理IJ已使用的数据和数据?.
‡ 加密方式相当宽松,因为这种加密方案在19世纪中叶被打破了.