2014-10-22 3 views
-3

Я делаю программу шифрования Vigenere в C#, но у меня проблема. У меня нет «Ñ», которую я хотел бы зашифровать, как это происходит в шифре Vigenere, но с «С», как добавить к этому коду букву «С»? такие, что оба ключа и s остаются таким образом: а = 0 Ь = 1 ... п = 13 С = 14 ... г = 26 после п, что место быть пролетовVigenere Cipher C# с «ñ»

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(); 
     } 
    } 

} 
} 
+0

Итак ... в чем проблема? – BradleyDotNET

+0

, когда я ввожу пароль больше, чем письмо с расширением дает мне ошибку –

+0

И эта ошибка есть? Какая линия? Пожалуйста, включите каждую возможную информацию об ошибке. – BradleyDotNET

ответ

6

Ваш код:

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); 
} 

в зависимости от того, что буквы от U + 0041 до U + 005A просто случается, сортируют буквы алфавитов некоторых языков, таких как английский язык *. (Если бы тест зависел от этого, а не просто проверял, что это письмо, то вы оставите Ñ без изменений, а не получите ошибку). Существуют и другие языки, алфавит которых смежны и упорядочены в UCS, но большинство языков - нет.

По этой причине вам необходимо определить свой собственный алфавит. Строка - достаточно простой способ сделать это для большинства целей.

string spanishAlphabet = "ABCDEFGHIJKLMNÑOPQRSTUVWXYZ"; 
string englishAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; 
string irishAlphabet = "ABCDEFGHILMNOPRSTU"; 
string danishAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ"; 
string norwegianAlphabet = danishAlphabet; 

Тогда вместо того, чтобы зависеть от совпадения между алфавитом и 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; 
    } 
    } 
} 

(я предполагаю, что ключ всегда состоит только из алфавит в вопросе, более надежное решение не сделало бы этого предположения, но есть несколько разных подходов к тому, что нужно делать в таком случае, поэтому нет ни одного правильного способа справиться с этим, и я проигнорировал проблема).

Я также достал ключевое слово 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(); 
} 

Если вы хотите обработать строки, 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; 
     } 
} 

И затем использовать цикл, который делает foreach(char c in RemoveDiacriticsEnum(s, alphabet)) и использует c где код выше использует s[i]. Это не будет охватывать все случаи, см. https://stackoverflow.com/a/3769995/400547 для некоторых возможных осложнений.

В качестве альтернативы можно было бы включать в себя общие комбинации акцента в алфавите:

string spanishAlphabet = "AÁBCDEÉFGHIÍJKLMNÑOÓPQRSTUÚÜVWXYZ"; 

* Строго говоря, существует целый ряд соглашений о том, где некоторые другие символы, в частности, Ð, Ȝ и Þ должен быть установлен, если используется , поэтому одна версия современного английского алфавита - 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 есть символ IJ в U + 0132, это совместимо с устаревшими кодировками. Все еще используя IJ в качестве символа-маркера для IJ будет аккуратно обрабатывать как IJ, так и данные, которые использовали IJ.

Шифрование в довольно свободном смысле, поскольку эта схема шифрования была нарушена к середине 19-го века.