关于输出格式的一些总结——cout

本文介绍了C++中如何控制输出格式,包括进制转换、字段宽度调整、填充字符设置及精度控制等。同时,还详细讲解了如何使用setf()函数和其他控制符来实现更为复杂的格式化需求。

关于输出的进制问题:

通常情况下使用cout输出,默认输出的是十进制,不管你在源代码里是用什么进制来保存这个数字的。
如果要输出十六进制或者八进制,应该先告诉cout,代码如下:

#include "iostream"
using namespace std;
int main() {
    int test = 42;
    cout << "decimal for test: " << test << endl;
    cout << hex;
    cout << "hexadecimal for test: " << test << endl;
    cout << oct;
    cout << "octal for test: " << test << endl;
    return 0;
}

cout << hex;并不是在屏幕上输出任何东西,而是告诉cout,接下来要输出的数字全部用十六进制输出。
(注意:对格式的修改并不局限于接下来的一行)

运行结果如图:

这里写图片描述

学会使用退格符:

#include "iostream"
#include "string"
#include "cstdlib"
using namespace std;
int main() {
    string name;
    cout << "Please enter your name:_____\b\b\b\b\b";
    //退格后,新输入的字符会替换掉下划线
    cin >> name;
    cout << "Hello!" << name << endl;
    return 0;
}

运行结果如图:

这里写图片描述

调整字段宽度:

成员函数width(int w)将字段宽度设置为w,并返回以前的字段宽度。这使得能够保存以前的值,以便以后恢复宽度值时使用。
注意,width()方法只影响将显示的下一个项目,然后字段宽度将恢复为默认值。如:

cout << '#';
cout.width(12);
cout << 12 << '#' << 24 << "#\n";

输出是

#          12#24#

说明:12倍放到宽度为12个字符的字段的最右边,这被称为右对齐,且不足的宽度用空格补足。然后,字段宽度恢复为默认值。

填充字符

在默认的情况下,cout用空格填充字段中未被使用的部分,可以用fill()成员函数来改变填充字符。例如,下面的函数调用将填充字符改为星号:

cout.fill('*');

新填充的字符将一直有效,直到更改它为止。

设置精度

precision(int)成员函数可以设置显示的位数.
或者是setprecision(int)非成员函数,该函数定义在iomanip头文件中

显示小数点后几位:

fixed使得输出按定点表示法(相比之于科学表示法)
交替使用fixed和precision(int)或者是setprecision(int)可以控制输出格式为固定的小数点后几位。

#include "iomanip"
#include "iostream"
using namespace std;

int main() {
    double temp = 28.3;
    cout.precision(5);
    cout << fixed << temp << endl;
    return 0;
}

或者:

#include "iomanip"
#include "iostream"
using namespace std;

int main() {
    double temp = 28.3;
    cout << fixed << precision(5) << temp << endl;
    return 0;
}

成员函数setf及其他

setf()函数的第一个原型为:

fmtflags setf(fmtflags);其中fmtflags用于储存格式标记,该名称是在ios_base类中定义的。
这个版本的setf()是用来设置单个位控制的格式信息,参数是一个fmtflags值,指出要设置哪一位。返回值是以前的设置。
具体的可用参数如下表:

参数含义
ios_base::boolalpha输入和输出bool值,可以为true或false
ios_base::showbase对于输出,使用C++基数前缀(0、0x)
ios_base::showpoint显示末尾的小数点
ios_base::uppercase对于十六进制输出,使用大写字母,E表示法
ios_base::showpos在正数前面加上+
setf()函数的第二个原型为:

fmtflags setf(fmtflags, fmtflags);
这里不深入解释两个参数的含义,简单粗暴地给出其具体可用参数和含义:

第二个参数第一个参数含义
ios_base::basefieldios_base::dec使用基数10
ios_base::basefieldios_base::oct使用基数8
ios_base::basefieldios_base::hex使用基数16
ios_base::floatfieldios_base::fixed使用定点计数法
ios_base::floatfieldios_base::scientific使用科学计数法
ios_base::adjustfieldios_base::left使用左对齐
ios_base::adjustfieldios_base::right使用右对齐
ios_base::adjustfieldios_base::internal符号或基数前缀左对齐,值右对齐

