テキストを暗号化し復号するコードを作ったのですが

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
hasimasqqsan
記事: 4
登録日時: 1年前

テキストを暗号化し復号するコードを作ったのですが

#1

投稿記事 by hasimasqqsan » 1年前

テキストを0から26までの数字に暗号化し、元のテキストに復号するコードを作りましたが、欠点があります。例えば暗号化するプレーンテキストは、T,H,I,Sなど一時ずつ ','で区切らないといけないし、復号の際は最後の数字の後に','をつけないと正しく復号してくれません。
またアルファベットだけでなく、日本語のひらがなも暗号化できるようにするにはどうすればよいのでしょうか。
お知恵を拝借できましたら、ありがたく存じます。

コード:

// 文字列をシャッフルして配列にし、暗号化と複合(std::shuffle使用)
#include <numeric>
#include <iterator>
#include <random>
#include <iostream>
#include <algorithm>
#include <random>


int main()
{
    std::string PRAIN = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    // std::cout << PRAIN << std::endl;

    std::random_device seed_gen;
    std::mt19937 engine(seed_gen());
    std::shuffle(PRAIN.begin(), PRAIN.end(), engine);

    // std::cout << PRAIN << std::endl;
    char alp[26] = {'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'};
    PRAIN.copy(alp, 26);

    for (int i = 0; i < 26; i++) {
        // std::cout << alp[i] << ' ';
    }

    int array_size = 26;
    int tagPos = 0;

    std::string plain;
    std::cout << "アルファベット大文字と','でテキストを入力してください。\n";
    std::cin >> plain;
    std::cout << "暗号文は : ";
    for (int i = 0; i < (int)plain.size(); ++i) {
        char p = plain[i];
        while (p != alp[tagPos] && p != ',') {
            if (tagPos < array_size) {
                tagPos++;
            } else {
                tagPos = 0;
            }
        }
    if (p != ',') std::cout << tagPos << ' ';
    }
    std::cout << "です。" << "\n";
    
    std::string cip;
    std::cout << "数字のあとに','を付けて暗号文を入力してください(例: 1,10,22,) : ";
    std::cin >> cip;
    std::cout << "復号された原文は : ";
        for (int j = 0; j < (int)cip.size(); j++) {
            if (cip[j] != ',' && cip[j + 1] == ',') {
                int c = cip[j] - '0';
                std::cout << alp[c];
            } else if (cip[j] != ',' && cip[j + 1] != ',') {
                int c = ((cip[j] - '0') * 10) + (cip[j + 1] - '0');
                std::cout << alp[c];
                j = j + 1;
            } else if (cip[j] == ',') {
                std::cout << ' ';
            } else {
                return 0;
            }
        }
    std::cout << "です。" << std::endl;
    
}

アバター
usao
記事: 1887
登録日時: 11年前

Re: テキストを暗号化し復号するコードを作ったのですが

#2

投稿記事 by usao » 1年前

あなたが「欠点」と述べている現仕様を定めた理由は何ですか?

…と問うのは,現状そのようになっていることには何かしらの理由があるハズだと思うから.
(だって,そうじゃなければ,わざわざそんな不自由な形にしないだろうし,「そういう形にしてやろう」とか思いついたりもしないよね?)

要は,何が必要事項なのかが他者には不明なのでこのままでは回答不能かと見えます.
(仮に何のこだわりもないのであれば,「じゃあその仕様をやめればいいよね」っていう話にしかならない)

---

ひらがなに関しては,

> 0から26までの数字に暗号化

という仕様のままで何とかしたいという方向の話なのか,相応に値の範囲を拡張するような方向性なのか,その他なのか,という方針次第でしょうし,
どのような暗号化の方式ならば OK/NG なのか? という線引きも不明なので,そこらへんの詳細を述べる必要があるでしょう.

アバター
usao
記事: 1887
登録日時: 11年前

Re: テキストを暗号化し復号するコードを作ったのですが

#3

投稿記事 by usao » 1年前

で,それはそれとして…

> 暗号化するプレーンテキストは、T,H,I,Sなど一時ずつ ','で区切らないといけない

本当ですか? そうとも見えませんが.
むしろカンマを入力させること自体の意味が不明と見えます.
(本来は「カンマの入力を必要とするもの」を作っているハズなのであれば,現状のコードはその点に関して間違っているのかもしれません)


> 復号の際は最後の数字の後に','をつけないと正しく復号してくれません。

何かを「カンマである」という条件だけで判断しているからでしょう.
そのような個所を例えば「カンマあるいは入力の末尾のいずれか」みたいな話にすれば良いのではないでしょうか.
この点に関しては,例えば「入力文字列の i 番目の文字から次の区切り文字(あるいは入力末尾)までの範囲を取り出してくる」みたいな作業を行う関数でも作ってやれば見通しが良くなるのではないかと見えます.

