【注意】
先将十六进制数转换成某进制数,再由某进制数转换成八进制。
这道题首先应该想到的是先将十六进制数转化成二进制数,然后再转化成八进制数。而且都得用字符类型,因为转化成二进制后肯定都超出int、long、long long 的数据范围,所以整型类型绝对是不能用的。
使用十进制作为中转
笔者一开始看到这个题目想的就是先将十六进制转为十进制,再将十进制转为八进制。可能是脑袋瓜子不灵活,也可能是因为学过C语言做过类型的题目,像什么十进制转八进制,八进制转十进制之类的。
代码如下:
#include<stdio.h>
#include<string.h>
int main()
{
int n,t,num_ten,num_eight;
char str[10][100000];
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%s",str[i]);
for(int i=0;i<n;i++)
{
int len = strlen(str[i])-1;
t=1;
num_ten=0;
while(len>=0) //将16进制转为10进制
{
if(str[i][len]>='0'&&str[i][len]<='9')
num_ten+=(str[i][len]-'0')*t;
else
num_ten+=(str[i][len]-'A'+10)*t;
t=t*16;
len--;
}
t=1;
num_eight=0;
while(num_ten) //将10进制转为8进制
{
num_eight+=num_ten%8*t;
t=t*10;
num_ten/=8;
}
printf("%d\n",num_eight);
}
return 0;
}
笔者很单纯的将测试样例带入程序,结果显示是正确的,但是,将代码提交却是说编译结果不正确😭😭😭。后面仔细分析题目,每个十六进制数长度不超过100000。这肯定是不能用十进制整型类型来做的。因为肯定会超过int,long,long long的范围。C语言int的取值范围在32/64位系统中都是32位,范围为-2147483648到+2147483647,无符号情况下表示为0到4294967295。(PS:之前学习C语言以为这个范围没什么用,但现在深刻体会到该范围的限制性)
接着看了大佬们的作品,思路发生转变,内心不禁感叹。
链接: https://blog.csdn.net/try_fei_ge/article/details/53239501.
使用二进制作为中转
本来是想将大佬的第二种方法细化的分析下的,可是我将他的第一个没改进前的代码提交了下,发现结果显示正确,且提交没有超时,结果如下图,就干脆将这个一起分析下算了。
先上代码,我将大佬代码的格式规范化了一下其实很简单,只要右击鼠标,然后点击format this file即可 ,还加了一点点注释,代码虽长,但很好理解。
先简单介绍下代码的思路:在进制转换中,可以直接将1位十六进制的数转换为4位二进制的数,1位八进制的数转换位3位二进制的数,因为此题一个十六进制的数最大有100000位,那么就需要400000位二进制作为中转站,为了节约运行时间和内存,采用同时将3位十六进制转为12位二进制,接着将这十二位二进制转为4位八进制的思路解决问题。
在看代码前先介绍一下各个变量的含义:
- n:代表输入十六进制数的个数
- s:二维数组,用来存放十六进制数
- a:用来表示某一个十六进制数的位数距离能被3整除还差几位
- ok:用于判断12位二进制转为八进制时,最前面的四位是否为0
- z:一维数组,用来暂存某个十六进制的某三位转为的12位二进制数
- i,j,k,cur只是作为循环的中间变量
下面看代码应该会很好理解了,请大家一定不要心躁,心平气和的来看代码,一遍不懂就再看多看几遍,坚持下去总归会弄明白的,加油💪。
#include<stdio.h>
#include<string.h>
int main()
{
int n,i,j,k,a=0,cur,ok;
char s[10][100000],z[12];
scanf("%d",&n);
for(j=0; j<n; j++)
scanf("%s",s[j]);
for(j=0; j<n; j++)
{
ok=1;
if(strlen(s[j])%3!=0&&a==0&&(a=(3-(strlen(s[j])%3)))!=0) //是判断当前的这个十六进制的位数离3的整数还差几位
{
for(k=0; k<a*4; k++) //将三位16进制的转二进制,不足12位的,在前面补全0
z[k]='0';
}
for(i=0; i<strlen(s[j]); i++) //将每位16进制位数转为四位二进制
{
switch(s[j][i])
{
case '0':
z[k]='0';
z[k+1]='0';
z[k+2]='0';
z[k+3]='0';
break;
case '1':
z[k]='0';
z[k+1]='0';
z[k+2]='0';
z[k+3]='1';
break;
case '2':
z[k]='0';
z[k+1]='0';
z[k+2]='1';
z[k+3]='0';
break;
case '3':
z[k]='0';
z[k+1]='0';
z[k+2]='1';
z[k+3]='1';
break;
case '4':
z[k]='0';
z[k+1]='1';
z[k+2]='0';
z[k+3]='0';
break;
case '5':
z[k]='0';
z[k+1]='1';
z[k+2]='0';
z[k+3]='1';
break;
case '6':
z[k]='0';
z[k+1]='1';
z[k+2]='1';
z[k+3]='0';
break;
case '7':
z[k]='0';
z[k+1]='1';
z[k+2]='1';
z[k+3]='1';
break;
case '8':
z[k]='1';
z[k+1]='0';
z[k+2]='0';
z[k+3]='0';
break;
case '9':
z[k]='1';
z[k+1]='0';
z[k+2]='0';
z[k+3]='1';
break;
case 'A':
z[k]='1';
z[k+1]='0';
z[k+2]='1';
z[k+3]='0';
break;
case 'B':
z[k]='1';
z[k+1]='0';
z[k+2]='1';
z[k+3]='1';
break;
case 'C':
z[k]='1';
z[k+1]='1';
z[k+2]='0';
z[k+3]='0';
break;
case 'D':
z[k]='1';
z[k+1]='1';
z[k+2]='0';
z[k+3]='1';
break;
case 'E':
z[k]='1';
z[k+1]='1';
z[k+2]='1';
z[k+3]='0';
break;
case 'F':
z[k]='1';
z[k+1]='1';
z[k+2]='1';
z[k+3]='1';
break;
}
k+=4;
if(k==12) //当三位十六进制都转为4为二进制后,开始将12位二进制变为四位八进制
{
for(cur=0; cur<12; cur+=3) //四位体现在cur+=3
if(z[cur]=='0'&&z[cur+1]=='0'&&z[cur+2]=='0')if(ok);
else putchar('0');
else if(z[cur]=='0'&&z[cur+1]=='0'&&z[cur+2]=='1')
{
putchar('1');
ok=0;
}
else if(z[cur]=='0'&&z[cur+1]=='1'&&z[cur+2]=='0')
{
putchar('2');
ok=0;
}
else if(z[cur]=='0'&&z[cur+1]=='1'&&z[cur+2]=='1')
{
putchar('3');
ok=0;
}
else if(z[cur]=='1'&&z[cur+1]=='0'&&z[cur+2]=='0')
{
putchar('4');
ok=0;
}
else if(z[cur]=='1'&&z[cur+1]=='0'&&z[cur+2]=='1')
{
putchar('5');
ok=0;
}
else if(z[cur]=='1'&&z[cur+1]=='1'&&z[cur+2]=='0')
{
putchar('6');
ok=0;
}
else if(z[cur]=='1'&&z[cur+1]=='1'&&z[cur+2]=='1')
{
putchar('7');
ok=0;
}
k=0;//将k置0,开始将下面三位16进制进行转换
}
}
a=0;//假设第j个十六进制数的位数是三的倍数
putchar('\n');
}
return 0;
}
下面解释一下比较难理解代码的含义:
if(strlen(s[j])%3!=0&&a==0&&(a=(3-(strlen(s[j])%3)))!=0) //是判断当前的这个十六进制的位数离3的整数还差几位
{
for(k=0; k<a*4; k++) //将三位16进制的转二进制,不足12位的,在前面补全0
z[k]='0';
}
正如注释中写到的,这段代码的含义就是将长度未满3的倍数的十六进制补全,例如所给的案例输入:39,因为它只有2位十六进制,不能满足上面所说的3位十六进制转为12位二进制,故用此段代码来进行补全,将十六进制39转为二进制0000 0011 1001,这样,所有情况都可以用下面的代码来转为8进制了👍👍👍。
经过我测试,发现这段代码放在ok=1的后面也是可以正确运行的,因为无论怎样,这段代码都只会运行一次,即if里面的条件只会满足一次,因为补全为三只需要一次即可。读者可以自行尝试😁。
优化二进制中转
感受到代码的奇妙之后,咱们再来看看优化之后的算法,怎么来优化呢,首先细细品味咱们上面的那个代码,将1位十六进制转为4位二进制时,每位都要进行判断,然后转换,这实在是太费事了。这可以怎么解决呢?咱们来看,十六进制0对应二进制0000,1对应0001,2对应0010…F对应1111,看出来了吧,我们可以之间建立一个大小为16的字符串数组,也就是二维字符数组,直接将十六进制作为四位二进制的下标,那不就简单了,比如说d16存放0-F的二进制字符串,那么d16[0]就是0000,d16[15]就是1111。
还有就是在进行二进制转八进制的时候,我们可以将十二位二进制之间按位权展开, 然后一起输出,就不必在进行一个一个判断,这又能节约很多时间,不过节约了时间就得多好费点空间,下面会说到空间是为什么会耗费的。
同样,在看代码前来解释下各变量的含义,以上一个大同小异,这里相同的就不再介绍了。
1. ok:是十六进制数的位数否能被3整除。
2. d16:二维数组,存放0-F的四位二进制代码
3. out:一维数组,用来保存某个十六进制数转为八进制数后的结果
#include<stdio.h>
#include<string.h>
int main()
{
int n,i,j,k,a,cur,ok,m,l;
char s[10][100001],d16[16][5]= {"0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111"},out[140000]= {},z[13]= {};
scanf("%d",&n);
for(j=0; j<n; j++)
scanf("%s",s[j]);
for(j=0; j<n; j++)
{
k=0;
ok=1;
m=0; /*初始化标记数据*/
l=strlen(s[j]); /* 初始化标记数据*/
a=3-l%3; /*初始化标记数据*/
if(a==3) ok=0; /*十六进制数长度刚好为3的倍数时转二进制不需补0,ok标记其是否为3的倍数0是1不是*/
for(i=0; i<l; i++) /*逐位读取十六进制数进行转换*/
{
if(65<=s[j][i])
s[j][i]-=7;
if(ok) /*十六进制数位数不足转二进制时补0占位*/
if(a==1)
{
strcat(z,"0000");
k=k+4;
ok=0;
}
else if(a==2)
{
strcat(z,"00000000");
k=k+8;
ok=0;
}
z[k++]=d16[s[j][i]-48][0]; /*一位十六进制转四位二进制*/
z[k++]=d16[s[j][i]-48][1];
z[k++]=d16[s[j][i]-48][2];
z[k++]=d16[s[j][i]-48][3];
if(k==12) /*每转三位十六进制数将其转为四位八进制数*/
{
for(cur=0; cur<12; m++)
out[m]=((z[cur++]-48)*4+(z[cur++]-48)*2+(z[cur++]-48)*1)+48;
//z[0]='\0';
k=0; /*z[0]='\0' 初始化字符串结束符位置避免溢出*/
}
}
for(; k<3; k++) /*输出时忽略前导0*/
if(out[k]!=48) break;
for(; k<m; k++)
printf("%c",out[k]);
putchar('\n');
}
return 0;
}
该代码和前一个代码的意思是相同的,让我们来详细的一段一段代码的分析下。
l=strlen(s[j]); /* 初始化标记数据*/
a=3-l%3; /*初始化标记数据*/
if(a==3) ok=0; /*十六进制数长度刚好为3的倍数时转二进制不需补0,ok标记其是否为3的倍数0是1不是*/
这段代码的意思也是判断十六进制的位数是否是3的倍数,不是的话得补几位
- 码就是实现将十六进制数对应为四位二进制数的下标,0-9很好对应,直接减去48即可。但是A-F呢?为了将0-F一同对待,同样减去48,我们知道A对应的ASCAII是65,A的十六进制数是10,所以先将A-?=10,?=55,所以A先减去7,在减去48就是对应的十六进制数10。
if(k==12) /*每转三位十六进制数将其转为四位八进制数*/
{
for(cur=0; cur<12; m++)
out[m]=((z[cur++]-48)*4+(z[cur++]-48)*2+(z[cur++]-48)*1)+48;
//z[0]='\0';
k=0; /*z[0]='\0' 初始化字符串结束符位置避免溢出*/
}
这里和前一个程序的思路不太一样,前一个程序是把每次三位的二进制数转八进制数后直接输出来,但这里是将某个十六进制数的转为八进制后,直接数出来。
z[0]=’\0’ 初始化字符串结束符位置避免溢出*/。这句笔者也不太能理解是做什么用的,不过我将这句去掉判分系统也是判的是正确的。望知道的读者能够解答解答😊。
对比上面两个:
可以明显看出,cpu的时间是大大减小,不过内存占用稍微大了一点点,是因为加了out[140000]这个一位数组的原因。
在我看来,可以将以上两个程序结合一下,这样内存的使用会不会也大大减小了,先看看结果:
果然,时间和空间都达到了理想效果,下面贴上代码以及解析。
#include<stdio.h>
#include<string.h>
int main()
{
int n,i,j,k,a,cur,ok,m,l,flag;
char s[10][100001],d16[16][5]= {"0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111"},out[4]= {},z[13]= {};
scanf("%d",&n);
for(j=0; j<n; j++)
scanf("%s",s[j]);
for(j=0; j<n; j++)
{
k=0;
ok=1;
flag = 0;
m=0; /*初始化标记数据*/
l=strlen(s[j]); /* 初始化标记数据*/
a=3-l%3; /*初始化标记数据*/
if(a==3) ok=0; /*十六进制数长度刚好为3的倍数时转二进制不需补0,ok标记其是否为3的倍数0是1不是*/
for(i=0; i<l; i++) /*逐位读取十六进制数进行转换*/
{
if(65<=s[j][i])
s[j][i]-=7;
if(ok) /*十六进制数位数不足转二进制时补0占位*/
if(a==1)
{
strcpy(z,"0000");
k=k+4;
ok=0;
a=0;
}
else if(a==2)
{
strcpy(z,"00000000");
k=k+8;
ok=0;
}
z[k++]=d16[s[j][i]-48][0]; /*一位十六进制转四位二进制*/
z[k++]=d16[s[j][i]-48][1];
z[k++]=d16[s[j][i]-48][2];
z[k++]=d16[s[j][i]-48][3];
if(k==12) /*每转三位十六进制数将其转为四位八进制数*/
{
m=0;
for(cur=0; cur<12; m++)
out[m]=((z[cur++]-48)*4+(z[cur++]-48)*2+(z[cur++]-48)*1)+48;
cur = 0;
k=0;
if(!flag)
while(out[cur]=='0'&&cur<m)
cur++;
if(cur==m)
flag = 0;
else
flag = 1;
for(; cur<m; cur++)
printf("%c",out[cur]);
}
}
putchar('\n');
}
return 0;
}
首先,此代码和第一个未优化的代码相似,是每次三位的二进制数转八进制数后直接输出来,并且加上了判断开头是否为0的情况。其次,在改写这个代码的时候,笔者发现上面的那个疑问,即z[0]=’\0’ 初始化字符串结束符位置避免溢出*/。一开始编写改代码发现一直报错,但一直找不到原因,后来仔细分析原来是strcat的原因。这句话去掉后,把strcat改成strcpy即可。😃😃😃
最后,接下来会不断更新蓝桥杯有关题目,希望大家共同学习,共同进步,并不是为了奖状而去比赛,是为了自己能够学到真本领,这样才会越学越快乐,越学越有动力哦,加油!!!
上文采用链接:http://t.csdn.cn/keA4G
#include<stdio.h>
#include<string.h>
int main()
{
int n,i,j,k,a,cur,ok,m,l;
char s[10][100001],d16[16][5]={"0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111"},out[140000]={},z[13]={};
scanf("%d",&n);
for(j=0;j<n;j++)
scanf("%s",s[j]);
for(j=0;j<n;j++)
{
k=0;ok=1;m=0; /*初始化标记数据*/
l=strlen(s[j]); /* 初始化标记数据*/
a=3-l%3; /*初始化标记数据*/ //求出要将这个十六进制数补足为3的倍数需要补上多少个0
if(a==3) ok=0; /*十六进制数长度刚好为3的倍数时转二进制不需补0,ok标记其是否为3的倍数0是1不是*/
for(i=0;i<l;i++) /*逐位读取十六进制数进行转换*/
{
if(65<=s[j][i])
s[j][i]-=7; //这里不理解请查阅ASCII代码表
if(ok) /*十六进制数位数不足转二进制时补0占位*/
if(a==1){ //若需补一个十六进制的0,相当于补上四个二进制的0即0000,下面补两位也一样
strcat(z,"0000");k=k+4;ok=0;
}else if(a==2){
strcat(z,"00000000");k=k+8;ok=0;
}
z[k++]=d16[s[j][i]-48][0]; /*一位十六进制转四位二进制*/
z[k++]=d16[s[j][i]-48][1];
z[k++]=d16[s[j][i]-48][2];
z[k++]=d16[s[j][i]-48][3];
if(k==12) /*每转三位十六进制数将其转为四位八进制数*/
{
for(cur=0;cur<12;m++)
out[m]=((z[cur++]-48)*4+(z[cur++]-48)*2+(z[cur++]-48)*1)+48;
z[0]='\0';k=0; /*z[0]='\0'初始化字符串结束符位置避免溢出*/
}
}
for(;k<3;k++) /*输出时忽略前导0*/
if(out[k]!=48) break; //48是字符‘0’的ASCII值
for(;k<m;k++)
printf("%c",out[k]);
putchar('\n');
}
return 0;
}
上文采用链接:http://t.csdn.cn/D372y
个人整理及详细解说
/* 想法:
1.数组存储字符串string ,shiliu[10][100000]:这样子表示只能是10个数,一个数占一行 ,100000是表示数值最大值
*****数组存储 整数 和 数组存储 字符串,表达的形态不太一样。
数组存储 整数
s[3][2]表示存储 3行2列个数,也就是 存储6 个数。
数组存储 字符串
s[3][2]表示存储 3 个数,一行代表一个数(字符串),2表示字符串的长度最大值,
也就是可以存99,1,但不可以存 100及以上。所以可以直接用s[i](s[3],可以输入3个数据)来输入数据,
当满足输入的数据 <= 2 (也就是字符串长度最大值)则存储。
er[12]。因为是存储字符串,所以要用char
2.进制转换,16-> 2-> 8。1 位十六进制 = 4位二进制数,1 位八进制数 = 3位二进制数。
为了转换方便一次转换 3 位16进制数,转化为 12 位二进制数,再转化为 4位八进制数。
问题1:不够 3的倍数 。3 位16进制数(例如1234,分为001,234。
也就是 16进制的 1转化位12位二进制数)转化为 二进制 , 不满12 位二进制数则补0。
问题2:输出的八进制数也不能有前导 0。设置一个标记,当前导为0;没有语句,也就是
if(ok) ; 这样子就不会有输出
/*题目:给定n个十六进制正整数,输出它们对应的八进制数。
看题得:用数组存储字符串(也可以直接采用字符串),进制转换 .1位十六进制的数转换为4位二进制的数,采用 字符串
1位八进制的数转换位3位二进制的数
3*4=12
将3位十六进制转为12位二进制,接着将这十二位二进制转为4位八进制
由此,可以用一维数组来存储中间商二进制数 B[12]
输入格式:
输入的第一行为一个正整数n (1<=n<=10)。
接下来n行,每行一个由0~9、大写字母A~F组成的字符串,表示要转换的十六进制正整数,每个十六进制数长度不超过100000。
由此可以设计二维数组为 A[10][100000]输出格式:
输出n行,每行为输入对应的八进制正整数。【注意】
输入的十0六进制数不会有前导0,比如012A。
输出的八进制数也不能有前导0。
进制转换:16-> 2-> 8
*/
//头文件
#include<stdio.h>//标准输入输出
#include<string.h>/*字符串处理 。string .h 头文件定义了一个变量类型、一个宏和各种操作字
符数组的函数。C语言里面关于字符数组的函数定义的头文件,常用函数有
strlen(字符串长度)、strcmp、strcpy等等,更详细的可以到include文件夹里面查看该文件。
*/
int main(){
//定义(不要忘记定义!!!!!!)
char A[10][100000], z[12];//char类型! 正整数n (1<=n<=10),十六进制数长度不超过100000,12位二进制
//B:一维数组,用来暂存某个十六进制的某三位转为的12位二进制数
int n,i,ok,a=0,k,j,cur;
//输入正整数n,表示有n个元素
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%s",&A[i]); //输入字符串 ,由于输入的字符串,则字符串就会有长度,采用头文件#include<string.h>
//&A[i],此处的 scanf("%s",&A[i]); 可以为scanf("%s",A[i]);
for(i=0;i<n;i++)
{
ok=1;//ok:用于判断12位二进制转为八进制时,最前面的四位是否为 0
/*是判断当前的这个十六进制的位数离 3的整数还差几位,如果不足12位,则补0
这是因为将一次性把 3位十六进制转为 12位二进制,所以 16进制数的位数要满足 3的倍数
这样子转化后方便后方 转化为 八进制。
*/
if(strlen(A[i])%3!=0)
{
a=3-(strlen(A[i])%3); //a:用来表示某一个十六进制数的位数距离能被3整除还差几位
//以下是将三位16进制的转二进制,不足12位的,在前面补全0
for(k=0;k<a*4;k++)
z[k]='0';//B:一维数组,用来暂存某个十六进制的 某三位 转为的12位二进制数
//此处要注意:是z[k]='0';,不是z[k]=0;要单引号!!!!!是字符串
//举例:若是为(1234)16,则对应的 24位。可以把(1234)16 看成(001,234)16 ,所以为24位
}
/*举个例子 1
(72)16转化为二进制, 1位十六进制的数转换为4位二进制数。则7转化为 0111,2可以转化为0010.
所以(72)16转化为二进制为(01110010)2
由于要转化为八进制,并且1位八进制的数转换为3位二进制数。所以16进制转化的二进制数的位数要为3的倍数
取最小公倍数,则可知3*4=12.
由于字符串72为2位16进制数,经过运算可知 a=1, k=4-1=3。B[0]~B[3],补充 4个0.
(72)16= (0111,0010)2=(0000,0111,0010)2 =(000,001,110,010)2 =(162)8
*/
/*举个例子 2
(1234)16,很明显位数不是 3的倍数,a =2,k =8-1=7. B[0]~B[7],补充 8个0.
(1234)16=(0001,0010,0011,0100)2 =(0000,0000,0001,0010,0011,0100)2
而(0000,0000,0001,0010,0011,0100)2=(000,000,000,001,001,000,110,100)2=(11064)8
*/
//以下是将每位16进制位数转为四位二进制
for(j=0;j<strlen(A[i]);j++)
{
switch(A[i][j])//A[i][j]的意思是第 i个数(字符串)的第 j个字符(第 j位数)
{
case '0':
z[k]='0';
z[k+1]='0';
z[k+2]='0';
z[k+3]='0';
break;
case '1':
z[k]='0';
z[k+1]='0';
z[k+2]='0';
z[k+3]='1';
break;
case '2':
z[k]='0';
z[k+1]='0';
z[k+2]='1';
z[k+3]='0';
break;
case '3':
z[k]='0';
z[k+1]='0';
z[k+2]='1';
z[k+3]='1';
break;
case '4':
z[k]='0';
z[k+1]='1';
z[k+2]='0';
z[k+3]='0';
break;
case '5':
z[k]='0';
z[k+1]='1';
z[k+2]='0';
z[k+3]='1';
break;
case '6':
z[k]='0';
z[k+1]='1';
z[k+2]='1';
z[k+3]='0';
break;
case '7':
z[k]='0';
z[k+1]='1';
z[k+2]='1';
z[k+3]='1';
break;
case '8':
z[k]='1';
z[k+1]='0';
z[k+2]='0';
z[k+3]='0';
break;
case '9':
z[k]='1';
z[k+1]='0';
z[k+2]='0';
z[k+3]='1';
break;
case 'A':
z[k]='1';
z[k+1]='0';
z[k+2]='1';
z[k+3]='0';
break;
case 'B':
z[k]='1';
z[k+1]='0';
z[k+2]='1';
z[k+3]='1';
break;
case 'C':
z[k]='1';
z[k+1]='1';
z[k+2]='0';
z[k+3]='0';
break;
case 'D':
z[k]='1';
z[k+1]='1';
z[k+2]='0';
z[k+3]='1';
break;
case 'E':
z[k]='1';
z[k+1]='1';
z[k+2]='1';
z[k+3]='0';
break;
case 'F':
z[k]='1';
z[k+1]='1';
z[k+2]='1';
z[k+3]='1';
break;
}
k+=4;//每位16进制位数转为四位二进制 ,是故,转化一位16进制数 就要往后挪 4 个空间给下一个使用
if(k==12) //当三位十六进制都转为4为二进制后,开始将12位二进制变为四位八进制
{
for(cur=0; cur<12; cur+=3) //四位体现在cur+=3
if(z[cur]=='0'&&z[cur+1]=='0'&&z[cur+2]=='0')if(ok);
//ok:用于判断12位二进制转为八进制时,最前面的四位是否为 0
else putchar('0');//此时不是最前面四位
else if(z[cur]=='0'&&z[cur+1]=='0'&&z[cur+2]=='1')
{
putchar('1');// putchar打印输出
ok=0;
}
else if(z[cur]=='0'&&z[cur+1]=='1'&&z[cur+2]=='0')
{
putchar('2');
ok=0;
}
else if(z[cur]=='0'&&z[cur+1]=='1'&&z[cur+2]=='1')
{
putchar('3');
ok=0;
}
else if(z[cur]=='1'&&z[cur+1]=='0'&&z[cur+2]=='0')
{
putchar('4');
ok=0;
}
else if(z[cur]=='1'&&z[cur+1]=='0'&&z[cur+2]=='1')
{
putchar('5');
ok=0;
}
else if(z[cur]=='1'&&z[cur+1]=='1'&&z[cur+2]=='0')
{
putchar('6');
ok=0;
}
else if(z[cur]=='1'&&z[cur+1]=='1'&&z[cur+2]=='1')
{
putchar('7');
ok=0;
}
k=0;//将k置0,继续将下面三位16进制进行转换,也就是转化够 12位二进制就转化为8进制,然后归0,再继续。
}
}
putchar('\n');//每个数转化成八进制后的输出规范
}
return 0;
}
二次理解
/*题目:
给定n个十六进制正整数,输出它们对应的八进制数。
输入格式
输入的第一行为一个正整数n (1<=n<=10)。
接下来n行,每行一个由0~9、大写字母A~F组成的字符串,表示要转换的十六进制正整数,
每个十六进制数长度不超过100000。
输出格式
输出n行,每行为输入对应的八进制正整数。
【注意】
输入的十六进制数不会有前导0,比如012A。
输出的八进制数也不能有前导0。
*/
/* 想法:
1.数组存储字符串string ,shiliu[10][100000]:这样子表示只能是10个数,一个数占一行 ,100000是表示数值最大值
*****数组存储 整数 和 数组存储 字符串,表达的形态不太一样。
数组存储 整数
s[3][2]表示存储 3行2列个数,也就是 存储6 个数。
数组存储 字符串
s[3][2]表示存储 3 个数,一行代表一个数(字符串),2表示字符串的长度最大值,
也就是可以存99,1,但不可以存 100及以上。所以可以直接用s[i](s[3],可以输入3个数据)来输入数据,
当满足输入的数据 <= 2 (也就是字符串长度最大值)则存储。
er[12]。因为是存储字符串,所以要用char
2.进制转换,16-> 2-> 8。1 位十六进制 = 4位二进制数,1 位八进制数 = 3位二进制数。
为了转换方便一次转换 3 位16进制数,转化为 12 位二进制数,再转化为 4位八进制数。
问题1:不够 3的倍数 。3 位16进制数(例如1234,分为001,234。
也就是 16进制的 1转化位12位二进制数)转化为 二进制 , 不满12 位二进制数则补0。
问题2:输出的八进制数也不能有前导 0。设置一个标记,当前导为0;没有语句,也就是
if(ok) ; 这样子就不会有输出
总结:想法是好,有点麻烦,需要变动很多地方
*/
//头文件(1)
#include <stdio.h>
#include<string.h>
int main(){
//定义(最终要检查是否有漏!!!!!)
char shiliu[10][100000],er[12];
int n,i,j,ok;
int w=0;
//输入(2)
scanf("%d",&n);
for(i=0;i<n;i++)
scanf("%s",&shiliu[i]);
//一个个的字符串数进行转换(3)
for(i=0;i<n;i++)
{
//ok:用于判断12位二进制转为八进制时,最前面的四位是否为 0(7)
ok=1;
/*由于二进制换为八进制,二进制的位数是3的倍数更方便转换,所以当不够倍数时,
在前面补0 补够位数 。为了节省时间空间,一次换:3位16进制数换为 12位二进制
3位3位一组,所以er[12],探求关系与联系(5)
*/
int q=0;//q:用来表示某一个十六进制数的位数距离能被3整除还差几位
if(strlen(shiliu[i])%3 !=0)
{
q=3-(strlen(shiliu[i])%3);
//不足12位,补0(找q与12位缺的位数关系)
for(w=0;w<q*4;w++)
er[w]='0';
}
//每个字符串的字符各自一个个转换(4)
for(j=0;j<strlen(shiliu[i]);j++)
{
// int w=0;(放前面调整)
//定义语句要放在switch外面!!!!因为switch里面是分支语句,不含有其他语句
//转换用switch 分支语句进行一一转换,注意是字符,所以要加单引号,数字和字母都要!(5)
switch(shiliu[i][j])//此时看每个字符,则要具体存储位置,eg:s[2][2] 是 字符A
{ //一位 16进制可以转换为 4 为 二进制 ,转换完一位,er[]空间要往后加四,给下一个字符空间存储
//所以设 w=0;做位置标记,最后一位转换完 w+=4;
case '0':
er[w]='0';
er[w+1]='0';
er[w+2]='0';
er[w+3]='0';
break;
case '1':
er[w]='0';
er[w+1]='0';
er[w+2]='0';
er[w+3]='1';
break;
case '2':
er[w]='0';
er[w+1]='0';
er[w+2]='1';
er[w+3]='0';
break;
case '3':
er[w]='0';
er[w+1]='0';
er[w+2]='1';
er[w+3]='1';
break;
case '4':
er[w]='0';
er[w+1]='1';
er[w+2]='0';
er[w+3]='0';
break;
case '5':
er[w]='0';
er[w+1]='1';
er[w+2]='0';
er[w+3]='1';
break;
case '6':
er[w]='0';
er[w+1]='1';
er[w+2]='1';
er[w+3]='0';
break;
case '7':
er[w]='0';
er[w+1]='1';
er[w+2]='1';
er[w+3]='1';
break;
case '8':
er[w]='1';
er[w+1]='0';
er[w+2]='0';
er[w+3]='0';
break;
case '9':
er[w]='1';
er[w+1]='0';
er[w+2]='0';
er[w+3]='1';
break;
case 'A':
er[w]='1';
er[w+1]='0';
er[w+2]='1';
er[w+3]='0';
break;
case 'B':
er[w]='1';
er[w+1]='0';
er[w+2]='1';
er[w+3]='1';
break;
case 'C':
er[w]='1';
er[w+1]='1';
er[w+2]='0';
er[w+3]='0';
break;
case 'D':
er[w]='1';
er[w+1]='1';
er[w+2]='0';
er[w+3]='1';
break;
case 'E':
er[w]='1';
er[w+1]='1';
er[w+2]='1';
er[w+3]='0';
break;
case 'F':
er[w]='1';
er[w+1]='1';
er[w+2]='1';
er[w+3]='1';
break;
}
w+=4;
//当 3位 16进制数转换为 12位二进制,也就是er[12]满足够了12位,则先转换为八进制(6)
if(w==12)
{
int ba;
//3位 二进制可以换一位 8进制,设位置标记 ba;每转换 3位便位置 +3,下三位继续转换,也就是 ba+=3
for(ba=0;ba<12;ba+=3)
{
//ok:用于判断12位二进制转为八进制时,最前面的四位是否为 0(7)
if(er[ba]=='0'&& er[ba+1]=='0' && er[ba+2]=='0')
if(ok) ;
else putchar('0');
else if(er[ba]=='0'&& er[ba+1]=='0' && er[ba+2]=='1')
{
putchar('1');//输出 printf用于多数 和 putchar用于输出字符
ok = 0;
}
else if(er[ba]=='0'&& er[ba+1]=='1' && er[ba+2]=='0')
{
putchar('2');
ok = 0;
}
else if(er[ba]=='0'&& er[ba+1]=='1' && er[ba+2]=='1')
{
putchar('3');
ok = 0;
}
else if(er[ba]=='1'&& er[ba+1]=='0' && er[ba+2]=='0')
{
putchar('4');
ok = 0;
}
else if(er[ba]=='1'&& er[ba+1]=='0' && er[ba+2]=='1')
{
putchar('5');
ok = 0;
}
else if(er[ba]=='1'&& er[ba+1]=='1' && er[ba+2]=='0')
{
putchar('6');
ok = 0;
}
else if(er[ba]=='1'&& er[ba+1]=='1' && er[ba+2]=='1')
{
putchar('7');
ok = 0;
}
}
w=0;//将w置0,继续将下面三位16进制进行转换,也就是转化够 12位二进制就转化为8进制,然后归0,再继续。?
}
}
//测试发现结果都是合在一起 ,为了输出规范(8)
putchar('\n');//也可以是printf("\n");
}
return 0;
}
个人整理详细解说:采用公式法
/*这个是使用ASCLL码字符串所对应的十进制的值进行运算
'0'的值是48,'9'的值是57,'A'的值是65,'F' 的值是70
*/
#include<stdio.h>
#include<string.h>
int main()
{
int n,i,j,k,a,cur,ok,m,l,flag;
char s[10][100001],d16[16][5]= {"0000","0001","0010","0011","0100","0101","0110","0111","1000","1001","1010","1011","1100","1101","1110","1111"},out[4]= {},z[13]= {};
scanf("%d",&n);
for(j=0; j<n; j++)
scanf("%s",s[j]);
for(j=0; j<n; j++)
{
k=0;
ok=1;
flag = 0;
m=0; /*初始化标记数据*/
l=strlen(s[j]); /* 初始化标记数据*/
a=3-l%3; /*初始化标记数据*/
if(a==3) ok=0; /*十六进制数长度刚好为3的倍数时转二进制不需补0,ok标记其是否为3的倍数0是1不是*/
for(i=0; i<l; i++) /*逐位读取十六进制数进行转换*/
{
if(s[j][i]>=65)
s[j][i]-=7;//查看ASCLL表可知,'9'距离'A',也就是'A'-'9'=7
if(ok) /*十六进制数位数不足转二进制时补0占位*/
if(a==1)//若需补一个十六进制的0,相当于补上四个二进制的0即0000,下面补两位也一样
{
strcpy(z,"0000");
k=k+4;
ok=0;
a=0;
}
else if(a==2)
{
strcpy(z,"00000000");
k=k+8;
ok=0;
}
/*一位十六进制转四位二进制*///'0'的值就是48 ,是故减了48后就是十进制的数
//举个例子:输入字符串37,先将字符3进行换算
z[k++]=d16[s[j][i]-48][0]; //此时z[k+1]=d16['3'-'0'=3][0],d16[3]对应的是 0011,而d16[3][0]指的是0
z[k++]=d16[s[j][i]-48][1]; //此时z[k+1+1=k+2]=d16['3'-'0'=3][0],d16[3]对应的是 0011,而d16[3][1]指的是0
z[k++]=d16[s[j][i]-48][2];//此时z[k+3]=d16['3'-'0'=3][0],d16[3]对应的是 0011,而d16[3][2]指的是1
z[k++]=d16[s[j][i]-48][3];//此时z[k+4]=d16['3'-'0'=3][0],d16[3]对应的是 0011,而d16[3][0]指的是1
//这样子便可以把十六进制的 3 转化为二进制的3(0011),一个一个数字存入z[k++]里面
/*每转三位十六进制数将其转为四位八进制数*/
if(k==12)
{
m=0;
for(cur=0; cur<12; m++)//48就是'0'
out[m]=((z[cur++]-48)*4+(z[cur++]-48)*2+(z[cur++]-48)*1)+48;//3 位二进制数转化为 1 位8进制数
//4=2的平方。 2=2的一次方 1=2的 0次方 (由于3 位二进制数转化为 1 位8进制数 ,所以最高就到2的平方=4)
//可以这么写:out[m]=((z[cur++]-'0')*4+(z[cur++]-'0')*2+(z[cur++]-'0')*1)+'0';
/*举个例子:37。经过16进制转化:(37)16 =(0000,0011,0111)2=(000,000,110,111)2=(67)8
在这里是这样子的:out[m=0]=((z[0]-'0')*4+(z[1]-'0')*2+(z[2]-'0')*1)+'0';
out[0]=(('0'-'0')*4+('0'-'0')*2+('0'-'0')*1)+'0'='0';
由于后面 m++以及 cur++
out[1]=((z[3]-'0')*4+(z[4]-'0')*2+(z[5]-'0')*1)+'0';
out[1]=(('0'-'0')*4+('0'-'0')*2+('0'-'0')*1)+'0'='0';
out[2]=((z[6]-'0')*4+(z[7]-'0')*2+(z[8]-'0')*1)+'0';
out[2]=(('1'-'0')*4+('1'-'0')*2+('0'-'0')*1)+'0'='6';
out[3]=((z[9]-'0')*4+(z[10]-'0')*2+(z[11]-'0')*1)+'0';
out[3]=(('1'-'0')*4+('1'-'0')*2+('1'-'0')*1)+'0'='7';
此时结果为: 0067 ,m=12
*/
cur = 0;//置0,给下一个字符串数使用
k=0;//置0,给下一个字符串数使用
if(!flag)
while(out[cur]=='0'&&cur<m)//前面的 0不输出,让结果输出例子的结果:67
cur++;
if(cur==m)
flag = 0;
else
flag = 1;
for(; cur<m; cur++)
printf("%c",out[cur]);
}
}
putchar('\n');
}
return 0;
}