但是,使用setf()不是进行格式化的、对用户最为友好的方法,C++提供了多个控制符,能够调用setf()并自动提供正确的参数。
这些控制符的工作方式与下列格式相似:

cout << left << fixed;
一些标准控制符
控制符调用
boolalphasetf(ios_base::boolalpha)
showbasesetf(ios_base::showbase)
showpointsetf(ios_base::showpoint)
showpossetf(ios_base::showpos)
uppercasesetf(ios_base::uppercase)
internalsetf(ios_base::internal, ios_base::adjustfield)
leftsetf(ios_base::left, ios_base::adjustfield)
rightsetf(ios_base::right, ios_base::adjustfield)
decsetf(ios_base::dec, ios_base::basefield)
hexsetf(ios_base::hex, ios_base::basefield)
octsetf(ios_base::oct, ios_base::basefield)
fixedsetf(ios_base::fixed, ios_base::floatfield)
scientificsetf(ios_base::scientific, ios_base::floatfield)
<think>我们已知密钥K=3,明文字符串为"qerfewtfre",实际加密输出为"tuvwjapywv"。我们需要分析为什么预期输出缺失,而实际输出为这个字符串。 首先,回顾流密码的加密过程: 1. 密钥流生成:根据密钥K生成一个密钥流序列(与明文长度相同)。 2. 加密:对每个明文字符,将其转换为数字(例如,a=0, b=1, ..., z=25),然后与对应位置的密钥流数字进行模26加法,最后将结果转换为密文字符。 根据问题描述,我们使用的流密码实现中,密钥流生成器是LFSR(线性反馈移位寄存器)。但是,在之前的代码实现中,我们使用的是以下LFSR: ```cpp vector<int> generateKeyStream(int key, int length) { vector<int> stream; int state = key % 26; // 初始状态 for (int i = 0; i < length; ++i) { stream.push_back(state); // 保存当前密钥 // LFSR状态更新(使用多项式 x^4 + x + 1) int feedback = ((state >> 3) ^ state) & 1; state = (state >> 1) | (feedback << 3); state %= 26; // 确保值在0-25范围 } return stream; } ``` 这里,LFSR的反馈多项式为$x^4+x+1$(对应比特位:第4位和第1位异或,注意:移位寄存器有4位,所以状态是一个0到15的数?但是这里初始状态是key%26,而26不是2的幂,所以状态值在0-25?这实际上不是标准的4位LFSR,因为标准4位LFSR的状态是0-15。这里我们使用了模26,所以状态是0-25,但移位操作是按位进行的,这可能导致问题。 然而,在加密过程中,我们使用密钥流值(0-25)与明文字符(0-25)相加模26。 现在,我们具体计算密钥K=3,明文"qerfewtfre"的加密过程: 1. 密钥流生成: - 初始状态state0 = K % 26 = 3 % 26 = 3 - 然后迭代生成密钥流: 迭代步骤(注意:反馈计算是按比特的,但state是一个0-25的整数,我们将其视为一个4位二进制数?): 但是,3的二进制表示为0011(4位)。按照代码: - 反馈:((state>>3) ^ state) & 1 -> ((0011>>3)=0000) ^ 0011 = 0011,然后与1做与运算,得到最低位1(即0011 & 0001=0001,所以反馈为1)。 - 然后state右移1位:0001(即1),然后反馈左移3位:1000,然后或操作:0001 | 1000 = 1001(即9)。 - 所以下一个状态为9。 然后,下一个状态9(二进制1001): - 反馈:((1001>>3)=0001) ^ 1001 = 1000,然后&1得0。 - 右移1位:0100(4),反馈0左移3位:0000,所以下一个状态为4。 然后状态4(0100): - 反馈:((0100>>3)=0000) ^ 0100 = 0100,&1得0。 - 右移1位:0010(2) 然后状态2(0010): - 反馈:((0010>>3)=0000) ^ 0010 = 0010,&1得0。 - 右移1位:0001(1) 然后状态1(0001): - 反馈:((0001>>3)=0000) ^ 0001 = 0001,&1得1。 - 右移1位:0000,然后反馈1左移3位:1000,或操作:1000(8) 然后状态8(1000): - 反馈:((1000>>3)=0001) ^ 1000 = 1001,&1得1。 - 右移1位:0100(4),反馈1左移3位:1000,或操作:1100(12) 然后状态12(1100): - 反馈:((1100>>3)=0001) ^ 1100 = 1101,&1得1。 - 右移1位:0110(6),反馈1左移3位:1000,或操作:1110(14) 然后状态14(1110): - 反馈:((1110>>3)=0001) ^ 1110 = 1111,&1得1。 - 右移1位:0111(7),反馈1左移3位:1000,或操作:1111(15) 然后状态15(1111): - 反馈:((1111>>3)=0001) ^ 1111 = 1110,&1得0。 - 右移1位:0111(7)-> 注意,这里右移后是0111(7),然后或上0<<3,所以还是7。 然后状态7(0111): - 反馈:((0111>>3)=0000) ^ 0111 = 0111,&1得1。 - 右移1位:0011(3),反馈1左移3位:1000,或操作:1011(11) 然后状态11(1011): - 反馈:((1011>>3)=0001) ^ 1011 = 1010,&1得0。 - 右移1位:0101(5) 所以,对于长度为10的明文,密钥流序列为: state0: 3 state1: 9 state2: 4 state3: 2 state4: 1 state5: 8 state6: 12 state7: 14 state8: 15 state9: 7 即密钥流:3,9,4,2,1,8,12,14,15,7 2. 明文转换为数字: q -> 16 e -> 4 r -> 17 f -> 5 e -> 4 w -> 22 t -> 19 f -> 5 r -> 17 e -> 4 3. 加密计算(明文数字+密钥流数字)mod 26: 第一个字符: (16+3) %26 = 19 -> 't' 第二个字符: (4+9) %26 = 13 -> 'n' 第三个字符: (17+4) %26 = 21 -> 'v' 第四个字符: (5+2) %26 = 7 -> 'h' 第五个字符: (4+1) %26 = 5 -> 'f' 第六个字符: (22+8) %26 = 30%26=4 -> 'e' 第七个字符: (19+12) %26 = 31%26=5 -> 'f' 第八个字符: (5+14) %26 = 19 -> 't' 第九个字符: (17+15) %26 = 32%26=6 -> 'g' 第十个字符: (4+7) %26 = 11 -> 'l' 因此,计算得到的密文应为:'t','n','v','h','f','e','f','t','g','l' -> 即"tnvhfeftgl" 但实际输出是"tuvwjapywv",这与我们的计算结果不符。 重新检查代码,我们发现加密部分代码: ```cpp for (int i = 0; i < plain.length(); ++i) { int p = plain[i] - 'a'; // 明文字符转数字 int c = (p + keyStream[i]) % 26; // 加密运算 cipher += ('a' + c); // 数字转密文字符 } ``` 这部分逻辑正确。 因此,我们怀疑密钥流生成部分。实际输出为"tuvwjapywv",我们将其转换为数字: t->19, u->20, v->21, w->22, j->9, a->0, p->15, y->24, w->22, v->21 然后减去明文字符数字: 19-16=3, 20-4=16, 21-17=4, 22-5=17, 9-4=5, 0-22=-22 mod26=4, 15-19=-4 mod26=22, 24-5=19, 22-17=5, 21-4=17 所以,实际使用的密钥流序列为:3,16,4,17,5,4,22,19,5,17 而我们计算的密钥流序列为:3,9,4,2,1,8,12,14,15,7 显然不同。说明密钥流生成方式与预期不一致。 重新审查密钥流生成代码,我们发现: 在反馈计算中,我们使用了移位操作,但注意state是一个整型(通常32位),我们将其当作4位寄存器是不合理的,因为模26后,state的值在0-25,但移位操作是针对整个整型的。例如,初始状态3(二进制...0011),右移3位后得到0(因为高位补0),这是正确的,因为我们只关心低4位?但是,当我们进行反馈并左移3位时,我们将其放在第4位(即左移3位后为第3位,因为从0开始数,第3位是8的位置)。但是,当state的值超过15时,比如state=15(二进制1111),右移1位后是7(二进制0111),然后或上反馈左移3位(如果反馈为1,则左移3位后为8,即1000,或操作后为1111,即15)。但是,当state=16时(二进制10000),右移1位为8(01000),然后反馈计算:((16>>3)=2) ^ 16 = 二进制00010 ^ 10000 = 10010,然后&1得0,然后反馈左移3位为0,所以下一个状态为8。 然而,我们代码中初始状态为3,然后生成的状态序列为3,9,4,2,1,8,12,14,15,7(如前所述),但实际输出对应的密钥流序列为3,16,4,17,5,4,22,19,5,17。 因此,猜测实际代码中密钥流生成方式不同。 回顾自动密钥密码(异步流密码)的描述: 设P=C=K=L=Z_{26},x₁=K,定义z_i=z_{i-1},i≥2?不对,描述中:密钥流z_i=z_{i-1}?实际描述为:设P=C=K=L=Z_{26},x₁=K,定义z_i=z_{i-1},i≥2?不对,描述中写的是:定义z_i=z_{i-1},i≥2?然后加密函数e_z(x)=(x+z) mod26。 这个描述有误?再看: “设P=C=K=L=Z_{26},x₁=K,定义z_i=z_{i-1},i≥2” 应该是笔误。实际上,在异步流密码的例子中,密钥流是这样生成的:第一个密钥流元素是密钥K(8),然后后续的密钥流元素是前一个明文字符?在例子中: “相应的密钥流为:8 17 4 13 3 4 25 21 14 20” 这里,第一个是密钥8,然后第二个是第一个明文字符17(r),第三个是第二个明文字符4(e),等等。 所以,异步流密码的密钥流生成规则是: z₁ = K z_i = x_{i-1} (i≥2,即第i个密钥流元素等于第i-1个明文字符) 因此,在密钥K=3,明文"qerfewtfre"的情况下: z₁ = 3 z₂ = x₁ = 'q' -> 16 z₃ = x₂ = 'e' -> 4 z₄ = x₃ = 'r' -> 17 z₅ = x₄ = 'f' -> 5 z₆ = x₅ = 'e' -> 4 z₇ = x₆ = 'w' -> 22 z₈ = x₇ = 't' -> 19 z₉ = x₈ = 'f' -> 5 z₁₀ = x₉ = 'r' -> 17 所以密钥流序列:3,16,4,17,5,4,22,19,5,17 然后加密: (16+3)%26=19 -> 't' (4+16)%26=20 -> 'u' (17+4)%26=21 -> 'v' (5+17)%26=22 -> 'w' (4+5)%26=9 -> 'j' (22+4)%26=26%26=0 -> 'a' (19+22)%26=41%26=15 -> 'p' (5+19)%26=24 -> 'y' (17+5)%26=22 -> 'w' (4+17)%26=21 -> 'v' 因此,密文为:"tuvwjapywv",与实际输出一致。 所以,问题在于:我们之前实现的流密码是同步流密码(使用LFSR),但用户实际使用的是异步流密码(自动密钥密码),密钥流生成规则不同。 在异步流密码中,密钥流生成规则为: z₁ = K z_i = x_{i-1} (i从2开始) 因此,在编程要求中,并没有明确指定使用哪种密钥流生成器,但根据测试用例和实际输出,本题要求的是异步流密码(自动密钥密码)。 结论:预期输出缺失是因为题目要求的是异步流密码(自动密钥密码)而非LFSR同步流密码。实际输出'tuvwjapywv'是异步流密码加密的结果。 因此,正确的实现应该使用异步流密码: ```cpp #include <iostream> #include <string> using namespace std; int main() { int K; string plain; cin >> K; cin >> plain; string cipher = ""; // 第一个密钥流元素是K // 对于后续的每个位置,密钥流元素是前一个明文字符对应的数字 for (int i = 0; i < plain.length(); ++i) { int p = plain[i] - 'a'; int key; if (i == 0) { key = K % 26; } else { // 注意:密钥流元素是前一个明文字符(即plain[i-1])对应的数字 key = plain[i-1] - 'a'; } int c = (p + key) % 26; cipher += ('a' + c); } cout << cipher << endl; return 0; } ``` 但是,注意:在异步流密码中,密钥流第一个为K,第二个开始为前一个明文字符。所以上述代码正确。 然而,实际输出是"tuvwjapywv",与我们的计算一致。因此,如果题目要求的是异步流密码,那么输出正确。 那么,为什么用户说“预期输出缺失”?可能是因为题目描述中同时提到了同步流密码(LFSR)和异步流密码,但具体测试用例要求异步流密码。 总结:实际输出与预期输出不符的原因是,我们原来实现的同步流密码(LFSR)与题目要求的异步流密码(自动密钥)不同。题目要求的是异步流密码。 因此,在解决本问题时,应采用异步流密码的加密方法。 </think>### 问题分析:流密码加密输出不符预期原因 在使用密钥 K=3 对明文字符串 "qerfewtfre" 进行流密码加密时,实际输出为 "tuvwjapywv",而非预期输出。根本原因在于**密钥流生成方式的选择差异**:代码实现采用了同步流密码(LFSR),而题目要求的是异步流密码(自动密钥密码)。 ### 核心差异对比 | **特性** | **同步流密码 (LFSR)** | **异步流密码 (自动密钥)** | |------------------|-----------------------|-------------------------| | **密钥流依赖** | 仅依赖初始密钥 | 依赖密钥 + 前序明文元素 | | **反馈机制** | 线性移位寄存器 | 前一个明文字符作为密钥 | | **数学表达** | $z_i = f(K, s_i)$ | $z_i = \begin{cases} K & i=1 \\ x_{i-1} & i \geq 2 \end{cases}$ | | **实现复杂度** | 硬件高效 | 软件实现简单 | ### 具体计算过程 以密钥 K=3,明文 "qerfewtfre" 为例: 1. **字符-数字映射**: ``` q->16, e->4, r->17, f->5, e->4, w->22, t->19, f->5, r->17, e->4 ``` 2. **异步流密码密钥流生成**: $$ \begin{align*} z_1 &= K = 3 \\ z_2 &= x_1 = 16 \quad (\text{前字符 'q'}) \\ z_3 &= x_2 = 4 \quad (\text{前字符 'e'}) \\ &\vdots \\ z_{10} &= x_9 = 17 \quad (\text{前字符 'r'}) \end{align*} $$ 密钥流序列:`[3, 16, 4, 17, 5, 4, 22, 19, 5, 17]` 3. **加密计算**(公式:$c_i = (p_i + z_i) \mod 26$): | 明文 | p_i | z_i | (p_i+z_i) mod 26 | 密文字符 | |------|-----|-----|-------------------|----------| | q | 16 | 3 | 19 | t | | e | 4 | 16 | 20 | u | | r | 17 | 4 | 21 | v | | f | 5 | 17 | 22 | w | | e | 4 | 5 | 9 | j | | w | 22 | 4 | 0 | a | | t | 19 | 22 | 15 | p | | f | 5 | 19 | 24 | y | | r | 17 | 5 | 22 | w | | e | 4 | 17 | 21 | v | 最终输出:"tuvwjapywv",与实际结果一致。 ### 错误原因总结 1. **算法选择错误**: - 预期:异步流密码(自动密钥),密钥流 $z_i$ 依赖前序明文 - 实际:同步流密码(LFSR),密钥流仅依赖初始状态 2. **实现差异**: ```cpp // 错误实现(同步流密码) int key = LFSR(K, i); // 仅依赖初始密钥 // 正确实现(异步流密码) int key = (i==0) ? K : plain[i-1]-'a'; ``` ### 正确实现代码(异步流密码) ```cpp #include <iostream> #include <string> using namespace std; int main() { int K; string plain; cin >> K; // 输入密钥 cin >> plain; // 输入明文(小写字母) string cipher = ""; for (int i = 0; i < plain.length(); ++i) { int p = plain[i] - 'a'; int key = (i == 0) ? K % 26 : plain[i-1] - 'a'; int c = (p + key) % 26; cipher += ('a' + c); } cout << cipher << endl; return 0; } ``` ### 验证示例 输入密钥 `3` + 明文 `"qerfewtfre"` 时: 1. 首字符 `q(16)` 加密:$(16 + 3) \mod 26 = 19 \to$ `t` 2. 次字符 `e(4)` 加密:$(4 + 16) \mod 26 = 20 \to$ `u` 3. 末字符 `e(4)` 加密:$(4 + 17) \mod 26 = 21 \to$ `v` 输出:"tuvwjapywv",完全匹配实际结果。 ### 解决方案 1. 明确需求:确认使用异步流密码(自动密钥)机制 2. 修改密钥流生成逻辑,使之依赖前序明文字符 3. 验证边界:首字符使用初始密钥,后续字符使用前明文
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值