アバター
usao
記事: 1887
登録日時: 11年前

Re: テキストを暗号化し復号するコードを作ったのですが

#4

投稿記事 by usao » 1年前

オフトピック
> …みたいな作業を行う関数

「んなもん,stringのメソッドを使えばよくね?」みたいな無粋な突っ込みが入りそうな気がしないでもないので,先手を打って述べておくぜ!

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: テキストを暗号化し復号するコードを作ったのですが

#5

投稿記事 by みけCAT » 1年前

C++で
・テキストを数値列にする
・数値列をコンマ区切りの10進文字列にする
・コンマ区切りの10進文字列を数値列にする
・数値列をテキストにする
を実装してみました。
「0から26までの数字」とのことだったので、とりあえずひらがなは「あ」のみの対応です。
参考になれば幸いです。

コード:

#include <cctype>
#include <iostream>
#include <map>
#include <sstream>
#include <string>
#include <vector>

// (この形式での初期化はC++11以降の仕様)
const std::vector<std::string> characterTable = {
	"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",
	"あ"
};

// 文字列を数値の列に変換する
std::vector<int> stringToNumbers(const std::string& str) {
	static bool initialized = false;
	static std::map<std::string, int> characterToInt;
	static int characterMaxSize;
	// 文字を数値に変換するテーブルを初期化する
	if (!initialized) {
		characterMaxSize = 0;
		for (int i = 0; static_cast<unsigned int>(i) < characterTable.size(); i++) {
			// 文字と数値の対応を記録する
			characterToInt[characterTable[i]] = i;
			// 文字の最大サイズを調べる
			int characterSize = static_cast<int>(characterTable[i].size());
			if (characterSize > characterMaxSize) characterMaxSize = characterSize;
		}
		initialized = true;
	}
	// 文字列を数値列に変換する
	std::vector<int> result;
	for (std::string::size_type i = 0; i < str.size(); ) {
		// 文字に対応する数値を探す
		int foundSize = 1, foundNumber = -1;
		// バイト数の多い文字を優先してマッチさせる
		for (int j = characterMaxSize; j > 0; j--) {
			if (i + j > str.size()) continue;
			std::string key(str.begin() + i, str.begin() + i + j);
			std::map<std::string, int>::iterator it = characterToInt.find(key);
			if (it != characterToInt.end()) {
				// 対応する数値が見つかったので、探索を終了する
				foundSize = j;
				foundNumber = it->second;
			}
		}
		// 数値を記録する
		i += foundSize;
		result.push_back(foundNumber);
	}
	return result;
}

// 数値の列をカンマ区切りの文字列に変換する
std::string numbersToCSVString(const std::vector<int>& arr) {
	std::stringstream ss;
	bool isFirst = true;
	for (std::vector<int>::const_iterator it = arr.begin(); it != arr.end(); it++) {
		if (!isFirst) ss << ',';
		ss << *it;
		isFirst = false;
	}
	return ss.str();
}

// カンマ区切りの文字列を数値の列に変換する (手抜き)
std::vector<int> CSVStringToNumbers(const std::string& str) {
	bool parsing = false;
	std::vector<int> result;
	bool negative = false;
	int currentValue = 0;
	for (std::string::const_iterator it = str.begin(); it != str.end(); it++) {
		if (*it == ',') {
			result.push_back(negative ? -currentValue : currentValue);
			currentValue = 0;
			parsing = false;
			negative = false;
		} else if (isdigit(static_cast<unsigned char>(*it))) {
			currentValue = currentValue * 10 + (*it - '0');
			parsing = true;
		} else if (*it == '-') {
			negative = true;
		}
	}
	if (parsing) result.push_back(currentValue);
	return result;
}

// 数値の列を文字列に変換する
std::string numbersToString(const std::vector<int>& arr) {
	std::stringstream ss;
	for (std::vector<int>::const_iterator it = arr.begin(); it != arr.end(); it++) {
		if (0 <= *it && static_cast<unsigned int>(*it) < characterTable.size()) {
			ss << characterTable[*it];
		} else {
			ss << '?';
		}
	}
	return ss.str();
}

void test(const std::string& str) {
	std::cout << "input string: " << str << '\n';
	std::vector<int> arr = stringToNumbers(str);
	std::string csvString = numbersToCSVString(arr);
	std::cout << "converted to numbers: " << csvString << '\n';
	std::vector<int> decodedArr = CSVStringToNumbers(csvString);
	std::string decodedString = numbersToString(decodedArr);
	std::cout << "decoded string: " << decodedString << '\n';
}

int main(void) {
	test("あSUMIKANA");
	test("HELLO,WORLD"); // コンマは変換対象外なので?になる
	return 0;
}
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)


返信

“C言語何でも質問掲示板” へ戻る