小练习:解密文件

  这学期在当程序设计基础实验的助教,也当是让自己复习一下C++编程吧,正好随着这些题将一些基本的知识点复习一下。第二次课程做的是下面这道题:

题目

  文本文件“ciphertext.txt”:
  wN(+2!Iw)6.-0)Bw.)2!$%#.w)6w3wN(+2!’w(%%06.0)wv
  该文件为加密文件,是原文通过下面的步骤生成:
  原文为一行英文句子,用字符数组存放;
  加密步骤1:用一个水仙花数-原英文中的每个字符,构成新的字符数组;
  加密步骤2:新数组中的相邻两个字符交换位置,即:下标[0]-[1]交换,下标[2]-[3]交换,以此类推。
  加密步骤3:将加密后的字符数组输出到文件“ciphertext.txt”
  实验输出要求:将ciphertext.txt的解密结果输出到屏幕上。
  提示:水仙花数是指一个 n 位数 ( n≥3 ),它的每个位上的数字的 n 次幂之和等于它本身,本题中的水仙花数是3位整数。

分析与思路

  这道题没什么太大的难度,就是按照加密的过程一步一步反过来解密,只是有一些基本的C++的知识可能会忘记了。

C++的解决方案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <fstream>

using namespace std;

bool isNarcissisticNumber(int num) {
int hundreds = num / 100;
int tens = num % 100 / 10;
int ones = num % 10;
if (hundreds*hundreds*hundreds + tens*tens*tens + ones*ones*ones == num)
return true;
return false;
}

int main() {
/*文件读写操作*/
ifstream in_file("d:\\ciphertext.txt", ios::in);
if (!in_file) exit(-1);

char ciphertext[100] = ""; //字符数组最好要初始化
in_file.getline(ciphertext, 100); //这里不能用in_file >> ciphertext,因为提取符会忽略空格

cout << ciphertext << endl;

for (int i = 0; i < strlen(ciphertext) / 2; i++) {
char temp = ciphertext[i * 2 + 1];
ciphertext[i * 2 + 1] = ciphertext[i * 2];
ciphertext[i * 2] = temp;
}

cout << ciphertext << endl;

for (int i = 100; i < 1000; i++) {
if (isNarcissisticNumber(i)) {
char originalText[100] = "";
for (int j = 0; j < strlen(ciphertext); j++) {
originalText[j] = i - ciphertext[j];
}
cout << originalText << endl;
}
}
return 0;
}

相关知识点总结

水仙花数

  水仙花数是指一个 n 位数 ( n≥3 ),它的每个位上的数字的 n 次幂之和等于它本身。(例如:1^3 + 5^3+ 3^3 = 153)
  三位的水仙花数共有4个:153,370,371,407;
  四位的四叶玫瑰数共有3个:1634,8208,9474;
  五位的五角星数共有3个:54748,92727,93084;
  六位的六合数只有1个:548834;

ASCII码

  ASCII码大致可以分作三部分组成。
  第一部分是:ASCII非打印控制字符,ASCII表上的数字0–31分配给了控制字符,用于控制像打印机等一些外围设备。
  第二部分是:ASCII打印字符,数字 32–126 分配给了能在键盘上找到的字符,当您查看或打印文档时就会出现,数字127代表 DELETE 命令。其中30~39代表0~9,41~90代表A~Z,97~122代表a~z。
  第三部分是:扩展ASCII打印字符,扩展的ASCII字符满足了对更多字符的需求。扩展的ASCII包含ASCII中已有的128个字符(数字0–32显示在下图中),又增加了128个字符,总共是256个。

C++文件读写操作

基于函数库的文件I/O

  从C语言标准库保留下来的输入/输出函数库中包含了对文件进行输入/输出操作的函数。

1
#include <cstdio>  //或 #include <stdio.h>

(1)打开文件

  使用函数fopen来实现:

1
FILE *fopen(const char *filename, const char *mode);  //打开文件

  filename是文件名,mode表示打开方式,有如下几种:
  ”w”——打开一个外部文件用于写操作,如果外部文件已存在,则首先把它的内容清除,否则先创建该外部文件。文件指针指向文件的头。
  ”a”——打开一个外部文件用于添加(从文件末尾)操作,如果外部文件不存在,则先创建该外部文件。文件指针指向文件的尾。
  ”r”——打开一个外部文件用于读操作,外部文件必须存在,否则打开文件失败。
  如果文件打开失败,则函数fopen返回空指针,例如:

1
2
3
4
5
FILE *fp = fopen("d:\\data\\file1.txt" , "w");
if (fp == NULL) {
cerr << "文件打开失败\n";
exit(-1);
}

(2)往文件中写入数据

(4)关闭文件

  文件操作结束后需要执行关闭操作:

1
int flcose(FILE *stream);  //关闭文件,返回0表示成功

基于类库的文件IO

  利用C++的I/O类库进行文件的输入/输出。

1
2
#include <iostream>
#include <fstream>

(1)在文件中写入数据

  首先创建一个ofstream类的对象:

1
ofstream out_file(<文件名>, <打开方式>);  //直接方式


1
2
ofstream out_file;  //间接方式
out_file.open(<文件名>, <打开方式>);

  其中<文件名>是对应文件的文件名,<打开方式>可以是:
  ·ios::out——含义与fopen的”w”相同
  ·ios:app——含义与fopen的”a”相同

1
2
3
4
5
6
7
8
9
10
11
//判断文件是否成功打开
if (!out_file) { //或out_file.fail() 或!out_file.is_open()
cerr << "文件打开失败\n";
exit(-1);
}

//文件成功打开后,可以使用插入操作符"<<"或者ofstream类的一些成员函数来往文件中写入数据。如:
out_file << x << " " << y << endl;

//关闭文件
out_file.close();

(2)在文件中读取数据

  首先创建一个ifstream类的对象:

1
ifstream in_file(<文件名>, <打开方式>);  //直接方式

  或

1
2
ifstream in_file;  //间接方式
in_file.open(<文件名>, <打开方式>);

  其中<文件名>是对应文件的文件名,<打开方式>可以是:
  ·ios::in——含义与fopen的”r”相同

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//读取数据前需要对文件打开是否成功进行判断
if (!in_file) { //或in_file.fail() 或!in_file.is_open()
cerr << "文件打开失败\n";
exit(-1);
}

//文件打开成功后,可以使用抽取操作符">>"或ifstream类的一些成员函数来进行读操作
in_file >> x >> y;

//在文件中读取数据时常常需要判断文件是否已结束(文件中的数据被读完),判断文件是否结束可以调用ios类的成员函数eof来实现
int ios::eof(); //该函数返回非0表示上一次读操作遇到文件末尾

//关闭文件
in_file.close();

(3)既能读入又能输出数据

  首先创建一个fstream类的对象(fstream是类iostream的派生类),文件的打开方式如下:
  ·ios::in|ios::out——可以在文件任意位置写
  ·ios::in|ios::app——只能在文件末尾写