2007年04月07日

当我们的程序中有经常使用的模块,而且这种模块在其他程序中也会用到,这时按照软件重用的思想,我们应该将它们生成库,使得以后编程可以减少开发代码量。这里介绍两个命令ar和nm,用来对库操作。

1.ar基本用法
2.nm基本用法命令

 当我们的程序中有经常使用的模块,而且这种模块在其他程序中也会用到,这时按照软件重用的思想,我们应该将它们生成库,使得以后编程可以减少开发代码量。这里介绍两个命令ar和nm,用来对库操作。

1.ar基本用法

  ar命令可以用来创建、修改库,也可以从库中提出单个模块。库是一单独的文件,里面包含了按照特定的结构组织起来的其它的一些文件(称做此库文件的member)。原始文件的内容、模式、时间戳、属主、组等属性都保留在库文件中。

  下面是ar命令的格式:

ar [-]{dmpqrtx}[abcfilNoPsSuvV] [membername] [count] archive files…

  例如我们可以用ar rv libtest.a hello.o hello1.o来生成一个库,库名字是test,链接时可以用-ltest链接。该库中存放了两个模块hello.o和hello1.o。选项前可以有‘-’字符,也可以没有。下面我们来看看命令的操作选项和任选项。现在我们把{dmpqrtx}部分称为操作选项,而[abcfilNoPsSuvV]部分称为任选项。

  {dmpqrtx}中的操作选项在命令中只能并且必须使用其中一个,它们的含义如下:

d:从库中删除模块。按模块原来的文件名指定要删除的模块。如果使用了任选项v则列出被删除的每个模块。
m:该操作是在一个库中移动成员。当库中如果有若干模块有相同的符号定义(如函数定义),则成员的位置顺序很重要。如果没有指定任选项,任何指定的成员将移到库的最后。也可以使用’a',’b',或’I'任选项移动到指定的位置。
p:显示库中指定的成员到标准输出。如果指定任选项v,则在输出成员的内容前,将显示成员的名字。如果没有指定成员的名字,所有库中的文件将显示出来。
q:快速追加。增加新模块到库的结尾处。并不检查是否需要替换。’a',’b',或’I'任选项对此操作没有影响,模块总是追加的库的结尾处。如果使用了任选项v则列出每个模块。 这时,库的符号表没有更新,可以用’ar s’或ranlib来更新库的符号表索引。
r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。
t:显示库的模块表清单。一般只显示模块名。
x:从库中提取一个成员。如果不指定要提取的模块,则提取库中所有的模块。
  下面在看看可与操作选项结合使用的任选项:

a:在库的一个已经存在的成员后面增加一个新的文件。如果使用任选项a,则应该为命令行中membername参数指定一个已经存在的成员名。
b:在库的一个已经存在的成员前面增加一个新的文件。如果使用任选项b,则应该为命令行中membername参数指定一个已经存在的成员名。
c:创建一个库。不管库是否存在,都将创建。
f:在库中截短指定的名字。缺省情况下,文件名的长度是不受限制的,可以使用此参数将文件名截短,以保证与其它系统的兼容。
i:在库的一个已经存在的成员前面增加一个新的文件。如果使用任选项i,则应该为命令行中membername参数指定一个已经存在的成员名(类似任选项b)。
l:暂未使用
N:与count参数一起使用,在库中有多个相同的文件名时指定提取或输出的个数。
o:当提取成员时,保留成员的原始数据。如果不指定该任选项,则提取出的模块的时间将标为提取出的时间。
P:进行文件名匹配时使用全路径名。ar在创建库时不能使用全路径名(这样的库文件不符合POSIX标准),但是有些工具可以。
s:写入一个目标文件索引到库中,或者更新一个存在的目标文件索引。甚至对于没有任何变化的库也作该动作。对一个库做ar s等同于对该库做ranlib。
S:不创建目标文件索引,这在创建较大的库时能加快时间。
u:一般说来,命令ar r…插入所有列出的文件到库中,如果你只想插入列出文件中那些比库中同名文件新的文件,就可以使用该任选项。该任选项只用于r操作选项。
v:该选项用来显示执行操作选项的附加信息。
V:显示ar的版本。
2.nm基本用法命令

  nm用来列出目标文件的符号清单。下面是nm命令的格式:

nm [-a|--debug-syms] [-g|--extern-only] [-B][-C|--demangle] [-D|--dynamic] [-s|--print-armap][-o|--print-file-name] [-n|--numeric-sort][-p|--no-sort] [-r|--reverse-sort] [--size-sort][-u|--undefined-only] [-l|--line-numbers] [--help][--version] [-t radix|--radix=radix][-P|--portability] [-f format|--format=format][--target=bfdname] [objfile...]

  如果没有为nm命令指出目标文件,则nm假定目标文件是a.out。下面列出该命令的任选项,大部分支持"-"开头的短格式和"—"开头的长格式。

-A、-o或–print-file-name:在找到的各个符号的名字前加上文件名,而不是在此文件的所有符号前只出现文件名一次。
例如nm libtest.a的输出如下:

CPThread.o:
00000068 T Main__8CPThreadPv
00000038 T Start__8CPThread
00000014 T _._8CPThread
00000000 T __8CPThread
00000000 ? __FRAME_BEGIN__
…………………………………

则nm -A 的输出如下:

libtest.a:CPThread.o:00000068 T Main__8CPThreadPv
libtest.a:CPThread.o:00000038 T Start__8CPThread
libtest.a:CPThread.o:00000014 T _._8CPThread
libtest.a:CPThread.o:00000000 T __8CPThread
libtest.a:CPThread.o:00000000 ? __FRAME_BEGIN__
…………………………………………………………

-a或–debug-syms:显示调试符号。
-B:等同于–format=bsd,用来兼容MIPS的nm。
-C或–demangle:将低级符号名解码(demangle)成用户级名字。这样可以使得C++函数名具有可读性。
-D或–dynamic:显示动态符号。该任选项仅对于动态目标(例如特定类型的共享库)有意义。
-f format:使用format格式输出。format可以选取bsd、sysv或posix,该选项在GNU的nm中有用。默认为bsd。
-g或–extern-only:仅显示外部符号。
-n、-v或–numeric-sort:按符号对应地址的顺序排序,而非按符号名的字符顺序。
-p或–no-sort:按目标文件中遇到的符号顺序显示,不排序。
-P或–portability:使用POSIX.2标准输出格式代替默认的输出格式。等同于使用任选项-f posix。
-s或–print-armap:当列出库中成员的符号时,包含索引。索引的内容包含:哪些模块包含哪些名字的映射。
-r或–reverse-sort:反转排序的顺序(例如,升序变为降序)。
–size-sort:按大小排列符号顺序。该大小是按照一个符号的值与它下一个符号的值进行计算的。
-t radix或–radix=radix:使用radix进制显示符号值。radix只能为"d"表示十进制、"o"表示八进制或"x"表示十六进制。
–target=bfdname:指定一个目标代码的格式,而非使用系统的默认格式。
-u或–undefined-only:仅显示没有定义的符号(那些外部符号)。
-l或–line-numbers:对每个符号,使用调试信息来试图找到文件名和行号。对于已定义的符号,查找符号地址的行号。对于未定义符号,查找指向符号重定位入口的行号。如果可以找到行号信息,显示在符号信息之后。
-V或–version:显示nm的版本号。
–help:显示nm的任选项。 

2006年08月08日
http://www.cublog.cn/u/21444/showart.php?id=147011
用同一块buffer,通过下面两个程序测试出来.
#include <iostream>;
#include <time.h>;
using namespace std;
int main()
{
 time_t now;
 char * ptime;
 if(time(&now)<0)
 {
  cout << "error\n" << endl;
  exit(-1);
 }
#ifdef _AIX
    ptime = asctime(gmtime((time_t *)&now));
#else 
    ptime = asctime(gmtime((long *)&now));
#endif
 cout << now << endl;
 cout << ctime(&now) << endl;
 cout << now << endl;
 cout << ptime << endl;
 printf("haha\n ");
 return 0;
}
 
 
#include <iostream>;
#include <time.h>;
using namespace std;
int main()
{
 time_t now;
 char * ptime;
 if(time(&now)<0)
 {
  cout << "error\n" << endl;
  exit(-1);
 }
 cout << now << endl;
 cout << ctime(&now) << endl;
#ifdef _AIX
    ptime = asctime(gmtime((time_t *)&now));
#else 
    ptime = asctime(gmtime((long *)&now));
#endif
 cout << now << endl;
 cout << ptime << endl;
 printf("haha\n ");
 return 0;
}
 
localtime()和gmtime()之间的区别是:localtime将日历时间转换成本地时间(考虑到本地时区和夏时制标志),而gmtime则将日历时间转换成国际标准时间的年、月、日、时、分、秒、周日。它们的定义如下:

     struct tm* gmtime(const time_t* mem);
     struct tm* localtime(const time_t* mem);

     函数mktime()则正好相反,它是以存放有本地时间年、月、日等的tm结构作为参数,将其转换成time_t类型的秒值。mktime()函数的定义是:

     time_t mktime(struct tm* tmptr);  //成功返回日历时间,失败则返回-1 

     函数asctime()和ctime()可以获得人们可读的时间字符串,表示形式如同使用date命令所获得的系统默认的时间输出形式。它们的定义如下:

     char* asctime(const struct tm* tmptr); //参数是指向存放有本地时间年、月、日等的tm结构的指针
     char* ctime(const time_t* mem);   //参数是指向日历时间的指针

     函数strftime()是最为复杂的时间函数,可用于用户自定义时间的表示形式。函数strftime()的定义如下:

     size_t strftime(char* buf, size_t maxsize, const char* format,
                       const struct tm* tmptr);  //有空间则返回所存入数组的字符数,否则为0

自定义格式的结果存放在一个长度为maxsize的buf数组中,如果buf数组长度足以存放格式化结果及一个null终止符,则该函数返回在buf数组中存放的字符数(不包括null终止符),否则该函数返回0。format参数用于控制自定义时间的表示格式,格式的定义是在百分号之后跟一个特定字符,format中的其他字符则按原样输出。其中特别应注意的是,两个连续的百分号则是表示输出一个百分号。常用的定义格式如下表所示。

