一:题目
(一)基础知识补充(RAID和奇偶校验)
磁盘管理—磁盘阵列(RAID)实例详解(本题目常用RAID 5技术实现)
奇偶校验(同行数据中同位上的1的个数,偶校验时:1的个数为偶数则校验结果为0,否则为1,奇校验时相反:1的个数为偶数则校验结果为1,否则为0)
(二)题目详解
RAID技术用多个磁盘保存数据。每份数据在不止一个磁盘上保存,因此在某个磁盘损 坏时能通过其他磁盘恢复数据。本题讨论其中一种RAID技术。数据被划分成大小 为s(1≤s≤64)比特的数据块保存在d(2≤d≤6)个磁盘上,如图所示,每d-1个数据块都 有一个校验块,使得每d个数据块的异或结果为全0(偶校验)或者全1(奇校验)。
(三)案例解析
例如,d=5,s=2,偶校验,数据6C7A79EDFC(二进制01101100 01111010 01111001 11101101 11111100)的保存方式如图所示
其中加粗块是校验块。输入d、s、b、校验的种类(E表示偶校验,O表示奇校验)以及b(1≤b≤100)个数据块(其中“x”表示损坏的数据),
你的任务是恢复并输出完整的数据。如果校验错或者由于损坏数据过多无法恢复,应报告磁盘非法。
(四)样例输入
注意:
其中样例输入中:第一组数据网站和书籍有所出入。自己检查后发现书上是可以的。所以将原来的 0001011111 0110111011 1011011111 1110101100 0010010111 替换为: 0001101100 0110111010 0111011001 1110111101 1111110011
样例输入:
5 2 5 //第一个参数是磁盘块(将一个数据块拆分为多个分别存放在各个磁盘块中)--列数 第二个参数是每个磁盘块存放的数据位数 第三个参数是数据块数(将每一块数据拆分为多个分别存放在各个磁盘中)--行数 E //E是偶校验 O是奇校验 0001101100 0110111010 0111011001 1110111101 1111110011 3 2 5 E 0001111111 0111111011 xx11011111 3 5 1 O 11111 11xxx x1111 0 //0代表输入结束
注意点:
1、校验块是不加入十六进制运算的 2、校验块的顺序是第一行的第一块,第二行的第二个,到了某行最后一个时,下一行就有从第一个开始算做校验块 ----- 当前行数%列数 3、十六进制转换是 一个十六进制数字需要四个二进制数字,所以每四位二进制就是一位十六进制 4、校验进行是一行中每个块的相同位进行校验 5、奇校验就是每个数互相异或下来是1,偶校验就是0 6、磁盘不合理有三种可能性:一是已知的位校验不符合,二是未知位有多位,无法判断其内容,三是校验位中含有x
数据实际存放样式:
5 2 5 E 0001101100 00 01 10 11 00 0110111010 01 10 11 10 10 0111011001 -----> 01 11 01 10 01 1110111101 11 10 11 11 01 1111110011 11 11 11 00 11
3 2 5 E 00 01 11 0001111111 11 11 01 0111111011 ----> 11 11 10 xx11011111 11 xx 11 01 11 11
3 5 1 O 11111 11xxx -----> 11111 11xxx x1111 x1111
(五)样例输出
Disk set 1 is valid, contents are: 6C7A79EDFC Disk set 2 is invalid. Disk set 3 is valid, contents are: FFC
二:代码实现
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string> #define ROW 100 //最多100条 100行 #define COL 6 //最多6个磁盘 6列 #define BITS 64 //每个磁盘块最多64位 int col, row, bits; char DISK[ROW][COL][BITS], ct; //获取基本数据:磁盘数据和校验类型 char Ddata[COL][BITS], CkCode[BITS]; //获取每行数据和校验值 //-1无效 0结束 1正确
获取磁盘数据,并进行检测和纠错
int getDiskData() //改进:在这里检错 { int flag = 1; int x_bits[BITS], xNum = 0, OneNum[BITS]; //记录x位置x_bits中内容是对于列数值和x的个数 scanf("%d", &col); if (col == 0) return 0; scanf("%d %d", &bits, &row); getchar(); scanf("%c", &ct); getchar(); //获取磁盘数据 for (int i = 0; i < row;i++) { memset(x_bits, -1, sizeof(x_bits)); memset(OneNum, 0, sizeof(OneNum)); for (int j = 0; j < col; j++) { for (int k = 0; k < bits; k++) { scanf("%c", &DISK[i][j][k]); if (DISK[i][j][k] == '\n') { k--; continue; } if (i%col == j) //不允许校验位为x { if (DISK[i][j][k] == 'x') flag = -1; } if (DISK[i][j][k] == 'x') { if (x_bits[k] != -1) //不允许在同一位出现多个x flag = -1; x_bits[k] = j; //最多记录不同位上的x位置 } if (i%col !=j && DISK[i][j][k] == '1') OneNum[k]++; } } //复制校验位 memcpy(CkCode, DISK[i][i%col], BITS); for (int k = 0; k < bits;k++) //将对应位置上的数据进行纠错 { if (x_bits[k] != -1) //该行有x数据,需要纠错(纠错就避免了检错) { if (ct == 'E') //偶校验 { if ((CkCode[k] == '0'&&OneNum[k] % 2 == 1) || (CkCode[k] == '1' && OneNum[k] % 2 == 0)) DISK[i][x_bits[k]][k] = '1'; else DISK[i][x_bits[k]][k] = '0'; } else //奇校验 { if ((CkCode[k] == '1'&&OneNum[k] % 2 == 1) || (CkCode[k] == '0' && OneNum[k] % 2 == 0)) DISK[i][x_bits[k]][k] = '1'; else DISK[i][x_bits[k]][k] = '0'; } } else //该行没有x数据,直接检错 { if (ct == 'E' && ((CkCode[k] == '1'&&OneNum[k] % 2 == 0) || (CkCode[k] == '0' && OneNum[k] % 2 == 1))) flag = -1; if (ct == 'O' && ((CkCode[k] == '1'&&OneNum[k] % 2 == 1) || (CkCode[k] == '0' && OneNum[k] % 2 == 0))) flag = -1; } } } getchar(); return flag; }
将字符串二进制转为16进制输出
//转换16进制输出 void printValidData() { int n = 0; int m; //获取每一行数据,进行输出 for (int i = 0; i < row; i++) { m = 0; for (int j = 0; j < col; j++) { if (i%col==j) continue; //跳过校验位 for (int k = 0; k < bits; k++) { if (DISK[i][j][k] == '1') n |= 0x01; else n |= 0x00; m++; if (m == 4) { printf("%X", n); n = 0, m = 0; } n <<= 1; } } //获取完一行 if (m > 0 && m != 4) { m++; while (m != 4) { n |= 0x00,n <<= 1; m++; } printf("%X", n); } } printf("\n"); }
主函数
void main() { int c = 1, flag; FILE* fp = freopen("data7.in", "r", stdin); freopen("data7.out", "w", stdout); while (!feof(fp)) { flag = getDiskData(); if (flag == 0) break; if (flag == -1) printf("Disk set %d is invalid.\n",c++); else { printf("Disk set %d is valid, contends are: ",c++); printValidData(); } } freopen("CON", "r", stdin); freopen("CON", "w", stdout); }