免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 4354 | 回复: 3
打印 上一主题 下一主题

[函数] 用C语言的curses例函数实现屏幕录入显示的cobol风格 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2008-04-26 22:07 |只看该作者 |倒序浏览
用C语言的curses例函数实现屏幕录入显示的cobol风格
                                ---by melove 2008/4/26

    尽管C语言是一门非常强大的编程语言,但在编写应用程序时如何实现
对数据和字符串在录入和显示时的有效控制,一直是一个令人头痛的问题
(别告诉我你用printf,scanf编写实用程序!)。 而略懂COBOL的人都
知道这方面恰恰是它的强项!!
    本文利用C语言强大的屏幕管理例函数---curses.h,来实现数据,
字符串录入和显示的COBOL风格做一个初步的探索。废话少说, 先看源
程序吧。

/* term.c */
/* 屏幕初始化
   该函数使得屏幕完全处在curses的控制之下 */
void initial()
{
   initscr();
   cbreak();
   nonl();
   noecho();
   intrflush(stdscr,FALSE);
   keypad(stdscr,TRUE);
   refresh();
}

/* 将一个指定小数点位数的双精度数, 格式化成带千分号的字符串 */
/* dec为小数点位数 */
char *mdou(double value,int dec)
{
   int i,sign;
   int len=24; /* 限定字符串的最大长度 */
   char *s;
   s=(char *)calloc(len+1,sizeof(char));
   /* 先置空格串 */
   /* 然后,从低位到高位将双精度数格式化成字符串 */
   for(i=0;i<len;i++) *(s+i)=' ';
   s+=len;
   *s='\0';
   /* 置符号位 */
   if(value<0){sign=1;value*=-1;} else sign=0;

   /* 误差纠正 */
   value+=0.1e-7;

   /* 先格式化小数部分 */
   for(i=0;i<dec;i++) value*=10;
   for(i=0;i<dec;i++){*--s=(int)fmod(value,10)+'0';value/=10;}
   if(dec>0) *--s='.'; /* 加入小数点 */
   /* 再格式化整数部分 */
   for(i=1;value>=10;value/=10,i++){
        *--s=(int)fmod(value,10)+'0';
        if(i%3==0) *--s=','; /* 加入千分符 */
        }
   if((int)value>0) *--s=(int)value+'0'; /* 加入最高位 */
   if(sign) *--s='-'; /* 加入符号位 */
   return(s);
}

/* 输入一个限制长度的字符串 */
/* min,max分别为最小和最大长度 */
int astr(char *s,int min,int max)
{
   int i,k,c;
   int lin,col; /* 光标行,列 */
   getyx(stdscr,lin,col); /* 获得光标的当前位置 */
   while(TRUE){
        k=strlen(s);
        /* 先显示已有的内容   
           不足的部分用空格清除 */
        mvaddnstr(lin,col,s,max);
        for(i=0;i<max-k;i++) addch(' ');
        if(k<max) move(lin,col+k);else move(lin,col+max-1);
        refresh();
        switch((c=getch())){ /* 接受一个字符并判断 */
           case '\r':
           case '\n':
           case KEY_DOWN:
                if(k>=min) return(2); else beep();
                break;
           case KEY_UP:
                if(k>=min) return(1); else beep();
                break;
           case KEY_LEFT:
                if(k>=min) return(3); else beep();
                break;
           case KEY_RIGHT:
                if(k>=min) return(4); else beep();
                break;
           case KEY_BACKSPACE: /* 删除字符,但该键读取可能会有问题 */
           case 8: /* 所以直接用ascii码判断 */
           case 127:
                if(k>0) *(s+k-1)='\0';else beep();
                break;
           default:
                if(k<max){*(s+k)=c;*(s+k+1)='\0';}
                   else if(k==max){*(s+k-1)=c;beep();}
                        else beep();
        }
   }
}

