我是C++的新手。我最近完成了一个命令行程序,它通过测试字典中的每个单词从文件中破解Vigenere编码的消息。
我从来没有C++的家教/老师,所以我想确保我没有养成任何坏习惯。
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include "Alphanumeric.h" //turns characters into numerical equivalents
#include "Vigenere.h" //Vigenere Functions
#include <list>
#ifdef __APPLE__
#define DICTPATH "/usr/share/dict/words"
#endif
int main(int argc, char* argv[])
{
std::cout << "*************VIGENERE BREAKER*************" << std::endl;
std::ifstream input(argv[1]);
std::ifstream dictionary(DICTPATH);
std::string process = argv[2];
std::string key = argv[3];
std::string plain;
std::string str;
while (std::getline(input, str))
{
plain += str;
}
if (process == "-e")
{
std::cout << "*************ENCODE*************" << std::endl;
std::string result = Vigenere::EncryptVigenere(plain, key);
std::cout << result << std::endl;
}
if (process =="-d")
{
std::cout << "*************DECODE*************" << std::endl;
std::string result = Vigenere::DecryptVigenere(plain, key);
std::cout << result << std::endl;
}
if (process == "-dicf")
{
const float englishfreq[] = {8.2,1.5,2.8,4.3,12.7,2.2,2,6.1,7,0.2,0.8,4,2.4,6.7,7.5,1.9,0.1,6,6.3,9.1,2.7,1,2.4,0.15,1.9,0.074};
const int threshold = atoi(argv[3]);
std::cout << "*************DICTIONARY_ATTACK*************" << std::endl;
std::list<std::string> results;
while (std::getline(dictionary, str))
{
float total = 0;
float frequency[26] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
std::cout << str << std::endl;
std::string result = Vigenere::DecryptVigenere(plain, str);
for (char c : result)
{
frequency[Alphanumeric::chartoint(c)]++;
}
int size = result.size();
int index = 0;
for (float freq : frequency)
{
freq = (freq / size)*100;
float difference = freq - englishfreq[index];
if (difference < 0)
{
difference *= -1;
}
total += difference;
index++;
}
if (total <= threshold) {
std::cout << "RESULT FOUND" << std::endl;
results.push_back("Using " + str);
results.push_back(result);
}
}
std::cout << "*************RESULTS*************" << std::endl;
for (std::string s : results)
{
std::cout << s << std::endl;
}
std::cout << "dictionary attack complete!!!" << std::endl;
}
if (process == "-dic")
{
std::cout << "*************DICTIONARY_ATTACK*************" << std::endl;
std::list<std::string> results;
std::string keyword = argv[3];
while (std::getline(dictionary, str))
{
std::cout << "key:";
std::cout << str << std::endl;
std::string result = Vigenere::DecryptVigenere(plain, str);
if (result.find(keyword) != std::string::npos) {
std::cout << "RESULT FOUND" << std::endl;
results.push_back("Using " + str);
results.push_back(result);
}
}
std::cout << "*************RESULTS*************" << std::endl;
for (std::string s : results)
{
std::cout << s << std::endl;
}
std::cout << "dictionary attack complete!!!" << std::endl;
}
return 0;
}#ifndef VIGENERE_H_
#define VIGENERE_H_
namespace Vigenere
{
std::string DecryptVigenere(std::string ciphertext, std::string key);
std::string EncryptVigenere(std::string plaintext, std::string key);
}
#endif /* VIGENERE_H_ */#include <string>
#include "Vigenere.h"
#include "Alphanumeric.h"
std::string Vigenere::DecryptVigenere(std::string ciphertext, std::string key)
{
std::string answer;
for (int i = 0; i < ciphertext.length(); i++)
{
int numeric = Alphanumeric::chartoint(ciphertext[i]);
int keyval = Alphanumeric::chartoint(key[i%key.length()]);
numeric -= keyval;
if (numeric < 0)
{
numeric += 26;
}
numeric %= 26;
answer += Alphanumeric::inttochar(numeric);
}
return answer;
}
std::string Vigenere::EncryptVigenere(std::string plaintext, std::string key)
{
std::string answer;
for (int i = 0; i < plaintext.length(); i++)
{
int numeric = Alphanumeric::chartoint(plaintext[i]);
int keyval = Alphanumeric::chartoint(key[i % key.length()]);
numeric += keyval;
numeric %= 26;
answer += Alphanumeric::inttochar(numeric);
}
return answer;
}发布于 2016-11-29 08:28:16
std::string DecryptVigenere(std::string ciphertext, std::string key);
std::string EncryptVigenere(std::string plaintext, std::string key);对于输入参数,通过引用到-const传递非原语类型,以避免不必要的分配。见康斯特-正确性和参数传递。
std::string DecryptVigenere(std::string const& ciphertext,
std::string const& key);
std::string EncryptVigenere(std::string const& plaintext,
std::string const& key);#include <string>
#include "Vigenere.h"
#include "Alphanumeric.h"通过健壮性组织您的头,以便尽快捕获潜在使用错误。
#include "Vigenere.h" // Interface
#include "Alphanumeric.h" // Project-Level header
#include <string> // Language-Level headerstd::string EncryptVigenere(std::string const& plaintext,
std::string const& key);
{
std::string answer;当您知道容器所需的空间大小时,请使用reserve成员函数预先分配空间。在两个转换函数中,答案的长度总是与第一个输入参数的长度相同。
std::string EncryptVigenere(std::string const& plaintext,
std::string const& key);
{
std::string answer;
answer.reserve(plaintext.size()); int keyval = Alphanumeric::chartoint(key[i%key.length()]);确保测试你的先决条件。key必须是非空的,因为您不能将/模块化除以零(根据C++标准§5.6/4,未定义的行为)。
与格式一致(操作符间距、函数命名)。
std::string encrypt_vigenere(std::string const& plaintext,
std::string const& key);
{
if (key.empty()) {
throw std::invalid_argument("Invalid key length of Zero.");
}
// ...
int keyval = Alphanumeric::char_to_int(key[i % key.length()]); std::cout << "*************VIGENERE BREAKER*************" << std::endl;要知道std::endl实际上做了什么。
将换行符插入到输出序列
os中,并像调用os.put(os.widen('\n'))和os.flush()一样刷新它。
如果您不打算强制缓冲区刷新,请考虑只输出'\n'。
std::cout << "*************VIGENERE BREAKER*************\n"; std::ifstream input(argv[1]);
std::string process = argv[2];
std::string key = argv[3];检查以确保这些参数确实存在。超出界限的索引访问是未定义的行为。
const限定了不可变对象。
if (argc != 4) {
// TODO: Print a helpful message instead.
std::cerr << "The syntax of the command is incorrect.\n";
return EXIT_FAILURE;
}
std::ifstream input(argv[1]);
const std::string process = argv[2];
const std::string key = argv[3];#ifdef __APPLE__
#define DICTPATH "/usr/share/dict/words"
#endif
// ...
std::ifstream dictionary(DICTPATH);如果没有定义__APPLE__怎么办?如果有备用字典就好了。
不要使用宏来定义变量。如果需要对变量、函数或对象进行编译时评估,请使用constexpr。源代码管理仍然是宏的合法用例。
#ifdef __APPLE__
constexpr auto dictionary_path = "/usr/share/dict/words";
#else
constexpr auto dictionary_path = "english.txt"
#endif
// ...
std::ifstream dictionary(DICTPATH); if (process == "-e") { /* ... */ }
if (process == "-d") { /* ... */ }
if (process == "-dicf") { /* ... */ }
if (process == "-dic") { /* ... */ }process可以支路的块是相互排斥的。要强制执行这种排他性,您可以使用else if向读者表明正在发生这种情况。
如果进程无效,应该发生什么?
if (process == "-e") { /* ... */ }
else if (process == "-d") { /* ... */ }
else if (process == "-dicf") { /* ... */ }
else if (process == "-dic") { /* ... */ }
else { /* handle invalid process */ } frequency[Alphanumeric::chartoint(c)]++;这在我看来有点可疑。我希望Alphanumeric类或函数集合返回一组字符和数字的映射。26个角色看上去不太对。
if (total <= threshold) {
std::cout << "RESULT FOUND" << std::endl;
results.push_back("Using " + str);
results.push_back(result);
}因为结果没有保存在任何东西上,所以当你找到结果时,就打印出来。
发布于 2016-11-28 14:33:12
如果您是C++的新手,这是非常好的,但是我认为您可以用C++做的最糟糕的事情之一是像C一样使用它。如果您不使用面向对象的范例,那么您更有可能使用STL编写C代码,而不是实际的C++。
如果您需要这个练习的第二个版本的想法,您可以尝试以一种将代码从主要功能中提取出来的方式来设计它,以便在课堂上组织它。这还允许您轻松地存储和使用您复制并粘贴到多个地方的神奇数字"26“(这是一个非常糟糕的实践,因为它会损害可维护性)。
检查命令行参数错误并给出反馈可能是个好主意。您应该检查argv[]的长度以避免任何问题,但是如果用户的参数不符合您的期望,您也应该向他提供反馈。
您正在使用std::list来存储结果,但您似乎只对其使用了push_back。std::vector将是一个更适合您使用的容器。
我也喜欢foreach循环,但是当您需要跟踪索引时,这可能意味着您应该使用一个标准的for循环。这就是在频率数组上迭代时所做的事情。
https://codereview.stackexchange.com/questions/148335
复制相似问题