格式
说明
例子
% a
缩写的周日名
Tue
% A
全周日名
Tuesday
% b
缩写的月名
Jan
% B
月全名
January
% c
日期和时间
Wed Aug 17 19:40:30 2005
% d
月日:[01, 31]
14
% H
小时(每天2 4小时):[00, 23]
19
% I
小时(上、下午各1 2小时[01, 12]
07
% j
年日:[001, 366]
014
% m
月:[01, 12]
01
% M
分:[00, 59]
40
% p
A M / P M
PM
% S
秒:[00, 61]
30
% U
星期日周数:[00, 53]
02
% w
周日:[ 0 =星期日,6 ]
2
% W
星期一周数:[00, 53]
02
% x
日期
08/17/05
% X
时间
19:40:30
% y
不带公元的年:[00, 991]
05
% Y
带公元的年
2005
% Z
时区名
MST

2006年04月10日

C语言中对时间和日期的处理

Chuck Allison

Chuck Allison是盐湖城圣Latter Day教堂总部下耶稣教堂家族历史研究处的软件体系设计师。他拥有数学学士和数学硕士学位。他从1975年起开始编程,从1984年起他开始从事c语言的教学和开发。他目前的兴趣是面向对象的技术及其教育。他是X3J16ANSI C ++标准化委员会的一员。发送e-mailallison@decus.org,或者拨打电话到(801)240-4510均可以与他取得联系。

大部分的操作系统有办法得到当前的日期和时间。通过定义在time.h的库函数,ANSI C能以许多不同的形式得到这个信息。函数time返回一个类型为time_t的值(通常为long),该函数在运行期间对当前的日期和时间进行编码。然后你可以将这个返回值传递给其他能对该值进行解码和格式化的函数。

Listing 1中的程序使用函数timelocaltimestrftime以不同的形式输出当前的日期和时间。函数localtime把已经编码的时间解码成如下的struct

struct tm


{


   int tm_sec;     /* (0 - 61) */


   int tm_min;     /* (0 - 59) */


   int tm_hour;    /* (0 - 23) */


   int tm_mday;    /* (1 - 31) */


   int tm_mon;     /* (0 - 11) */


   int tm_year;    /* past 1900 */


   int tm_wday;    /* (0 - 6) */


   int tm_yday;    /* (0 - 365) */


   int tm_isdst;   /* daylight savings flag */


};


每次当你调用localtime的时候,它会重写一个静态的结构并返回该结构的地址(因此同一时刻在一个程序中只能取得一个这样的结构,而不能做明显的拷贝)。函数ctime返回一个指向静态字符串的指针,该字符串以标准的格式包含了完整的时间和日期。strftime根据用户的指定格式格式化字符串(例如,%A代表一周中每一天的名称)。Table 1列出了格式描述符的完整列表。

时间/日期运算

通过改变tm结构里的值,可对时间/日期进行运算。Listing 2中的程序展示了如何计算将来某天的日期和以秒为单位所计算出的程序执行时间。注意函数time的语法(参数time_t由地址传入,并非作为函数的返回值)。函数mktime改变tm结构的值,以便日期和时间在一个合适的范围内,之后day-of-week (tm_wday)和day-of-year (tm_yday)域进行相应的更新。mktimetm结构中日期和时间的值置于合适的范围之内,相应的更新day of week (tm-wday)和day of year (tm-yday)的值。这种情况发生在当一个日期超出了你的实现能够支持的范围的时候。例如,我的MS-DOS的编译器不能编码1970年1月份之前的日期。函数asctime返回tm参数所描述时间的标准字符串(因此ctime (&tval)与asctime (localtime(&tval)是相等的)。函数difftime返回用秒做单位的两个time_t的差。

如果需要处理超出系统范围的日期,或者需要计算两个日期的间隔又不是用秒来做单位,那你需要设计自己的date编码。Listing 3Listing 5中的应用程序通过使用一个简单的month-day-year结构,展示了确定两个日期间隔的年数、月份数和天数的技术。日期的相减就像你在小学里做的减法那样(例如,首先进行天数的相减,如果需要就向月份数借位,以此类推)。注意跳过的年份都被计算进去了。为了简略起见,date_interval函数假设日期都是有效的,并且第一个日期在第二个日期之前。函数返回一个指向静态Date结构的指针,该结构包含了我们想要的答案。

文件时间/日期戳

大多数操作系统为文件维护时间/日期戳。至少你能得知一个文件最后被修改的时间。(常用的make工具使用这一信息来决定一个文件是否需要被重新编译,或者一个应用程序是否需要被重新连接)。由于文件系统在不同平台上有所不同,没有什么通用的函数得到一个文件的时间/日期戳,因此ANSI 标准没有定义这样的函数。然而,大多数流行的操作系统(包括MS-DOS和VAX/VMS)提供了UNIX函数stat,该函数返回相关的文件信息,包括用time_t表示的最后修改时间。

Listing 6中的程序使用statdifftime来确定是否time1.ctime2.c更新(例如,是否最近被修改过)。

如果你需要更新一个文件的时间/日期戳到当前时间,可简单的重写文件的第一个字节。虽然实际内容并未改变,但你的文件系统会认为文件已经被改变了,并且会相应的更新时间/日期戳。(知道你的文件系统!在VAX/VMS下,当你得到一个文件的新版本的时候,旧的版本仍会被保留)。这种技术叫做“‘touching’一个文件”。Listing 7touch的实现在指定文件不存在的时候会创建一个新文件。注意文件以“binary”模式打开(在打开模式字符串中由字符b决定—在将来的专栏中我会详细讨论文件处理的问题)。

1:strftime的格式描述符

Code  Sample Output
---------------------------------------------
%a    Wed
%A    Wednesday
%b    Oct
%B    October
%c    Wed Oct 07 13:24:27 1992
%d    07    (day of month [01-31])
%H    13    (hour in [00-23])
%I    01    (hour in [01-12])
%j    281   (day of year [001-366])
%m    10    (month [01-12])
%M    24    (minute [00-59])
%p    PM
%S    27    (second [00-59] )
%U    40    (Sunday week of year [00-52])
%w    3     (day of week [0-6])
%W    40    (Monday week of year [00-52])
%x    Wed Oct 7, 1992
%X    13:24:27
%y    92
%Y    1992
%Z    EDT   (daylight savings indicator)

Listing 1 time1.c — 采用不同格式输出当前的日期和时间

#include <stdio.h>
#include <time.h>
 


#define BUFSIZE 128
 


main()
{
   time_t tval;
   struct tm *now;
   char buf[BUFSIZE];
   char *fancy_format =
     "Or getting really fancy:\n"
     "%A, %B %d, day %j of %Y.\n"
     "The time is %I:%M %p.";
 


   /* Get current date and time */
   tval = time(NULL);
   now = localtime(&tval);
   printf("The current date and time:\n"
         "%d/%02d/%02d %d:%02d:%02d\n\n",
     now->tm_mon+1, now->tm_mday, now->tm_year,
     now->tm_hour, now->tm_min, now->tm_sec);
   printf("Or in default system format:\n%s\n",
         ctime(&tval));
   strftime(buf,sizeof buf,fancy_format,now);
   puts(buf);
 


   return 0;
}
 


/*  Output
The current date and time:
10/06/92 12:58:00
 


Or in default system format:
Tue Oct 06 12:58:00 1992
 


Or getting really fancy:
Tuesday, October 06, day 280 of 1992.
The time is 12:58 PM.
*/
 


/* End of File */

Listing 2 time2.c —展示如何计算将来某一天的日期以及以秒为单位计算出的执行时间

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
 


main()
{
   time_t start, stop;
   struct tm *now;
   int ndays;
 


   /* Get current date and time */
   time(&start);
   now = localtime(&start);
 


   /* Enter an interval in days */
   fputs("How many days from now? ",stderr);
   if (scanf("%d",&ndays) !=1)
      return EXIT_FAILURE;
   now->tm_mday += ndays;
   if (mktime(now) != -1)
      printf("New date: %s",asctime(now));
   else
      puts("Sorry. Can't encode your date.");
 


   /* Calculate elapsed time */
   time(&stop);
   printf("Elapsed program time in seconds: %f\n",
     difftime(stop,start));
 


   return EXIT_SUCCESS;
}
 


/* Output
How many days from now? 45
New date: Fri Nov 20 12:40:32 1992
Elapsed program time in seconds: 1.000000
*/
 


/* End of File */

Listing 3 date.h — 一个简单的日期结构

struct Date
{
   int day;
   int month;
   int year;
};
typedef struct Date Date;
 


Date* date_interval(const Date *, const Date *);
/* End of File */

Listing 4 date_int.c — 计算两个日期的间隔

/* date_int.c: Compute duration between two dates */
 


#include "date.h"
 


#define isleap(y) \
 ((y)%4 == 0 && (y)%100 != 0 || (y)%400 == 0)
 


static int Dtab [2][13] =
{
  {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
  {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
};
 


Date *date_interval(const Date *d1, const Date *d2)
{
   static Date result;
   int months, days, years, prev_month;
 


   /* Compute the interval - assume d1 precedes d2 */
   years = d2->year - d1->year;
   months = d2->month - d1->month;
   days = d2->day - d1->day;
 


   /* Do obvious corrections (days before months!)
    *
    * This is a loop in case the previous month is
    * February, and days < -28.
    */
   prev_month = d2->month - 1;
   while (days < 0)
   {
      /* Borrow from the previous month */
      if (prev_month == 0)
         prev_month = 12;
      --months;
      days += Dtab[isleap(d2->year)][prev_month--];
   }
 


   if (months < 0)
   {
      /* Borrow from the previous year */
      --years;
      months += 12;
   }
 


   /* Prepare output */
   result.month = months;
   result.day = days;
   result.year = years;
   return &result;
}
/* End of File */

Listing 5 tdate.c — 举例说明日期间隔函数的使用

/* tdate.c: Test date_interval() */
 


#include <stdio.h>
#include <stdlib.h>
#include "date.h"
 


main()
{
   Date d1, d2, *result;
   int nargs;
 


   /* Read in two dates - assume 1st precedes 2nd */
   fputs("Enter a date, MM/DD/YY> ",stderr);
   nargs = scanf("%d/%d/%d%*c", &d1.month,
     &d1.day, &d1.year);
   if (nargs != 3)
      return EXIT_FAILURE;
 


   fputs("Enter a later date, MM/DD/YY> ",stderr);
   nargs = scanf("%d/%d/%d%*c", &d2.month,
     &d2.day, &d2.year);
   if (nargs != 3)
      return EXIT_FAILURE;
 


   /* Compute interval in years, months, and days */
   result = date_interval(&d1, &d2);
   printf("years: %d, months: %d, days: %d\n",
      result->year, result->month, result->day);
   return EXIT_SUCCESS;
 


}
/* Sample Execution:
Enter a date, MM/DD/YY> 10/1/51
Enter a later date, MM/DD/YY> 10/6/92
years: 41, months: 0, days: 5 */
/* End of File */

Listing 6 ftime.c — 确定是否time1.c比time2.c更新

/* ftime.c: Compare file time stamps */
 


#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <time.h>
 


main()
{
   struct stat fs1, fs2;
 


   if (stat("time1.c",&fs1) == 0 &&
      stat("time2.c",&fs2) == 0)
   {
      double interval =
        difftime(fs2.st_mtime,fs1.st_mtime);
 


      printf("time1.c %s newer than time2.c\n",
        (interval < 0.0) ? "is" : "is not");
      return EXIT_SUCCESS;
   }
   else
      return EXIT_FAILURE;
}
/* Output
time1.c is not newer than time2.c */
/* End of File */

Listing 7 touch.c —通过覆盖旧文件或者创建一个新的文件来更新时间戳

/* touch.c: Update a file's time stamp */
 


#include <stdio.h>
 


void touch(char *fname)
{
   FILE *f = fopen(fname,"r+b");
   if (f != NULL)
   {
      char c = getc(f);
      rewind(f);
      putc(c,f);
   }
   else
      fopen(fname,"wb");
 


   fclose(f);
}
 


/* End of File */
int i;
  file1>>i;//从文件输入一个整数值。 

  这种方式还有一种简单的格式化能力,比如可以指定输出为16进制等等,具体的格式有以下一些

操纵符 功能 输入/输出 
dec 格式化为十进制数值数据 输入和输出 
endl 输出一个换行符并刷新此流 输出 
ends 输出一个空字符 输出 
hex 格式化为十六进制数值数据 输入和输出 
oct 格式化为八进制数值数据 输入和输出 
setpxecision(int p) 设置浮点数的精度位数 输出 

  比如要把123当作十六进制输出:file1<<hex<<123;要把3.1415926以5位精度输出:file1<<setpxecision(5)<<3.1415926。

  2、二进制文件的读写
①put()
  put()函数向流写入一个字符,其原型是ofstream &put(char ch),使用也比较简单,如file1.put(‘c’);就是向流写一个字符’c'。 

②get()
  get()函数比较灵活,有3种常用的重载形式:

  一种就是和put()对应的形式:ifstream &get(char &ch);功能是从流中读取一个字符,结果保存在引用ch中,如果到文件尾,返回空字符。如file2.get(x);表示从文件中读取一个字符,并把读取的字符保存在x中。

  另一种重载形式的原型是: int get();这种形式是从流中返回一个字符,如果到达文件尾,返回EOF,如x=file2.get();和上例功能是一样的。

  还有一种形式的原型是:ifstream &get(char *buf,int num,char delim=’\n’);这种形式把字符读入由 buf 指向的数组,直到读入了 num 个字符或遇到了由 delim 指定的字符,如果没使用 delim 这个参数,将使用缺省值换行符’\n’。例如:

  file2.get(str1,127,’A');//从文件中读取字符到字符串str1,当遇到字符’A'或读取了127个字符时终止。

③读写数据块
  要读写二进制数据块,使用成员函数read()和write()成员函数,它们原型如下:

    read(unsigned char *buf,int num);
    write(const unsigned char *buf,int num);

  read()从文件中读取 num 个字符到 buf 指向的缓存中,如果在还未读入 num 个字符时就到了文件尾,可以用成员函数 int gcount();来取得实际读取的字符数;而 write() 从buf 指向的缓存写 num 个字符到文件中,值得注意的是缓存的类型是 unsigned char *,有时可能需要类型转换。

例:

    unsigned char str1[]="I Love You";
    int n[5];
    ifstream in("xxx.xxx");
    ofstream out("yyy.yyy");
    out.write(str1,strlen(str1));//把字符串str1全部写到yyy.yyy中
    in.read((unsigned char*)n,sizeof(n));//从xxx.xxx中读取指定个整数,注意类型转换
    in.close();out.close(); 

四、检测EOF
  成员函数eof()用来检测是否到达文件尾,如果到达文件尾返回非0值,否则返回0。原型是int eof();

例:  if(in.eof())ShowMessage("已经到达文件尾!");

五、文件定位
  和C的文件操作方式不同的是,C++ I/O系统管理两个与一个文件相联系的指针。一个是读指针,它说明输入操作在文件中的位置;另一个是写指针,它下次写操作的位置。每次执行输入或输出时,相应的指针自动变化。所以,C++的文件定位分为读位置和写位置的定位,对应的成员函数是 seekg()和 seekp(),seekg()是设置读位置,seekp是设置写位置。它们最通用的形式如下:

    istream &seekg(streamoff offset,seek_dir origin);
    ostream &seekp(streamoff offset,seek_dir origin); 

  streamoff定义于 iostream.h 中,定义有偏移量 offset 所能取得的最大值,seek_dir 表示移动的基准位置,是一个有以下值的枚举: 

ios::beg:  文件开头 
ios::cur:  文件当前位置 
ios::end:  文件结尾 
  这两个函数一般用于二进制文件,因为文本文件会因为系统对字符的解释而可能与预想的值不同。

例:

     file1.seekg(1234,ios::cur);//把文件的读指针从当前位置向后移1234个字节
     file2.seekp(1234,ios::beg);//把文件的写指针从文件开头向后移1234个字节 

作者: lirong  2004-10-21 09:45   回复此发言  

2006年04月05日

tellg() ——返回一个int型数值,它表示“内置指针”的当前位置。此函数仅当你在读取一个文件时有效。例如:
   
#include <fstream.h>
     
    void main()
    {
       
// 假如我们已经在test_file.txt中存有了“Hello”的内容
   
    ifstream File("test_file.txt");
     
        char arr[10];
     
        File.read(arr,10);
       
       
// 由于Hello5个字符,因此这里将返回5
   
    cout << File.tellg() << endl;
     
        File.close();
    }
   
   
tellp() —— tellg()有同样的功能,但它用于写文件时。总而言之:当我们读取一个文件,并要知道内置指针的当前位置时,应该使用tellg();当我们写入一个文件,并要知道内置指针的当前位置时,应该使用tellp(). 由于此函数的用法与tellg()完全一样,我就不给出示例代码了。
   
    seekp() ——
还记得seekg()么?当我在读取一个文件,并想到达文件中某个特定位置时,就曾使用过它。seekp()亦如此,只不过它用于写入一个文件的时候。例如,假如我在进行文件读写,而要定位到当前位置的三个字符之前,则需调用FileHandle.seekg(-3). 但如果我是在写入一个文件,并且比如我要重写后5个字符的内容,我就必须往回跳转5个字符,因而,我应该使用FileHandle.seekp(-5) .
   
    ignore() ——
使用于读取文件之时。如果你想略过一定数量的字符,只需使用此函数。实际上,你也可以使用seekg()来代替,然而使用ignore()有一个优点——你可以指定一个特定“界限规则(delimiter rule)”,同样使得ignore()在指定的位置停下。函数原型如下:
   
   
istream& ignore( int nCount, delimiter );
   
   
nCount表示要略过的字符数量,而delimiter —— 与它的名称有着同样的含义:假如你想在文件末尾停下,则可使用EOF值传入,这样一来此函数就等同于seekg();但该参数还可以使用其他值,例如‘\n’这样可以在换行的同时定位在新行处。下面是示例:
   
#include <fstream.h>
     
    void main()
    {
       
// 假设test_file.txt中已经存有"Hello World"这一内容
   
    ifstream File("test_file.txt");
     
        static char arr[10];
     
       
// 假如一直没有遇到字符"l",则向前定位直到跳过6个字符
   
    // 而如果期间遇到"l",则停止向前,定位在该处
   
    File.ignore(6,’l');
     
        File.read(arr,10);
     
        cout << arr << endl;
// 它将显示"lo World!"
     
   
    File.close();
     
    }
   

    getline() ——
虽然前面的章节中我曾提到过这个函数,但还有一些内容我们未曾涉及:此函数不但可用于逐行读取,而且它还可以设为遇到某个特定字符后停止读取。下面给出传递这一参数的方法:
   
   
getline(array,array_size,delim);
   
   
以下为示例代码:
   
   
#include <fstream.h>
     
    void main()
    {
       
// 假设test_file.txt中已经存有"Hello World"这一内容
   
    ifstream File("test_file.txt");
     
        static char arr[10];
     
       
/* 读取,直到满足下面的条件之一:
    1
)已经读取10个字符
    2
)遇到字母"o"
    3
)出现新一行
   
    */
        File.getline(arr,10,’o');
     
        cout << arr << endl;
// 将显示"Hell"
   
    File.close();
    }
   

    peek() ——
此函数将返回输入流文件的下一个字符,但它不移动内置指针。我想你该记得,像get()这样的函数也返回输入流文件的下一个字符,而与此同时它将移动内置指针。所以当你再次调用get()函数的时候,它会返回再下一个字符,而非前面那个。哦,使用peek()也会返回字符,但它不会移动“光标”。所以,假如你连续两次调用peek()函数,它会返回同一个字符。考虑以下代码:
   
   
#include <fstream.h>
     
    void main()
    {
       
// 假设test_file.txt中已经存有"Hello World"这一内容
   
    ifstream File("test_file.txt");
     
        char ch;
     
        File.get(ch);
        cout << ch << endl;
// 将显示"H"
     
        cout <<    char(File.peek()) << endl;
//将显示"e"
        cout <<    char(File.peek()) << endl;
//将再次显示"e"
     
        File.get(ch);
        cout << ch << endl;
// 还是显示"e"
   

    File.close();
       
    }
   

   
顺便说一下,我忘了讲——peek()函数实质上返回的是字符的ASCII码,而非字符本身。因此,假如你想看到字符本身,你得像我在示例中做的那样进行调用(译注:即要转为char类型)
   
    _unlink() ——
删除一个文件。假如你要使用此函数,需要在你的程序中包含io.h头文件。下面是示例代码:
   
   
#include <fstream.h>
    #include <io.h>
     
    void main()
    {
        ofstream File;
     
        File.open("delete_test.txt");
//创建一个文件
   
    File.close();
     
        _unlink("delete_test.txt");
// 删除这个文件
   
 
       
// 试图打开此文件,但假如它已不存在
   
    // 函数将返回一个ios::failbit错误值
   
    File.open("delete_test.txt",ios::nocreate);
     
       
// 验证它是否返回该值
   
    if(File.rdstate() == ios::failbit)
            cout << "Error…!\n";
// 耶,成功了
   
   
File.close();
     
    }
   

    putback() ——
此函数将返回最后一个所读取字符,同时将内置指针移动-1个字符。换言之,如果你使用get()来读取一个字符后再使用putback(),它将为你返回同一个字符,然而同时会将内置指针移动-1个字符,所以你再次使用get()时,它还是会为你返回同样的字符。下面是示例代码:
   
   
#include <fstream.h>
     
    void main()
    {
       
// test_file.txt应包含内容"Hello World"
   
    ifstream File("test_file.txt");
       
        char ch;
     
        File.get(ch);
     
        cout << ch << endl;
// 将显示"H"
     
        File.putback(ch);
        cout << ch << endl;
// 仍将显示"H"
       
        File.get(ch);
        cout << ch << endl;
// 再一次显示"H"
     
        File.close();
    }
   
   
flush() —— 在处理输出流文件的时候,你所存入的数据实际上并非立刻写入文件,而是先放入一个缓冲区中,直到该缓冲区放满数据之后,这些数据才被存入真正的文件中(在你的磁盘上)。旋即缓冲区会被清空,再重新进行下一轮写入。
   
但假如你想在缓冲区写满之前就将其中的数据写入磁盘,则使用flush()函数。只须像这样进行调用:FileHandle.flush(),这样缓冲区内的数据将会写入实际的物理文件,而后缓冲区被清空。
   
再补充一点(高阶的)内容:flush()函数会调用与相应流缓冲(streambuf)相联系的sync()函数(出自MSDN)。
   

在C++中,有一个stream这个类,所有的I/O都以这个“流”类为基础的,包括我们要认识的文件I/O,stream这个类有两个重要的运算符:

1、插入器(<<)
  向流输出数据。比如说系统有一个默认的标准输出流(cout),一般情况下就是指的显示器,所以,cout<<"Write Stdout"<<’\n’;就表示把字符串"Write Stdout"和换行字符(‘\n’)输出到标准输出流。

2、析取器(>>)
  从流中输入数据。比如说系统有一个默认的标准输入流(cin),一般情况下就是指的键盘,所以,cin>>x;就表示从标准输入流中读取一个指定类型(即变量x的类型)的数据。

  在C++中,对文件的操作是通过stream的子类fstream(file stream)来实现的,所以,要用这种方式操作文件,就必须加入头文件fstream.h。下面就把此类的文件操作过程一一道来。

一、打开文件
  在fstream类中,有一个成员函数open(),就是用来打开文件的,其原型是:

void open(const char* filename,int mode,int access);

参数:

filename:  要打开的文件名 
mode:    要打开文件的方式 
access:   打开文件的属性
打开文件的方式在类ios(是所有流式I/O类的基类)中定义,常用的值如下: 

ios::app:   以追加的方式打开文件 
ios::ate:   文件打开后定位到文件尾,ios:app就包含有此属性 
ios::binary:  以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文 
ios::in:    文件以输入方式打开 
ios::out:   文件以输出方式打开 
ios::nocreate: 不建立文件,所以文件不存在时打开失败  
ios::noreplace:不覆盖文件,所以打开文件时如果文件存在失败 
ios::trunc:  如果文件存在,把文件长度设为0 
  可以用“或”把以上属性连接起来,如ios::out|ios::binary

  打开文件的属性取值是:

0:普通文件,打开访问 
1:只读文件 
2:隐含文件 
4:系统文件 
  可以用“或”或者“+”把以上属性连接起来 ,如3或1|2就是以只读和隐含属性打开文件。

  例如:以二进制输入方式打开文件c:\config.sys 

  fstream file1;
  file1.open("c:\\config.sys",ios::binary|ios::in,0);

  如果open函数只有文件名一个参数,则是以读/写普通文件打开,即:

  file1.open("c:\\config.sys");<=>file1.open("c:\\config.sys",ios::in|ios::out,0);

  另外,fstream还有和open()一样的构造函数,对于上例,在定义的时侯就可以打开文件了:

  fstream file1("c:\\config.sys");

  特别提出的是,fstream有两个子类:ifstream(input file stream)和ofstream(outpu file stream),ifstream默认以输入方式打开文件,而ofstream默认以输出方式打开文件。

  ifstream file2("c:\\pdos.def");//以输入方式打开文件
  ofstream file3("c:\\x.123");//以输出方式打开文件

  所以,在实际应用中,根据需要的不同,选择不同的类来定义:如果想以输入方式打开,就用ifstream来定义;如果想以输出方式打开,就用ofstream来定义;如果想以输入/输出方式来打开,就用fstream来定义。

二、关闭文件
  打开的文件使用完成后一定要关闭,fstream提供了成员函数close()来完成此操作,如:file1.close();就把file1相连的文件关闭。

三、读写文件
  读写文件分为文本文件和二进制文件的读取,对于文本文件的读取比较简单,用插入器和析取器就可以了;而对于二进制的读取就要复杂些,下要就详细的介绍这两种方式

  1、文本文件的读写
  文本文件的读写很简单:用插入器(<<)向文件输出;用析取器(>>)从文件输入。假设file1是以输入方式打开,file2以输出打开。示例如下:

  file2<<"I Love You";//向文件写入字符串"I Love You"
  int i;
  file1>>i;//从文件输入一个整数值。 

  这种方式还有一种简单的格式化能力,比如可以指定输出为16进制等等,具体的格式有以下一些

操纵符 功能 输入/输出 
dec 格式化为十进制数值数据 输入和输出 
endl 输出一个换行符并刷新此流 输出 
ends 输出一个空字符 输出 
hex 格式化为十六进制数值数据 输入和输出 
oct 格式化为八进制数值数据 输入和输出 
setpxecision(int p) 设置浮点数的精度位数 输出 

  比如要把123当作十六进制输出:file1<<hex<<123;要把3.1415926以5位精度输出:file1<<setpxecision(5)<<3.1415926。

  2、二进制文件的读写
①put()
  put()函数向流写入一个字符,其原型是ofstream &put(char ch),使用也比较简单,如file1.put(‘c’);就是向流写一个字符’c'。 

②get()
  get()函数比较灵活,有3种常用的重载形式:

  一种就是和put()对应的形式:ifstream &get(char &ch);功能是从流中读取一个字符,结果保存在引用ch中,如果到文件尾,返回空字符。如file2.get(x);表示从文件中读取一个字符,并把读取的字符保存在x中。

  另一种重载形式的原型是: int get();这种形式是从流中返回一个字符,如果到达文件尾,返回EOF,如x=file2.get();和上例功能是一样的。

  还有一种形式的原型是:ifstream &get(char *buf,int num,char delim=’\n’);这种形式把字符读入由 buf 指向的数组,直到读入了 num 个字符或遇到了由 delim 指定的字符,如果没使用 delim 这个参数,将使用缺省值换行符’\n’。例如:

  file2.get(str1,127,’A');//从文件中读取字符到字符串str1,当遇到字符’A'或读取了127个字符时终止。

③读写数据块
  要读写二进制数据块,使用成员函数read()和write()成员函数,它们原型如下:

    read(unsigned char *buf,int num);
    write(const unsigned char *buf,int num);

  read()从文件中读取 num 个字符到 buf 指向的缓存中,如果在还未读入 num 个字符时就到了文件尾,可以用成员函数 int gcount();来取得实际读取的字符数;而 write() 从buf 指向的缓存写 num 个字符到文件中,值得注意的是缓存的类型是 unsigned char *,有时可能需要类型转换。

例:

    unsigned char str1[]="I Love You";
    int n[5];
    ifstream in("xxx.xxx");
    ofstream out("yyy.yyy");
    out.write(str1,strlen(str1));//把字符串str1全部写到yyy.yyy中
    in.read((unsigned char*)n,sizeof(n));//从xxx.xxx中读取指定个整数,注意类型转换
    in.close();out.close(); 

四、检测EOF
  成员函数eof()用来检测是否到达文件尾,如果到达文件尾返回非0值,否则返回0。原型是int eof();

例:  if(in.eof())ShowMessage("已经到达文件尾!");

五、文件定位
  和C的文件操作方式不同的是,C++ I/O系统管理两个与一个文件相联系的指针。一个是读指针,它说明输入操作在文件中的位置;另一个是写指针,它下次写操作的位置。每次执行输入或输出时,相应的指针自动变化。所以,C++的文件定位分为读位置和写位置的定位,对应的成员函数是 seekg()和 seekp(),seekg()是设置读位置,seekp是设置写位置。它们最通用的形式如下:

    istream &seekg(streamoff offset,seek_dir origin);
    ostream &seekp(streamoff offset,seek_dir origin); 

  streamoff定义于 iostream.h 中,定义有偏移量 offset 所能取得的最大值,seek_dir 表示移动的基准位置,是一个有以下值的枚举: 

ios::beg:  文件开头 
ios::cur:  文件当前位置 
ios::end:  文件结尾 
  这两个函数一般用于二进制文件,因为文本文件会因为系统对字符的解释而可能与预想的值不同。

例:

     file1.seekg(1234,ios::cur);//把文件的读指针从当前位置向后移1234个字节
     file2.seekp(1234,ios::beg);//把文件的写指针从文件开头向后移1234个字节 
=================================

在数据从一个对象流动到另一个对象的过程中,无论是否对数据进行缓冲或对数据进行格式变换,这种流动都被抽象为流.
=================================================
cout 标准输出
cerr 标准错误输出,没有缓冲,发给它的内容立即输出
clog 类似于cerr,但有缓冲,缓冲区满时被输出.
====================================================
根据数据的组织形式,文件可以分为文本文件和二进制文件。
文本文件也称ASCII文件,每个字节存放一个ASCII字符。
二进制文件是将数据按在内存中的存储形式存放到磁盘上。

+++++++++++++++++++++++++++++++

再注意一下main函数中最激动人心的那一行:
        myout << myStr << a << "\n";
      
我们知道,最后出现的"\n"可以实现一个换行,不过我们在用C++时教程中总是有意无意地让我们使用endl,两者看上去似乎一样——究竟其中有什么玄妙?查书,书上说endl是一个操纵符(manipulator),它不但实现了换行操作,而且还对输出缓冲区进行刷新。什么意思呢?原来在执行输出操作之后,数据并非立刻传到输出设备,而是先进入一个缓冲区,当适宜的时机(如设备空闲)后再由缓冲区传入,也可以通过操纵符flush进行强制刷新:
cout << "Hello, World! " << "Flush the screen now!!!" << flush;
       这样当程序执行到operator<<(flash)之前,有可能前面的字符串数据还在缓冲区中而不是显示在屏幕上,但执行operator<<(flash)之后,程序会强制把缓冲区的数据全部搬运到输出设备并将其清空。而操纵符endl相当于<< "\n" << flush的简写版本,它先输出一个换行符,再实现缓冲区的刷新。大概这是因为一般的输出都是以换行结尾,而结尾处又是习惯进行刷新的时期,方便起见就把两者结合成了endl。

C++的iostream标准库介绍以及对左移与右移运算符的重载

  我们从一开始就一直在利用C++的输入输出在做着各种练习,输入输出是由iostream库提供的,所以讨论此标准库是有必要的,它与C语言的 stdio库不同,它从一开始就是用多重继承与虚拟继承实现的面向对象的层次结构,作为一个c++的标准库组件提供给程序员使用。


  iostream为内置类型类型对象提供了输入输出支持,同时也支持文件的输入输出,类的设计者可以通过对iostream库的扩展,来支持自定义类型的输入输出操作。

  为什么说要扩展才能提供支持呢?我们来一个示例。

 C++ 代码

#include
#include <iostream>
using namespace std;

class Test
{
public:
Test(int a=0,int b=0)
{
Test::a=a;
Test::b=b;
}
int a;
int b;
};
int main()
{
Test t(100,50);
printf("%???",t);//不明确的输出格式
scanf("%???",t);//不明确的输入格式
cout<<//同样不够明确
cin>>t;//同样不够明确
system("pause");
}



  由于自定义类的特殊性,在上面的代码中,无论你使用c风格的输入输出,或者是c++的输入输出都不是不明确的一个表示,由于c语言没有运算符重载机制,导致stdio库的不可扩充性,让我们无法让printf()和scanf()支持对自定义类对象的扩充识别,而c++是可以通过运算符重载机制扩充 iostream库的,使系统能能够识别自定义类型,从而让输入输出明确的知道他们该干什么,格式是什么。

  在上例中我们之所以用printf与cout进行对比目的是为了告诉大家,C与C++处理输入输出的根本不同,我们从c远的输入输出可以很明显看出是函数调用方式,而c++的则是对象模式,cout和cin是ostream类和istream类的对象

C++中的iostream库主要包含下图所示的几个头文件:



  我们所熟悉的输入输出操作分别是由istream(输入流)和ostream(输出流)这两个类提供的,为了允许双向的输入/输出,由istream和ostream派生出了iostream类。

类的继承关系见下图:




iostream库定义了以下三个标准流对象:

  1.cin,表示标准输入(standard input)的istream类对象。cin使我们可以从设备读如数据。
  2.cout,表示标准输出(standard output)的ostream类对象。cout使我们可以向设备输出或者写数据。
  3.cerr,表示标准错误(standard error)的osttream类对象。cerr是导出程序错误消息的地方,它只能允许向屏幕设备写数据。

  输出主要由重载的左移操作符(<<)来完成,输入主要由重载的右移操作符(>>)完成。

  >>a表示将数据放入a对象中。
  <

这些标准的流对象都有默认的所对应的设备,见下表:



图中的意思表明cin对象的默认输入设备是键盘,cout对象的默认输出设备是显示器屏幕。

那么原理上C++有是如何利用cin/cout对象与左移和右移运算符重载来实现输入输出的呢?

下面我们以输出为例,说明其
实现原理

  cout是ostream类的对象,因为它所指向的是标准设备(显示器屏幕),所以它在iostream头文件中作为全局对象进行定义。

  ostream cout(stdout);//其默认指向的C中的标准设备名,作为其构造函数的参数使用。

  在iostream.h头文件中,ostream类对应每个基本数据类型都有其友元函数对左移操作符进行了友元函数的重载。
  ostream& operator<<(ostream &temp,int source);
  ostream& operator<<(ostream &temp,char *ps);
  。。。。等等

  一句输出语句:cout<<"www.cndev-lab.com";,事实上调用的就是ostream& operator<<(ostream &temp,char *ps);这个运算符重载函数,由于返回的是流对象的引用,引用可以作为左值使用,所以当程序中有类似cout<<"www.cndev- lab.com"<<"中国软件开发实验室";这样的语句出现的时候,就能够构成连续输出。


  由于iostream库不光支持对象的输入输出,同时也支持文件流的输入输出,所以在详细讲解左移与右移运算符重载只前,我们有必要先对文件的输入输出以及输入输出的控制符有所了解。

  和文件有关系的输入输出类主要在fstream.h这个头文件中被定义,在这个头文件中主要被定义了三个类,由这三个类控制对文件的各种输入输出操作,他们分别是ifstream、ofstream、fstream,其中fstream类是由iostream类派生而来,他们之间的继承关系见下图所示。



  由于文件设备并不像显示器屏幕与键盘那样是标准默认设备,所以它在fstream.h头文件中是没有像cout那样预先定义的全局对象,所以我们必须自己定义一个该类的对象,我们要以文件作为设备向文件输出信息(也就是向文件写数据),那么就应该使用ofstream类。

ofstream类的默认构造函数原形为:

ofstream::ofstream(const char *filename,int mode = ios::out,int openprot = filebuf::openprot);

filename:  要打开的文件名
mode:    要打开文件的方式
prot:    打开文件的属性

其中mode和openprot这两个参数的可选项表见下表:

mode属性表

ios::app:   以追加的方式打开文件
ios::ate:   文件打开后定位到文件尾,ios:app就包含有此属性
ios::binary:  以二进制方式打开文件,缺省的方式是文本方式。两种方式的区别见前文
ios::in:    文件以输入方式打开
ios::out:   文件以输出方式打开
ios::trunc:  如果文件存在,把文件长度设为0

可以用“或”把以上属性连接起来,如ios::out|ios::binary。

openprot属性表:

0:普通文件,打开访问
1:只读文件
2:隐含文件
4:系统文件

可以用“或”或者“+”把以上属性连接起来 ,如3或1|2就是以只读和隐含属性打开文件。

示例代码如下:


 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#include <fstream>
using namespace std;

int main()
{
ofstream myfile("c:\\1.txt",ios::out|ios::trunc,0);
myfile<<"中国软件开发实验室"<<<"网址:"<<"WWW.CNDEV-LAB.COM";
myfile.close()
system("pause");
}



文件使用完后可以使用close成员函数关闭文件。


ios::app为追加模式,在使用追加模式的时候同时进行文件状态的判断是一个比较好的习惯。

示例如下:


 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ofstream myfile("c:\\1.txt",ios::app,0);
if(!myfile)//或者写成myfile.fail()
{
cout<<"文件打开失败,目标文件状态可能为只读!";
system("pause");
exit(1);
}
myfile<<"中国软件开发实验室"<<<"网址:"<<"WWW.CNDEV-LAB.COM"<
myfile.close();
}



在定义ifstream和ofstream类对象的时候,我们也可以不指定文件。以后可以通过成员函数open()显式的把一个文件连接到一个类对象上。

例如:

 C++ 代码

//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
ofstream myfile;
myfile.open("c:\\1.txt",ios::out|ios::app,0);
if(!myfile)//或者写成myfile.fail()
{
cout<<"文件创建失败,磁盘不可写或者文件为只读!";
system("pause");
exit(1);
}
myfile<<"中国软件开发实验室"<<<"网址:"<<"WWW.CNDEV-LAB.COM"<
myfile.close();
}


  下面我们来看一下是如何利用ifstream类对象,将文件中的数据读取出来,然后再输出到标准设备中的例子。

代码如下:


 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
ifstream myfile;
myfile.open("c:\\1.txt",ios::in,0);
if(!myfile)
{
cout<<"文件读错误";
system("pause");
exit(1);
}
char ch;
string content;
while(myfile.get(ch))
{
content+=ch;
cout.put(ch);//cout<<>
}
myfile.close();
cout<
system("pause");
}



  上例中,我们利用成员函数get(),逐一的读取文件中的有效字符,再利用put()成员函数,将文件中的数据通过循环逐一输出到标准设备(屏幕)上, get()成员函数会在文件读到默尾的时候返回假值,所以我们可以利用它的这个特性作为while循环的终止条件,我们同时也在上例中引入了C++风格的字符串类型string,在循环读取的时候逐一保存到content中,要使用string类型,必须包含string.h的头文件。
  我们在简单介绍过ofstream类和ifstream类后,我们再来看一下fstream类,fstream类是由iostream派生而来,fstream类对象可以同对文件进行读写操作。


 C++ 代码
示例代码如下:

//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
#include <fstream>
using namespace std;
int main()
{
fstream myfile;
myfile.open("c:\\1.txt",ios::out|ios::app,0);
if(!myfile)
{
cout<<"文件写错误,文件属性可能为只读!"<
system("pause");
exit(1);
}
myfile<<"中国软件开发实验室"<<<"网址:"<<"WWW.CNDEV-LAB.COM"<
myfile.close();

myfile.open("c:\\1.txt",ios::in,0);
if(!myfile)
{
cout<<"文件读错误,文件可能丢失!"<
system("pause");
exit(1);
}
char ch;
while(myfile.get(ch))
{
cout.put(ch);
}
myfile.close();
system("pause");
}




  由于fstream类可以对文件同时进行读写操作,所以对它的对象进行初始话的时候一定要显式的指定mode和openprot参数。


  接下来我们来学习一下串流类的基础知识,什么叫串流类
  简单的理解就是能够控制字符串类型对象进行输入输出的类,C++不光可以支持C++风格的字符串流控制,还可以支持C风格的字符串流控制。

  我们先看看看C++是如何对C风格的字符串流进行控制的,C中的字符串其实也就是字符数组,字符数组内的数据在内存中的位置的排列是连续的,我们通常用 char str[size]或者char *str的方式声明创建C风格字符数组,为了能让字符数组作为设备并提供输入输出操作,C++引入了ostrstream、istrstream、 strstream这三个类,要使用他们创建对象就必须包含strstream.h头文件。
  istrstream类用于执行C风格的串流的输入操作,也就是以字符串数组作为输入设备。
  ostrstream类用于执行C风格的串流的输出操作,也就是一字符串数组作为输出设备。
  strstream类同时可以支持C风格的串流的输入输出操作。

  istrstream类是从istream(输入流类)和strstreambase(字符串流基类)派生而来,ostrstream是从 ostream(输出流类)和strstreambase(字符串流基类)派生而来,strstream则是从iostream(输入输出流类)和和 strstreambase(字符串流基类)派生而来。

他们的继承关系如下图所示:



  串流同样不是标准设备,不会有预先定义好的全局对象,所以不能直接操作,需要通过构造函数创建对象。

  类istrstream的构造函数原形如下:
  istrstream::istrstream(const char *str,int size);
  参数1表示字符串数组,而参数2表示数组大小,当size为0时,表示istrstream类对象直接连接到由str所指向的内存空间并以\0结尾的字符串。

下面的示例代码就是利用istrstream类创建类对象,制定流输入设备为字符串数组,通过它向一个字符型对象输入数据。

代码如下:


 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
#include <strstream>
using namespace std;
int main()
{
char *name = "www.cndev-lab.com";
int arraysize = strlen(name)+1;
istrstream is(name,arraysize);
char temp;
is>>temp;
cout<
system("pause");
}

  类ostrstream用于执行C风格的串流的输出,它的构造函数如下所示:

  ostrstream::ostrstream(char *_Ptr,int streamsize,int Mode = ios::out);

  第一个参数是字符数组,第二个是说明数组的大小,第三个参数是指打开方式。

我们来一个示例代码:


 C++ 代码

//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#include <iostream>
#include <strstream>
using namespace std;
int main()
{
int arraysize = 1;
char *pbuffer = new char[arraysize];
ostrstream ostr(pbuffer,arraysize,ios::out);
ostr<<//使用ostrstream输出到流对象的时候,要用ends结束字符串
cout<
delete[] pbuffer;
system("pause");
}



  上面的代码中,我们创建一个c风格的串流输出对象ostr,我们将arraysize内的数据成功的以字符串的形式输出到了ostr对象所指向的pbuffer指针的堆空间中,pbuffer也正是我们要输出的字符串数组,在结尾要使用ends结束字符串,如果不这么做就有溢出的危险。


  接下来我们继续看一下C++风格的串流控制,C++引入了ostringstream、istringstream、stringstream这三个类,要使用他们创建对象就必须包含sstream.h头文件。
  istringstream类用于执行C++风格的串流的输入操作。
  ostringstream类用于执行C++风格的串流的输出操作。
  stringstream类同时可以支持C++风格的串流的输入输出操作。

  istringstream类是从istream(输入流类)和stringstreambase(c++字符串流基类)派生而来, ostringstream是从ostream(输出流类)和stringstreambase(c++字符串流基类)派生而来, stringstream则是从iostream(输入输出流类)和和stringstreambase(c++字符串流基类)派生而来。

他们的继承关系如下图所示:



  istringstream是由一个string对象构造而来,istringstream类从一个string对象读取字符。
  istringstream的构造函数原形如下:
  istringstream::istringstream(string str);

示例代码如下:

 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
istringstream istr;
istr.str("1 56.7",);
//上述两个过程可以简单写成 istringstream istr("1 56.7");
cout << istr.str()<
int a;
float b;
istr>>a;
cout<<
istr>>b;
cout<<
system("pause");
}



  上例中,构造字符串流的时候,空格会成为字符串参数的内部分界,例子中对a,b对象的输入"赋值"操作证明了这一点,字符串的空格成为了整型数据与浮点型数据的分解点,利用分界获取的方法我们事实上完成了字符串到整型对象与浮点型对象的拆分转换过程。
  str()成员函数的使用可以让istringstream对象返回一个string字符串(例如本例中的输出操作(cout<


  ostringstream同样是由一个string对象构造而来,ostringstream类向一个string插入字符。
  ostringstream的构造函数原形如下:
  ostringstream::ostringstream(string str);

示例代码如下:


 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main()
{
ostringstream ostr;
//ostr.str("abc");//如果构造的时候设置了字符串参数,那么增长操作的时候不会从结尾开始增加,而是修改原有数据,超出的部分增长
ostr.put(‘d’);
ostr.put(‘e’);
ostr<<"fg";

string gstr = ostr.str();
cout<
system("pause");
}



  在上例代码中,我们通过put()或者左移操作符可以不断向ostr插入单个字符或者是字符串,通过str()函数返回增长过后的完整字符串数据,但值得注意的一点是,当构造的时候对象内已经存在字符串数据的时候,那么增长操作的时候不会从结尾开始增加,而是修改原有数据,超出的部分增长。

  对于stringstream了来说,不用我多说,大家也已经知道它是用于C++风格的字符串的输入输出的。
  stringstream的构造函数原形如下:

  stringstream::stringstream(string str);

 C++ 代码
示例代码如下:

//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

int main()
{
stringstream ostr("ccc");
ostr.put(‘d’);
ostr.put(‘e’);
ostr<<"fg";
string gstr = ostr.str();
cout<<

char a;
ostr>>a;
cout<

system("pause");
}



  除此而外,stringstream类的对象我们还常用它进行string与各种内置类型数据之间的转换。

示例代码如下:


 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
#include <sstream>
#include <string>
using namespace std;

int main()
{
stringstream sstr;
//——–int转string———–
int a=100;
string str;
sstr<
sstr>>str;
cout<<
//——–string转char[]——–
sstr.clear();//如果你想通过使用同一stringstream对象实现多种类型的转换,请注意在每一次转换之后都必须调用clear()成员函数。
string name = "colinguan";
char cname[200];
sstr<
sstr>>cname;
cout<
system("pause");
}


  接下来我们来学习一下输入/输出的状态标志的相关知识,C++中负责的输入/输出的系统包括了关于每一个输入/输出操作的结果的记录信息。这些当前的状态信息被包含在io_state类型的对象中。io_state是一个枚举类型(就像open_mode一样),以下便是它包含的值。

goodbit 无错误

Eofbit 已到达文件尾

failbit 非致命的输入/输出错误,可挽回

badbit 致命的输入/输出错误,无法挽回


  有两种方法可以获得输入/输出的状态信息。一种方法是通过调用rdstate()函数,它将返回当前状态的错误标记。例如,假如没有任何错误,则rdstate()会返回goodbit.

下例示例,表示出了rdstate()的用法:


 C++ 代码

//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#include <iostream>
using namespace std;

int main()
{
int a;
cin>>a;
cout<<cin.rdstate()<
if(cin.rdstate() == ios::goodbit)
{
cout<<"输入数据的类型正确,无错误!"<
}
if(cin.rdstate() == ios_base::failbit)
{
cout<<"输入数据类型错误,非致命错误,可清除输入缓冲区挽回!"<
}
system("pause");
}



另一种方法则是使用下面任何一个函数来检测相应的输入/输出状态:

bool bad();

bool eof();

bool fail();

bool good();

下例示例,表示出了上面各成员函数的用法:


 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#include <iostream>
using namespace std;

int main()
{
int a;
cin>>a;
cout<<cin.rdstate()<
if(cin.good())
{
cout<<"输入数据的类型正确,无错误!"<
}
if(cin.fail())
{
cout<<"输入数据类型错误,非致命错误,可清除输入缓冲区挽回!"<
}
system("pause");
}


  如果错误发生,那么流状态既被标记为错误,你必须清除这些错误状态,以使你的程序能正确适当地继续运行。要清除错误状态,需使用clear()函数。此函数带一个参数,它是你将要设为当前状态的标志值。,只要将ios::goodbit作为实参。

示例代码如下:


 C++ 代码

//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#include <iostream>
using namespace std;

int main()
{
int a;
cin>>a;
cout<<cin.rdstate()<
cin.clear(ios::goodbit);
cout<<cin.rdstate()<
system("pause");
}



  通常当我们发现输入有错又需要改正的时候,使用clear()更改标记为正确后,同时也需要使用get()成员函数清除输入缓冲区,以达到重复输入的目的。

示例代码如下:

 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
using namespace std;

int main()
{
int a;
while(1)
{
cin>>a;
if(!cin)//条件可改写为cin.fail()
{
cout<<"输入有错!请重新输入"<
cin.clear();
cin.get();
}
else
{
cout<
break;
}
}
system("pause");
}



最后再给出一个对文件流错误标记处理的例子,巩固学习,代码如下:


 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
#include <fstream>
using namespace std;

int main()
{
ifstream myfile("c:\\1.txt",ios_base::in,0);
if(myfile.fail())
{
cout<<"文件读取失败或指定文件不存在!"<
}
else
{
char ch;
while(myfile.get(ch))
{
cout<
}
if(myfile.eof())
{
cout<<"文件内容已经全部读完"<
}
while(myfile.get(ch))
{
cout<
}
}
system("pause");
}


  C语言提供了格式化输入输出的方法,C++也同样,但是C++的控制符使用起来更为简单方便,在c++下有两中方法控制格式化输入输出。
  1.有流对象的成员函数。
  例如,下列程序以成员函数的方式控制输出的精度:


 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#include <iostream>
using namespace std;

int main()
{
float pi=3.14159f;
cout<<
cout.precision(2);
cout<<
system("pause");
}



  2.使用C++输入输出控制符,控制符是在拖文件iomanip.h中定义的对象,与成员函数有一样的效果,控制符不必像成员函数学那样单独调用,它可以直接插入流中使用。
  例如,下列程序以控制符的方式控制输出的精度:

 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
float pi=3.14159f;
cout<<
cout<
cout<<
system("pause");
}



  下表我们列出了一些比较常用的控制符号,由于篇幅有限读者请根据自己的需要查阅相关书籍:

  对于iostream标准库来说包含了众多的成员函数,各函数都有其自身的作用,篇幅问题笔者在这里不能一一说明例举,由于标准输入对象cin 提供输入的时候会自动以空格作为分界,给我们获取一行带有空格的完整字符串带来了困难,在这里补充一个非常用有的成员函数----getline()。

  其函数原型为:
  getlin(chiar *str,int size,char=’\n’);

  第一个参数是字符数组,用于存放整行文本,第二个参数读取的最大字符个数,第三个参数为作为分界界限的字符,默认识是\n,换行符。

示例代码如下:

 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#include <iostream>
#include <iomanip>
using namespace std;

int main()
{
char str[100];
cin.getline(str,sizeof(str),’\n’);
cout<<
system("pause");
}


  通过上面内容的学习,我们对i/o有了一些基本点基本的认识,现在是该切入正题的时候了,详细学习一下,如何重载左移与右移操作符。

  先说左移(<<)操作符,也就是我们常说的输出操作符
  对于自定义类来说,重载左移操作符的方法我们常使用类的友元方式进行操作。

示例代码如下:


 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#include <iostream>
using namespace std;

class Test
{
public:
Test(int age = 0,char *name = "\0")
{
Test::age = age;
strcpy(Test::name,name);
}
void outmembers(ostream &out)
{
out<<"Age:"<<<<"NAME:"<<this->name<
}
friend ostream& operator <<(ostream& ,Test&);
protected:
int age;
char name[50];
};
ostream& operator <<(ostream& out,Test &temp)
{
temp.outmembers(out);
return out;
}
int main()
{
Test a(24,"管宁");
cout<
system("pause");
}



  上例代码中,我们对void outmembers(ostream &out)的参数使用ostream定义主要是为了可以向它传递任何ostream类对象不光是cout也可以是ofstrem或者是 ostrstream和ostringstream类对象,做到通用性。

  重载运算符,我们知道可以是非成员方式也可以是成员方式的,对于<<来说同样也可以是成员方式,但我十分不推荐这么做,因为对于类的成员函数来说,第一个参数始终是会被隐藏的,而且一定是当前类类型的。

  下面的示例代码就是将上面的<<重载函数修改成成员方式的做法:


 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者

#include <iostream>
using namespace std;

class Test
{
public:
Test(int age = 0,char *name = "\0")
{
Test::age = age;
strcpy(Test::name,name);
}
void outmembers(ostream &out)
{
out<<"Age:"<<<<"NAME:"<<this->name<
}
ostream& operator <<(ostream &out)
{
this->outmembers(out);
return out;
}
protected:
int age;
char name[50];
};
int main()
{
Test a(24,"管宁");
a<<cout;
system("pause");
}



  从代码实现上,我们将函数修改成了ostream& operator <<(ostream &out),迫不得已将ostream类型的引用参数放到了后面,这是因为,成员方式运算符重载函数第一个参数会被隐藏,而且一定是当前类类型的,这和ostream类型冲突了。由此我们在使用cout输出的时候就必须写成a<

  为了巩固学习,下面我们以fstream对象输出为例做一个练习。

代码如下:

 C++ 代码
//程序作者:管宁
//站点:www.cndev-lab.com
//所有稿件均有版权,如要转载,请务必著名出处和作者
#include <iostream>
#include <fstream>
using namespace std;

class Test
{
public:
Test(int age = 0,char *name = "\0")
{
Test::age = age;
strcpy(Test::name,name);
}
void outmembers(ostream &out)
{
out<<"Age:"<<<<"NAME:"<<this->name<
}
friend ostream& operator <<(ostream& ,Test&);
protected:
int age;
char name[50];
};
ostream& operator <<(ostream& out,Test &temp)
{
temp.outmembers(out);
return out;
}
int main()
{
Test a(24,"管宁");
ofstream myfile("c:\\1.txt",ios::out,0);
if (myfile.rdstate() == ios_base::goodbit)
{
myfile<
cout<<"文件创建成功,写入正常!"<
}
if (myfile.rdstate() == ios_base::badbit)
{
cout<<"文件创建失败,磁盘错误!"<
}
system("pause");
}

 
     对于左移运算符重载函数来说,由于不推荐使用成员方式,那么使用非成员方式在类有多重继承的情况下,就不能使用虚函数进行左移运算符重载的区分,为了达到能够区分显示的目的,给每个类分别添加不同的虚函数是必要的。 示例代码如下:  C++ 代码 //程序作者:管宁 //站点:www.cndev-lab.com //所有稿件均有版权,如要转载,请务必著名出处和作者 #include #include using namespace std; class Student { public: Student(int age = 0,char *name = "\0") { Student::age = age; strcpy(Student::name,name); } virtual void outmembers(ostream &out) = 0; friend ostream& operator <<(ostream& ,Student&); protected: int age; char name[50]; }; ostream& operator <<(ostream& out,Student &temp) { temp.outmembers(out); return out; } class Academician:public Student { public: Academician(int age = 0,char *name = "\0",char *speciality="\0"):Student(age,name) { strcpy(Academician::speciality,speciality); }   对于左移运算符重载函数来说,由于不推荐使用成员方式,那么使用非成员方式在类有多重继承的情况下,就不能使用虚函数进行左移运算符重载的区分,为了达到能够区分显示的目的,给每个类分别添加不同的虚函数是必要的。

示例代码如下:


 C++ 代码


#include <iostream>
#include <fstream>
using namespace std;

class Student
{
public:
Student(int age = 0,char *name = "\0")
{
Student::age = age;
strcpy(Student::name,name);
}
virtual void outmembers(ostream &out) = 0;
friend ostream& operator <<(ostream& ,Student&);
protected:
int age;
char name[50];
};
ostream& operator <<(ostream& out,Student &temp)
{
temp.outmembers(out);
return out;
}
class Academician:public Student
{
public:
Academician(int age = 0,char *name = "\0",char *speciality="\0"):Student(age,name)
{
strcpy(Academician::speciality,speciality);
}
&
2006年03月24日
Makefile的编写方法
#############################################################################
# Makefile
#############################################################################

####### Compiler, tools and options
CXX             = g++
CPPFLAGS        = -pipe -Wall -W -pthread -g -D_REENTRANT
INCPATH         = .
LIBS            =
EXECUTABLE      =
RM                      = rm -rf
SRCS            = $(wildcard *.cc)
OBJS            = $(patsubst %.cc,%.o,$(SRCS))
DEPS            = $(patsubst %.o,%.d,$(OBJS))
CPPFLAGS        += -MMD $(addprefix -I, $(INCPATH))
MISSING_DEPS = $(filter-out $(wildcard $(DEPS)),$(DEPS))
MISSING_DEPS_SRCS = $(wildcard $(patsubst %.d,%.cc,$(MISSING_DEPS)))
    
.PHONY : everything deps objs clean veryclean rebuild
    
everything : $(EXECUTABLE)
    
deps : $(DEPS)
    
objs : $(OBJS)
    
clean :
        @$(RM) $(OBJS)
        @$(RM) $(DEPS)
    
veryclean: clean
        @$(RM) $(EXECUTABLE)
    
rebuild: veryclean everything
    
ifneq ($(MISSING_DEPS),)
$(MISSING_DEPS):
        @$(RM) $(patsubst %.d,%.o,$@)
endif
    
-include $(DEPS)

$(EXECUTABLE) : $(OBJS)
        $(CXX) -o $(EXECUTABLE) $(OBJS) $(addprefix -l,$(LIBS))


说明:
1,CPPFLAGS里面的-MMD是针对gnu的compilers的,跟-MD(其他compilers)的区别是-MMD去掉了depends里面的系统的头文件(使用#include 的头文件)

2,@$(RM) $(OBJS),这里的@是让编译的时候不显示这条命令,gnu make默认是显示命令的

3,-include $(DEPS),前面的-号是为了忽略当后面的文件不存在的errors

4,.PHONY : everything deps objs clean veryclean rebuild,这句是为了让make不检测:后面的项是否存在同名的文件

Vi使 用 说 明

—- Vi 简 介

—- vi 做为Linux 系统中一种编辑器,它的使用方法和界面与 Unix 平台十分相似。在 Linux 中有很多功能强大,界面友好的编辑器,但是vi 还是值得你学习它的使用方法。初步接触 VI,你觉得它的界面不太友好,不容易掌握,可是一旦你掌握了vi 的命令,你可以感觉到它强大的功能与高效。而且vi 相对来说较小,无论你使用任何 Linux系统,你总是可以使用vi的。在很多系统中,可能只有 vi 供你选择。

—- 进 入 与 离 开

—- 要 进 入 vi 可 以 直 接 在 系 统 提 示 字 符 下 键 入 vi < 文 件 名 称 >,vi 可 以 自 动 帮 你 载 入 所 要 编 辑 的 文 件 或 是 开 启 一 个 新 文 件。 进 入 vi 後 屏 幕 左 方 会 出 现 波 浪 符 号, 凡 是 列 首 有 该 符 号 就 代 表 此 列 目 前 是 空 的。 要 离 开 vi 可 以 在 指 令 模 式 下 键 入 :q, :wq 指 令 则 是 存 档 後 再 离 开( 注 意 冒 号)。 要 切 换 到 指 令 模 式 下 则 是 用 [ESC] 键, 如 果 不 晓 得 现 在 是 处 於 什 麽 模 式, 可 以 多 按 几 次 [ESC], 系 统 会 发 出 哔 哔 声 以 确 定 进 入 指 令 模 式。

—- Vi 输 入 模 式

—- 如 何 输 入 资 料 呢 ? 有 好 几 个 指 令 可 以 进 入 输 入 模 式:

新增 (append)
—- a 从 光 标 所 在 位 置 後 面 开 始 新 增 资 料, 光 标 後 的 资 料 随 新 增 资 料 向 後 移 动。
—- A 从 光 标 所 在 列 最 後 面 的 地 方 开 始 新 增 资 料。

插 入 (insert)
—- i 从 光 标 所 在 位 置 前 面 开 始 插 入 资 料, 光 标 後 的 资 料 随 新 增 资 料 向 後 移 动。
—- I 从 光 标列 的 第 一 个 非 空 白 字 符 前 面 开 始 插 入 资 料。

开 始 (open)
—- o 在 光 标 所 在 列 下 新 增 一 列 并 进 入 输 入 模 式。
—- O 在 光 标 所 在 列 上 方 新 增 一 列 并 进 入 输 入 模 式。

—- - 删 除 与 修 改

—- 何 谓 编 辑 ? 在 这 里 我 们 认 为 是 文 字 的 新 增 修 改 以 及 删 除, 甚 至 包 括 文 字 区 块 的 搬 移、 复 制 等 等。 这 里 先 介 绍 vi 的 如 何 做 删 除 与 修 改。( 注 意: 在 vi 的 原 始 观 念 里, 输 入 跟 编 辑 是 两 码 子 事。 编 辑 是 在 指 令 模 式 下 操 作 的, 先 利 用 指 令 移 动 光 标 来 定 位 要 进 行 编 辑 的 地 方, 然 後 才 下 指 令 做 编 辑。)
—- x 删 除 光 标 所 在 字 符。
—- dd 删 除 光 标 所 在 的 列。
—- r 修 改 光 标 所 在 字 符,r 後 接 著 要 修 正 的 字 符。
—- R 进 入 取 代 状 态, 新 增 资 料 会 覆 盖 原 先 资 料, 直 到 按 [ESC] 回 到 指 令 模 式 下 为 止。
—- s 删 除 光 标 所 在 字 符, 并 进 入 输 入 模 式。
—- S 删 除 光 标 所 在 的 列, 并 进 入 输 入 模 式。

—-
这里列出 vi 中较常用的操作命令。

  1. 输入模式命令
  2. 输入模式命令光标移动
  3. 输入模式命令删除操作
  4. 改变与替换
  5. 查询命令
  6. 拷贝与粘贴
  7. 文件保存及退出vi


输入模式命令


进入输入输出的方法

命令 作用
<a> 在光标后输入文本
<A> 在当前行末尾输入文本
<i> 在光标前输入文本
<I> 在当前行开始输入文本
<o> 在当前行后输入新一行
<O> 在当前行前输入新一行

光标移动命令


光标移动

命令 作用
<b> 移动到当前单词的开始
<e> 移动到当前单词的结尾
<w> 向前移动一个单词
<h> 向前移动一个字符
<j> 向上移动一行
<k> 向下移动一行
<l> 向后移动一个字符

删除操作命令


删除操作

命令 作用
<x> 删除光标所在的字符
<dw> 删除光标所在的单词
<d$> 删除光标至行尾的所有字符
<D> 同<d$>
<dd> 删除当前行


  可在删除命令前加上数字,如<5x>表示删除5行。

改变与替换操作命令


改变与替换操作

命令 作用
<r> 替换光标所在的字符
<R> 替换字符序列
<cw> 替换一个单词
<ce> 同<cw>
<cb> 替换光标所在的前一字符
<c$> 替换自光标位置至行尾的所有字符
<C> 同<c$>
<cc> 替换当前行

查询命令


查询

命令 作用
</abc> 向前查询abc
<?abc> 向后查询abc
<n> 向前继续查询
<N> 向后继续查询

拷贝与粘贴命令


拷贝与粘贴

命令 作用
</yw> 将光标所在单词拷入剪贴板
<y$> 将光标至行尾的字符拷入剪贴板
<Y> 同<y$>
<yy> 将当前行拷入剪贴板
<p> 将剪贴板中的内容粘贴在光标后
<P> 将剪贴板中的内容粘贴在光标前

文件保存及退出vi命令


文件保存及退出vi

命令 作用
<:q> 不包存退出
<:q!> 不保存强制性退出
<:w> 保存编辑
<:w filename> 存入文件 filename 中
<:w! filename> 强制性存入文件 filename 中
<:wq> 保存退出
<:x> 同 <:wq>
<ZZ> 同 <:wq>
2006年03月10日

历史最傻求人法——千万别犯这个缺

  大多数人骨子里少不了古道热心肠,朋友有难,必两肋插刀,或者人家实在求到自己头上,磨不开面子也就硬着头皮上了。只是有的人不知道缺根筋还是怎的,求人的法子总让人心里不爽,能帮的也懒得帮了。

  给枣型选手

  “忘了在什么场合认识的那家伙了,关系很一般。他倒是很有心‘经营友情’的样子,隔上两三个月就会打来个电话叙叙旧,或者约个时间吃顿饭什么的。慢慢地我摸出了一个规律,其实平常他也是想不起我的,但只要他给我打过电话联络感情,过不了两天,肯定会趁热打铁,要我帮忙做点什么事情。后来我实在忍不住跟他说,大家都是朋友,真有什么事情求我,开门见山就是了,用不着这样事先做满功课,好像我多难办似的。况且越这样我越觉得你功利。”

  突击型选手

  “无事不登三宝殿,大张就是,十几年前的老同学了,初中毕业之后就没什么来往,可他倒是记着我电话,而且想起来给我打电话一准是有事让我帮忙,还都特急,一看就是求了半天别人帮不上忙才想起我来了。真讨厌。”

  理所当然型选手

  “说不清楚为什么,每次替他做了事心里都特别堵得慌,好像欠他什么没还似的。因为沾点血缘关系,所以他永远是一副理所当然的样子——我的忙,你必须帮,因为你跟我是亲戚。因为这种‘应当应分’的心理作祟,无论他提出什么麻烦的要求我都必须在第一时间里应允,必须在规定时间里完成得漂亮,不然他就一下子拉长了脸,下巴掉到脚面上转身走人。”

  翻旧账型选手

  “几年前一桩生意闹得我自己周转不灵,情急之下向他借了一大笔钱,过后一年多才还清。谁知道这人情债却一辈子也还不清了。从那之后,我就成了他的贴身侍卫,有什么疑难问题都要我去解决,一旦我提出有什么困难,他就会翻出当年的旧账,说你看我在你危急的时候是怎么帮你的。真是……唉。”

  翻脸型选手

  “上个月他的爸爸得病住院,让我帮忙找床位约医生,我使尽了全身解数,终于让老人住进这家永远‘客满’的医院,但是做手术只能排号了。后来他又找了我另一个同学,那同学同样很卖力,打通人脉关系,找到卫生局长给院长写了信,老人很快被推进了手术室——于是,他的态度完全变了,虽然我依然在医院里跑前跑后,但看到的却是他的冷脸,他还居然当着我的面对我同学说,早知道求你就好了。我是能力所限,但是已经尽力了,至于这样甩脸子吗?”

  求人蛤蟆功

  第一项——人脉网

  一只老年青蛙和一只老年蜘蛛的区别是什么?回答:老年青蛙腿脚不灵便,无法再像青年时一样蹦着捉虫子,而老年蜘蛛却可以舒舒服服地在网上趴着,自有冤大头撞上来。

  青蛙仅发挥了本身能量,所以生活艰辛,但蜘蛛除了本身的能量之外,更借助了外延能量,所以生活轻松。如果不能像蜘蛛一样有一张庞大结实的人脉关系网,一旦需要人帮忙,你很可能就变成了“突击型选手”或者“给枣型选手”。如果网大,即便朋友帮不上忙,朋友的朋友或许能帮上忙。下面是如何“张网”的几大秘诀——

  A. 不要与人失去联络。“突击型选手”和“给枣型选手”之所以惹人厌烦,是因为他们平时没有和朋友保持联络,或者有事情了才事先预热一把。当你半年都没有与一个朋友联络的时候,你很可能就快失去他了。试着每天打5到10个电话吧,不但可以维系旧情谊,还可以扩张自己的“情面”。一天打10个电话,一个月就是300个,平均一下,你的人际网络每个月至少有十来个人可以为你拔刀相助了。

  B. 选好时机。有的人是“大忙人”,白天打电话给他,他在开会,晚上他在应酬,夜里当然不妥,可能在鬼混。傍晚是比较合适的时机,刚好是这些工作狂下班后晚饭前的时间,见缝插针吧。

  C. 别拿豆包不当干粮。鸡鸣狗盗之徒尚能助人成就大事,不要因为某人看似实力淡薄就不予理睬,保安还知道通往老板办公室的消防通道呢。

  D. 情报无所不在。街头、饭店大厅、机场、出租车停泊站、酒吧、沙龙,处处都有最新情报,跟人谈上一两个小时,说不定就能拉到有用的人员。

  E. 记录“关系”进展。如果网足够大,那么就需要像写日记一样记录一下——姓名、联系方式、你的看法以及日后的联络之道。这样便于以后找出“人尖儿”。

  F. 不要急于求成。做推销的很讨厌,因为他希望你在很短的时间内买下他的全部货品。拓展关系也是如此,如果盲目地向前冲,别人一下子就会看出你的“不择手段”,只能离你越来越远。记住,织网,是个力气活,长线活。

  第二项——好形象

  如果您长着一副人见人烦的模样,恐怕多小的事情,求多瓷的瓷器,也难办成事。当然,这是极少数。不过,讲究仪表确实是求人前的必要准备。想想看,一个衣冠不整、邋里邋遢的人和一个装束典雅、整洁利落的人在其他条件差不多的情况之下,同去求一个人,谁会得到善待,谁会得到冷落呢?

  别钻牛角尖,好形象不仅仅指你穿什么衣服戴什么帽子,还有一系列配套的包装。举个例子,某公司的老板希望从一个大公司那里接下一个项目,大公司老板心里有点忐忑,生怕这小公司财力不够弄砸他的买卖,于是带着财务总监来小公司打探虚实。小公司老板很有心计,开了辆“悍马”前来接人,大老板一看,呵,小老板都开悍马了,看来公司还是有一定实力的,这生意一来二去的就谈成了。当然,假如大老板身高不足1.6米,你弄辆悍马他爬半天爬不上去,肯定会心里暗骂你捉弄他。如此说来包装形象很重要,你的形象对不对他胃口,更重要。

  第三项——察言观色

  俗话说:“出门观天色,进门看脸色。”别傻了吧唧的在人家心情不好的时候麻烦人家帮你做事情。一定要注意对方所处状态,也要注意对方的情绪和心理需要。

  比如,如果他一边跟你说话一边接电话、看手表,那一定是有很急的事情,此刻不要把你求他办的事情说出来。

  比如,当对方情绪低落,但依旧很热情地跟你说“对不起,今天我心情不好,不过,你说罢……”那么此时无论他多热情,你也最好换个时机说事。

  再比如,如果你求他的事情过了些时日仍没有答复,那就不要再请求了,等过两天时机对了再点一点,这样多重复几次之后,对方也就容易接受你的请求了。

  察言观色除了揣摩对方的情绪心态以外,还有重要的一条就是不能犯忌。如果犯了对方的忌讳,恐怕本该成的事情也难办了。

  性格外向喜欢交际的人,在办公室里跟他们谈事情,一般没什么问题。而那些内向、胆小怕事、敏感多疑的人就很难说产生什么副作用了。所以对于这些人,换个没人的地方“私下解决”,比较容易达到目的。

  还有,千万别一根筋似的闷头谈自己的事情,并且不停地说“麻烦您、拜托、求您帮忙”,这样会让人有种强烈的厌恶感。假如想把自己的请求向对方说明,就应该先摆出愿意听取对方讲话的姿态来,让人看到你有倾听别人言谈的诚意,他才会愿意听你说话。