/* 输入一个指定长度和小数点位数的双精度数 */
/* min,max,dec分别为最小,最大位数和小点数位数 */
int adou(double *value,int min,int max,int dec)
{
   int i,k,c,len;
   int lin,col; /* 光标行,列 */
   int Ex=0; /* 结束标志 */
   int sign=0; /* 符号标志 */
   char *s,*ss;
   double p;
   /* 置符号位,前把初值转换成只含整数的绝对值 */
   if(*value>=0) p=*value;
        else {p=*value*-1;sign=1;}

   /* 误差纠正 */
   p+=0.1e-7;

   for(i=0;i<dec;i++) p*=10;
   s=(char *)calloc(2*max+1,sizeof(char));
   for(i=0;i<max;i++) *(s+i)=' ';
   *(s+max)='\0';
   ss=s+max;
   /* 把初值转换成字符串 */
   while(p>=10){*--ss=(int)fmod(p,10)+'0';p/=10;}
   if((int)p>0) *--ss=(int)p+'0';
   while(*s==' ') s++; /* 舍去前置多余的空格 */
   /* 获得光标的当前位置 */
   getyx(stdscr,lin,col);
   /* 现在可以开始接受输入了 */
   while(TRUE){
        k=strlen(s);
        move(lin,col);
        for(i=0;i<max-k-1;i++) addch('_');
        /* 打印符号位 */
        if(sign==1) addch('-');
           else if(k<max) addch('_');
        addstr(s);
        move(lin,col+max-1); /* 把输入光标定位在最后一个位置 */
        refresh();
        switch(c=getch()){
           case '0':
           case '1':
           case '2':
           case '3':
           case '4':
           case '5':
           case '6':
           case '7':
           case '8':
           case '9':
                if(k<max-sign){*(s+k)=c;*(s+k+1)='\0';}
                   else if(k=max-sign) *(s+k-1)=c;
                        else beep();
                break;
           case '\r':
           case '\n':
           case KEY_DOWN:
                if(k>=min) Ex=2;else beep();
                break;
           case KEY_UP:
                if(k>=min) Ex=1;else beep();
                break;
           case KEY_LEFT:
                if(k>=min) Ex=3;else beep();
                break;
             case KEY_RIGHT:
                if(k>=min) Ex=4;else beep();
                break;
           case KEY_BACKSPACE: /* 删除字符,但该键读取可能会有问题 */
           case 8: /* 所以直接用ascii码判断 */
           case 127:
                if(k>=1) *(s+k-1)='\0';else beep();
                break;
           case '-':
                if(sign==1) sign=0;
                   else if(k<max) sign=1; /* 如果位置已满就不接受负号键 */
                        else beep();
                break;
           case '+':
                sign=0;
                break;
           /* default:
                 beep(); */
        }
        /* 结束前处理 */
        if(Ex!=0){
           p=atof(s); /* 把字符串转换成双精度数 */
           for(i=0;i<dec;i++) p/=10; /* 重新给出小数部分 */
           if(sign==1) *value=p*-1;else *value=p; /* 给出符号位 */
           return(Ex);
        }
   }
}

      看完以上源程序,想必你已明白了它的目的。现在就让我们利用它来编写一个简单的测
试程序来看看它的效果如何,以及是否准确吧。

/* test.c */
#include "stdio.h"
#include "math.h"
#include "curses.h"

#include "term.c" /* 自定义函数 */

main(){
        int i;
        double value=0;
        char *s;
        /* 空间申请 */
        s=(char *)calloc(50,sizeof(char));
        initial(); /* 显幕初始化 */
        while(value!=9){
                /* 输入一个12位长,其中3位是小数的双精度数 */
                move(24,10);i=adou(&value,0,12,3);
                /* 打印输入的数值和函数返回值 */
                mvprintw(22,10,"%lf    %d",value,i);
                /* 删除光标后的本行字符 */
                clrtoeol();
                /* 然后把它转换成带千分号的字符串,并存到s中 */
                strcpy(s,mdou(value,3));
                attron(A_REVERSE); /* 反白 */
                /* 以s为初值,输入一个最小长度为2,最大长度为40的串 */
                move(20,10);astr(s,2,40);
                attroff(A_REVERSE);
        }
        endwin();
}

      本文的测试环境为SCO OpenServer 5.0.5的自带开发系统,编译命令:

cc test.c -lc -lm -lcurses -o test

现在你该可以运行test了吧。

    别急,你还得注意以下几个问题:
    1、double类型的数据,有效位最多为16位,超过这个长度是不可靠的!
    2、程序中之所以要进行误差纠正,是因为内存中的实际数值总比你要的小那么一丁点,
       这不是程序或算法的问题,这是double本身的问题。
    3、结束录入不仅可以是回车,还可以用上下左右方向键,并把它做为函数的返回值,
当然你也可以不这样做。
    4、删除一个字符时,curses keypad定义的退格或删除键(KEY_BACKSPACE)在某些键盘
的某些环境下会读不到,所以直接用ascii码进行判断,这当然不是好办法,却也是无奈之举。
    5、经过长时间测试,录入数值时,小数点位数最好不要超过4位!但我想也够用了。
    6、调用initial()后,由于屏幕完全处在curses的控制之下,任何意图跟屏幕打交道的操作都
得使用curses的库函数,如printf,scanf等将不能再被使用!

    本文只是初探,如果你觉得有用,则完全可以在这基础上,编写你自己的个性化函数,以供实
际应用!水平有限,如有更好的方法,或有什么错误请及时告知,不胜感激!!

