首页 > 解决方案 > Vigenere 密码,同时保持空间并仅加密以辅音字母开头的单词 [Java]

问题描述

我正在尝试编写一个程序,该程序将使用 Vigenere 密码加密一个句子,但只有以辅音字母开头的单词,同时存储空格。我对Java还不是很好,但是我写了一个方法来加密任何给定的单词(所有小写字母)我曾认为使用数组在需要的地方打印空格并忽略以元音开头的单词就足够了,但这样做实际上是每当我打印数组的第二个单词左右时给我错误的输出。有人可以指导我做错了什么吗?有没有更好的方法来做到这一点?到目前为止,这是我的代码:

public class Main {
public static void main(String[] args) {

    Scanner u = new Scanner(System.in);
    final String k = u.next();
    u.nextLine();
    String message = u.nextLine();
    String[] f = message.split(" ");
    System.out.println(encipher(message,k));

    for (String s : f) {
        if (s.charAt(0) == 'a' || s.charAt(0) == 'e' || s.charAt(0) == 'i' ||
                s.charAt(0) == 'o' || s.charAt(0) == 'u') {
            System.out.println(s);
        } else {
            System.out.println(encipher(s, k));
        }

    }
}


public static String encipher(String message, final String key)
{
    String output = "";

    for (int x = 0, y = 0; x < message.length(); x++)
    {
        char c = message.charAt(x);
        if (c < 'a' || c > 'z')
            continue;
        output += (char) ((c + key.charAt(y) - 2 * 'a') % 26 + 'a');
        y = ++y % key.length();
    }

    return output;
}}

您可以看到正常输出(忽略空格和元音/辅音)和数组输出之间的差异。对于字符串键obi和字符串消息“olimpiada brasileira de informatica”,它应该打印“olimpiada psigjtsjzo em informatica”,但在数组中打印“olimpiada psigjtsjzo rf informatica”[暂时忽略打印行的事情,我会在我修复它获得正确的加密]

标签: javastringencryption

解决方案


逐字应用加密时,会为每个单词重置密钥,并使用以下"de"命令进行加密"ob"

brasileira
obiobiobio

de
ob

当整个消息被加密时,密钥不会被重置("olimpiada"有 9 个可以被 整除的字母"obi".length() == 3),并"de"用 加密"bi"

olimpiada brasileira de informatica
obiobiobi obiobiobio bi obiobiobiob

因此,如果可能需要以某种方式记住y索引,或者应该实现另一种方法来在下一个索引处的非空白键中提供一个字符:

private static Map<String, Integer> keyIndexes = new HashMap<>();

private static char getNextPosition(String key) {
    return key.charAt(keyIndexes.compute(
        key, (k, v) -> v == null ? 0 : (v + 1) % k.length()
    ));
}

private static void resetKey(String key) {
    keyIndexes.remove(key);
}

然后该方法encipher可以更新为:

public static String encipher(String message, final String key)
{
    StringBuilder output = new StringBuilder();

    for (char c : message.toCharArray())
    {
        if (c < 'a' || c > 'z')
            continue; // skip non-letters

        output.append((char) ((c + getNextPosition(key) - 2 * 'a') % 26 + 'a'));
    }

    return output.toString();
}

测试:

String message = "olimpiada brasileira de informatica";
String k = "obi";
System.out.println(encipher(message, k));
// !!!
resetKey(k);

for (String s : message.split(" ")) {
    if (s.matches("[aeiou]\\w*")) { // use regexp to detect a word starting with a vowel
        System.out.println(s);
    } else {
        System.out.println(encipher(s, k));
    }
}

输出:

cmqaqqoeipsigjtsjzoemwoncsuouqqb   // full message, spaces skipped
olimpiada
psigjtsjzo
em
informatica

如果需要保留非字母和以元音开头的单词作为整个消息(不仅仅是单个单词),则应添加几个标志来跟踪当前字符是否在单词中,如果第一个已检测到元音:

public static String encipherWork(String message, final String key) {
    StringBuilder output = new StringBuilder();
    
    boolean inWord = false;
    boolean firstVowel = false;

    for (char c : message.toCharArray()) {
        if ('a' <= c && c <= 'z') {
            if (!inWord) {
                firstVowel = (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u');
                inWord = true;
            }
            if (!firstVowel) {
                output.append((char) ((c + getNextPosition(key) - 2 * 'a') % 26 + 'a'));
                continue;
            }
        } else {
            inWord = false;
            firstVowel = false;
        }
        output.append(c); // print non-enciphered characters
    }

    return output.toString();
}

此方法的输出:

olimpiada psigjtsjzo em informatica // full message
olimpiada
psigjtsjzo
em
informatica

推荐阅读