论坛徽章:
0
2 [报告]
发表于 2008-04-29 18:19 |只看该作者
优化一下函数。期待高手给个更好的思路或算法,谢了。:wink: :wink:

/* 将一个指定小数点位数的双精度数,
   格式化成一串指定长度,并带千分号的字符串 */
char *mdou(double value,int dec)
{
   int i,len;
   int sign,zs;
   char *s1,*s2;
   s1=(char *)calloc(30,sizeof(char));
   s2=(char *)calloc(30,sizeof(char));
   /* 将value转换成字符串,并保留16位有效数 */
   strcpy(s1,ecvt(value,16,&zs,&sign));
   /* 如果value为零则空串返回 */
   if(value==0) *s2='\0';
        else{        if(sign==1) strcpy(s2,"-"; /* 先写入符号位 */
                /* 如果只有小数部分 */
                if(zs<=0&&dec+zs>0){
                        strcat(s2,".";
                        for(i=0;i<abs(zs);i++) strcat(s2,"0";
                        strncat(s2,s1,dec+zs);}
                else if(dec+zs<=0) *s2='\0';
                     else{
                        /* 如果有整数,先写入整数部分 */
                        while(zs>0){
                                strncat(s2,s1++,1);
                                /* 写入千分符 */
                                zs--;
                                if((zs%3==0)&&zs>0)strcat(s2,",";
                        }
                        /* 再写入小数部分 */
                        if(dec>0){
                                strcat(s2,".";
                                strncat(s2,s1,dec);
                        }
                     }
            }
   return(s2); /* 返回 */
}


/* 输入一个指定长度和小数点位数的双精度数 */
int adou(double *value,int min,int max,int dec)
{
   int i,k,c,len;
   int lin,col; /* 光标行,列 */
   int Ex=0; /* 结束标志 */
   int sign=0; /* 符号标志 */
   double p;
   char *s,*r;
   s=(char *)calloc(max+2,sizeof(char));
   /* 把初值转换成字符串 */
   strcpy(s,ecvt(*value,max,&i,&sign));
   if((i+dec>0)&&(i+dec<=max)&&(*s!='0')) *(s+i+dec)='\0';
        else *s='\0';
   /* 获得光标的当前位置 */
   getyx(stdscr,lin,col);
   /* 现在可以开始接受输入了 */
   k=strlen(s);
   while(TRUE){
        move(lin,col);
        for(i=0;i<max-k-1;i++) addch('_');
        /* 打印符号位 */
        if(sign==1) addch('-');
           else if(k<max) addch('_');
        addstr(s);
        move(lin,col+max-1); /* 把输入光标定位在最后一个位置 */
        refresh();
        switch(c=getch()){
           case '0':
           case '1':
           case '2':
           case '3':
           case '4':
           case '5':
           case '6':
           case '7':
           case '8':
           case '9':
                if(k<max-sign){*(s+k)=c;*(s+k+1)='\0';k++;}
                   else if(k=max-sign) *(s+k-1)=c;
                        else beep();
                break;
           case '\r':
           case '\n':
           case KEY_DOWN:
                if(k>=min) Ex=2;else beep();
                break;
           case KEY_UP:
                if(k>=min) Ex=1;else beep();
                break;
           case KEY_LEFT:
                if(k>=min) Ex=3;else beep();
                break;
             case KEY_RIGHT:
                if(k>=min) Ex=4;else beep();
                break;
           case KEY_BACKSPACE: /* 删除字符,但该键读取可能会有问题 */
           case 8: /* 所以直接用ascii码判断 */
           case 127:
                if(k>=1) *(s+--k)='\0';else beep();
                break;
           case '-':
                if(sign==1) sign=0;
                   else if(k<max) sign=1; /* 如果位置已满就不接受负号键 */
                        else beep();
                break;
           case '+':
                sign=0;
                break;
           /* default:
                 beep(); */
        }
        /* 结束前处理 */
        if(Ex!=0){
              r=(char *)calloc(max+2,sizeof(char));
           if(sign==1) strcpy(r,"-"; /* 给出符号位 */
           if(k<=dec){
                strcat(r,"0.";
                for(i=0;i<dec-k;i++) strcat(r,"0";
                strcat(r,s);}
                else {        strncat(r,s,k-dec);
                        strcat(r,".";
                        s=s+k-dec;
                        strcat(r,s); }
           *value=atof(r); /* 把字符串转换成双精度数 */
           if(sign==1) *value-=0.1e-8;
                else *value+=0.1e-8;
           return(Ex);
        }
   }
}

论坛徽章:
0
3 [报告]
发表于 2008-04-29 18:20 |只看该作者
这么变成这样了。唉。

论坛徽章:
0
4 [报告]
发表于 2008-04-30 08:41 |只看该作者
代码用
  1. [code]
复制代码
[/code]包围起来
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP