分类存档: Program

Opencv的opencv_haartraining和opencv_traincascade训练

网上一大堆Opencv的opencv_haartraining和opencv_traincascade训练教程,我试着用了一下,别人的经验多少有些问题,我把我的使用经验和大家分享一下。我的环境如下:

操作系统:Archlinux 4.1.3-1,Opencv版本:2.4.11-1

1.训练先要准备正样本和负样本。

正样本就是要识别的目标图像了,我是手动一个一个的抠出来的,长宽大致相同就可以了,我把正样本存在/home/foo/pos目录下,一共140个文件。负样本就是不含目标的图像,我的负样本保存在/home/foo/neg目录下,一共900个。网上有人说图像文件名称最好是普通的名称复杂的汉字或者符号有可能opencv识别不了,我乖乖的起了很简单的文件名。此外还要准备正、负样本的清单。

/home/foo/pos/posdata.txt文件是正样本文件清单,手动建立该文件,格式如下

1.jpg 1 0 0 200 199

2.jpg 1 0 0 180 190

其中1.jpg是正样本文件名,1是图像中目标个数,0,0 是目标在图像中的左上角,200,199是目标在图像中的右下角。手动添加很累,我是做了个小程序自动生成的。

/home/foo/neg/negdata.txt文件是负样本文件清单,手动建立该文件,格式如下

1.jpg

2.jpg

其中1.jpg是负样本文件名

2.生成vec文件。

准备好样本图像需要使用opencv_createsamples生成.vec文件,就是将这个样本图像进行大小之类的调整,加速训练过程。vec文件生成命令如下:

opencv_createsamples -info pos/posdata.txt  -bg neg/negdata.txt -num 140 -w 24 -h 24 -vec myvec_24.vec

在/home/foo目录下运行该命令,运行后/home/foo目录下会出现myvec_24.vec文件。

-info pos/posdata.txt是指定正样本清单为pos/posdata.txt

-bg neg/negdata.txt是指定负样本清单为neg/negdata.txt

-num 140是指定正样本图像个数为140个

-w 24 -h 24是指将正样本尺寸转换为宽24x高24大小,如果样本过大那么训练过程会很慢的

-vec myvec_24.vec指定vec文件名称为myvec_24.vec

3.使用opencv_traincascade训练

在/home/foo目录下使用命令

opencv_traincascade -data tmp -vec myvec_24.vec -bg neg/negdata.txt -numPos 140 -numNeg 900 -numStages 20 -w 24 -h 24 -featureType HOG

进行训练。其中

-data tmp是指定训练结果存放的目录为tmp,tmp目录要事先建立。

-vec myvec_24.vec是指定vec文件为myvec_24.vec

-bg neg/negdata.txt是指定负样本清单为neg/negdata.txt

-numPos 140是指定正样本数目为140

-numNeg 900是指定负样本数目为900

-numStages 20指定训练阶数为20,太大会很慢

-w 24 -h 24 样本的尺寸,应和生成vec文件时的参数-w -h一致

-featureType HOG使用HOG特征,其他几个参数我实验了,不能用,报错,只有这个能用

然后就是等待了,会出现HR击中率,FA虚警率等信息

训练完成后会有一个xml文件,保存训练结果,不过我虽然训练成功了,但这个文件我用程序加载不上,加载报错。

3.使用opencv_haartraining训练

由于使用opencv_traincascade训练的结果无法加载,我没兴趣查找这个原因了,于是我用老的opencv_haartraining进行训练,训练的结果可以加载使用。

在/home/foo/下运行

opencv_haartraining -data tmp -vec myvec_24.vec -w 24 -h 24 -bg neg/negdata.txt  -nstages 20 -npos 140 -nneg 900

其中

-data tmp是指定中间结果存放的目录为tmp,该目录要提前建立

-nstages 20指定训练阶数为20,太大会很慢

-npos 140 指定正样本数目为140

-nneg 900指定负样本数目为900

漫长等待后,/home/foo目录下会出现一个xml文件,用这个文件就可以进行识别了。

 

 

 

转载jpeglib使用实例

[cc lang="c"]

/*
 * example.c
 *
 * This file illustrates how to use the IJG code as a subroutine library
 * to read or write JPEG image files.  You should look at this code in
 * conjunction with the documentation file libjpeg.doc.
 *
 * This code will not do anything useful as-is, but it may be helpful as a
 * skeleton for constructing routines that call the JPEG library.  
 *
 * We present these routines in the same coding style used in the JPEG code
 * (ANSI function definitions, etc); but you are of course free to code your
 * routines in a different style if you prefer.
 */

#include <stdio.h>

/*
 * Include file for users of JPEG library.
 * You will need to have included system headers that define at least
 * the typedefs FILE and size_t before you can include jpeglib.h.
 * (stdio.h is sufficient on ANSI-conforming systems.)
 * You may also wish to include "jerror.h".
 */

#include "jpeglib.h"

/*
 * <setjmp.h> is used for the optional error recovery mechanism shown in
 * the second part of the example.
 */

#include <setjmp.h>



/******************** JPEG COMPRESSION SAMPLE INTERFACE *******************/

/* This half of the example shows how to feed data into the JPEG compressor.
 * We present a minimal version that does not worry about refinements such
 * as error recovery (the JPEG code will just exit() if it gets an error).
 */


/*
 * IMAGE DATA FORMATS:
 *
 * The standard input image format is a rectangular array of pixels, with
 * each pixel having the same number of "component" values (color channels).
 * Each pixel row is an array of JSAMPLEs (which typically are unsigned chars).
 * If you are working with color data, then the color values for each pixel
 * must be adjacent in the row; for example, R,G,B,R,G,B,R,G,B,... for 24-bit
 * RGB color.
 *
 * For this example, we'll assume that this data structure matches the way
 * our application has stored the image in memory, so we can just pass a
 * pointer to our image buffer.  In particular, let's say that the image is
 * RGB color and is described by:
 */

extern JSAMPLE * image_buffer;	/* Points to large array of R,G,B-order data */
extern int image_height;	/* Number of rows in image */
extern int image_width;		/* Number of columns in image */


/*
 * Sample routine for JPEG compression.  We assume that the target file name
 * and a compression quality factor are passed in.
 */

GLOBAL(void)
write_JPEG_file (char * filename, int quality)
{
  /* This struct contains the JPEG compression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   * It is possible to have several such structures, representing multiple
   * compression/decompression processes, in existence at once.  We refer
   * to any one struct (and its associated working data) as a "JPEG object".
   */
  struct jpeg_compress_struct cinfo;
  /* This struct represents a JPEG error handler.  It is declared separately
   * because applications often want to supply a specialized error handler
   * (see the second half of this file for an example).  But here we just
   * take the easy way out and use the standard error handler, which will
   * print a message on stderr and call exit() if compression fails.
   * Note that this struct must live as long as the main JPEG parameter
   * struct, to avoid dangling-pointer problems.
   */
  struct jpeg_error_mgr jerr;
  /* More stuff */
  FILE * outfile;		/* target file */
  JSAMPROW row_pointer[1];	/* pointer to JSAMPLE row[s] */
  int row_stride;		/* physical row width in image buffer */

  /* Step 1: allocate and initialize JPEG compression object */

  /* We have to set up the error handler first, in case the initialization
   * step fails.  (Unlikely, but it could happen if you are out of memory.)
   * This routine fills in the contents of struct jerr, and returns jerr's
   * address which we place into the link field in cinfo.
   */
  cinfo.err = jpeg_std_error(&jerr);
  /* Now we can initialize the JPEG compression object. */
  jpeg_create_compress(&cinfo);

  /* Step 2: specify data destination (eg, a file) */
  /* Note: steps 2 and 3 can be done in either order. */

  /* Here we use the library-supplied code to send compressed data to a
   * stdio stream.  You can also write your own code to do something else.
   * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
   * requires it in order to write binary files.
   */
  if ((outfile = fopen(filename, "wb")) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    exit(1);
  }
  jpeg_stdio_dest(&cinfo, outfile);

  /* Step 3: set parameters for compression */

  /* First we supply a description of the input image.
   * Four fields of the cinfo struct must be filled in:
   */
  cinfo.image_width = image_width; 	/* image width and height, in pixels */
  cinfo.image_height = image_height;
  cinfo.input_components = 3;		/* # of color components per pixel */
  cinfo.in_color_space = JCS_RGB; 	/* colorspace of input image */
  /* Now use the library's routine to set default compression parameters.
   * (You must set at least cinfo.in_color_space before calling this,
   * since the defaults depend on the source color space.)
   */
  jpeg_set_defaults(&cinfo);
  /* Now you can set any non-default parameters you wish to.
   * Here we just illustrate the use of quality (quantization table) scaling:
   */
  jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);

  /* Step 4: Start compressor */

  /* TRUE ensures that we will write a complete interchange-JPEG file.
   * Pass TRUE unless you are very sure of what you're doing.
   */
  jpeg_start_compress(&cinfo, TRUE);

  /* Step 5: while (scan lines remain to be written) */
  /*           jpeg_write_scanlines(...); */

  /* Here we use the library's state variable cinfo.next_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   * To keep things simple, we pass one scanline per call; you can pass
   * more if you wish, though.
   */
  row_stride = image_width * 3;	/* JSAMPLEs per row in image_buffer */

  while (cinfo.next_scanline < cinfo.image_height) {
    /* jpeg_write_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could pass
     * more than one scanline at a time if that's more convenient.
     */
    row_pointer[0] = & image_buffer[cinfo.next_scanline * row_stride];
    (void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
  }

  /* Step 6: Finish compression */

  jpeg_finish_compress(&cinfo);
  /* After finish_compress, we can close the output file. */
  fclose(outfile);

  /* Step 7: release JPEG compression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_compress(&cinfo);

  /* And we're done! */
}


/*
 * SOME FINE POINTS:
 *
 * In the above loop, we ignored the return value of jpeg_write_scanlines,
 * which is the number of scanlines actually written.  We could get away
 * with this because we were only relying on the value of cinfo.next_scanline,
 * which will be incremented correctly.  If you maintain additional loop
 * variables then you should be careful to increment them properly.
 * Actually, for output to a stdio stream you needn't worry, because
 * then jpeg_write_scanlines will write all the lines passed (or else exit
 * with a fatal error).  Partial writes can only occur if you use a data
 * destination module that can demand suspension of the compressor.
 * (If you don't know what that's for, you don't need it.)
 *
 * If the compressor requires full-image buffers (for entropy-coding
 * optimization or a multi-scan JPEG file), it will create temporary
 * files for anything that doesn't fit within the maximum-memory setting.
 * (Note that temp files are NOT needed if you use the default parameters.)
 * On some systems you may need to set up a signal handler to ensure that
 * temporary files are deleted if the program is interrupted.  See libjpeg.doc.
 *
 * Scanlines MUST be supplied in top-to-bottom order if you want your JPEG
 * files to be compatible with everyone else's.  If you cannot readily read
 * your data in that order, you'll need an intermediate array to hold the
 * image.  See rdtarga.c or rdbmp.c for examples of handling bottom-to-top
 * source data using the JPEG code's internal virtual-array mechanisms.
 */



/******************** JPEG DECOMPRESSION SAMPLE INTERFACE *******************/

/* This half of the example shows how to read data from the JPEG decompressor.
 * It's a bit more refined than the above, in that we show:
 *   (a) how to modify the JPEG library's standard error-reporting behavior;
 *   (b) how to allocate workspace using the library's memory manager.
 *
 * Just to make this example a little different from the first one, we'll
 * assume that we do not intend to put the whole image into an in-memory
 * buffer, but to send it line-by-line someplace else.  We need a one-
 * scanline-high JSAMPLE array as a work buffer, and we will let the JPEG
 * memory manager allocate it for us.  This approach is actually quite useful
 * because we don't need to remember to deallocate the buffer separately: it
 * will go away automatically when the JPEG object is cleaned up.
 */


/*
 * ERROR HANDLING:
 *
 * The JPEG library's standard error handler (jerror.c) is divided into
 * several "methods" which you can override individually.  This lets you
 * adjust the behavior without duplicating a lot of code, which you might
 * have to update with each future release.
 *
 * Our example here shows how to override the "error_exit" method so that
 * control is returned to the library's caller when a fatal error occurs,
 * rather than calling exit() as the standard error_exit method does.
 *
 * We use C's setjmp/longjmp facility to return control.  This means that the
 * routine which calls the JPEG library must first execute a setjmp() call to
 * establish the return point.  We want the replacement error_exit to do a
 * longjmp().  But we need to make the setjmp buffer accessible to the
 * error_exit routine.  To do this, we make a private extension of the
 * standard JPEG error handler object.  (If we were using C++, we'd say we
 * were making a subclass of the regular error handler.)
 *
 * Here's the extended error handler struct:
 */

struct my_error_mgr {
  struct jpeg_error_mgr pub;	/* "public" fields */

  jmp_buf setjmp_buffer;	/* for return to caller */
};

typedef struct my_error_mgr * my_error_ptr;

/*
 * Here's the routine that will replace the standard error_exit method:
 */

METHODDEF(void)
my_error_exit (j_common_ptr cinfo)
{
  /* cinfo->err really points to a my_error_mgr struct, so coerce pointer */
  my_error_ptr myerr = (my_error_ptr) cinfo->err;

  /* Always display the message. */
  /* We could postpone this until after returning, if we chose. */
  (*cinfo->err->output_message) (cinfo);

  /* Return control to the setjmp point */
  longjmp(myerr->setjmp_buffer, 1);
}


/*
 * Sample routine for JPEG decompression.  We assume that the source file name
 * is passed in.  We want to return 1 on success, 0 on error.
 */


GLOBAL(int)
read_JPEG_file (char * filename)
{
  /* This struct contains the JPEG decompression parameters and pointers to
   * working space (which is allocated as needed by the JPEG library).
   */
  struct jpeg_decompress_struct cinfo;
  /* We use our private extension JPEG error handler.
   * Note that this struct must live as long as the main JPEG parameter
   * struct, to avoid dangling-pointer problems.
   */
  struct my_error_mgr jerr;
  /* More stuff */
  FILE * infile;		/* source file */
  JSAMPARRAY buffer;		/* Output row buffer */
  int row_stride;		/* physical row width in output buffer */

  /* In this example we want to open the input file before doing anything else,
   * so that the setjmp() error recovery below can assume the file is open.
   * VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
   * requires it in order to read binary files.
   */

  if ((infile = fopen(filename, "rb")) == NULL) {
    fprintf(stderr, "can't open %s\n", filename);
    return 0;
  }

  /* Step 1: allocate and initialize JPEG decompression object */

  /* We set up the normal JPEG error routines, then override error_exit. */
  cinfo.err = jpeg_std_error(&jerr.pub);
  jerr.pub.error_exit = my_error_exit;
  /* Establish the setjmp return context for my_error_exit to use. */
  if (setjmp(jerr.setjmp_buffer)) {
    /* If we get here, the JPEG code has signaled an error.
     * We need to clean up the JPEG object, close the input file, and return.
     */
    jpeg_destroy_decompress(&cinfo);
    fclose(infile);
    return 0;
  }
  /* Now we can initialize the JPEG decompression object. */
  jpeg_create_decompress(&cinfo);

  /* Step 2: specify data source (eg, a file) */

  jpeg_stdio_src(&cinfo, infile);

  /* Step 3: read file parameters with jpeg_read_header() */

  (void) jpeg_read_header(&cinfo, TRUE);
  /* We can ignore the return value from jpeg_read_header since
   *   (a) suspension is not possible with the stdio data source, and
   *   (b) we passed TRUE to reject a tables-only JPEG file as an error.
   * See libjpeg.doc for more info.
   */

  /* Step 4: set parameters for decompression */

  /* In this example, we don't need to change any of the defaults set by
   * jpeg_read_header(), so we do nothing here.
   */

  /* Step 5: Start decompressor */

  (void) jpeg_start_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* We may need to do some setup of our own at this point before reading
   * the data.  After jpeg_start_decompress() we have the correct scaled
   * output image dimensions available, as well as the output colormap
   * if we asked for color quantization.
   * In this example, we need to make an output work buffer of the right size.
   */ 
  /* JSAMPLEs per row in output buffer */
  row_stride = cinfo.output_width * cinfo.output_components;
  /* Make a one-row-high sample array that will go away when done with image */
  buffer = (*cinfo.mem->alloc_sarray)
		((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);

  /* Step 6: while (scan lines remain to be read) */
  /*           jpeg_read_scanlines(...); */

  /* Here we use the library's state variable cinfo.output_scanline as the
   * loop counter, so that we don't have to keep track ourselves.
   */
  while (cinfo.output_scanline < cinfo.output_height) {
    /* jpeg_read_scanlines expects an array of pointers to scanlines.
     * Here the array is only one element long, but you could ask for
     * more than one scanline at a time if that's more convenient.
     */
    (void) jpeg_read_scanlines(&cinfo, buffer, 1);
    /* Assume put_scanline_someplace wants a pointer and sample count. */
    put_scanline_someplace(buffer[0], row_stride);
  }

  /* Step 7: Finish decompression */

  (void) jpeg_finish_decompress(&cinfo);
  /* We can ignore the return value since suspension is not possible
   * with the stdio data source.
   */

  /* Step 8: Release JPEG decompression object */

  /* This is an important step since it will release a good deal of memory. */
  jpeg_destroy_decompress(&cinfo);

  /* After finish_decompress, we can close the input file.
   * Here we postpone it until after no more JPEG errors are possible,
   * so as to simplify the setjmp error logic above.  (Actually, I don't
   * think that jpeg_destroy can do an error exit, but why assume anything...)
   */
  fclose(infile);

  /* At this point you may want to check to see whether any corrupt-data
   * warnings occurred (test whether jerr.pub.num_warnings is nonzero).
   */

  /* And we're done! */
  return 1;
}


/*
 * SOME FINE POINTS:
 *
 * In the above code, we ignored the return value of jpeg_read_scanlines,
 * which is the number of scanlines actually read.  We could get away with
 * this because we asked for only one line at a time and we weren't using
 * a suspending data source.  See libjpeg.doc for more info.
 *
 * We cheated a bit by calling alloc_sarray() after jpeg_start_decompress();
 * we should have done it beforehand to ensure that the space would be
 * counted against the JPEG max_memory setting.  In some systems the above
 * code would risk an out-of-memory error.  However, in general we don't
 * know the output image dimensions before jpeg_start_decompress(), unless we
 * call jpeg_calc_output_dimensions().  See libjpeg.doc for more about this.
 *
 * Scanlines are returned in the same order as they appear in the JPEG file,
 * which is standardly top-to-bottom.  If you must emit data bottom-to-top,
 * you can use one of the virtual arrays provided by the JPEG memory manager
 * to invert the data.  See wrbmp.c for an example.
 *
 * As with compression, some operating modes may require temporary files.
 * On some systems you may need to set up a signal handler to ensure that
 * temporary files are deleted if the program is interrupted.  See libjpeg.doc.
 */

 

 

[\cc]

转载:一个关于字符编码的文章,很全面

原文连接:http://www.crifan.com/files/doc/docbook/char_encoding/release/html/char_encoding.html#zhcn_charset_encoding
字符编码详解

版本:v2.2

Crifan Li

摘要

本文主要介绍了字符编码的基础知识,以及常见的字符编码类型,比如ASCII,Unicode,
UTF-8,ISO 8859等,以及各种编码之间的关系,同时专门解释了中文字符相关的编码标准
,包括GB2312,GBK,GB18030,也专门解释了Windows系统中的Code Page,以及相关的BOM
等内容

[提 本文提供多种格式供:
示]      在线阅读      HTML HTMLs PDF CHM TXT RTF WEBHELP
    下载(7zip压缩包) HTML HTMLs PDF CHM TXT RTF WEBHELP

    HTML版本的在线地址为:

    http://www.crifan.com/files/doc/docbook/char_encoding/release/html/
    char_encoding.html

    有任何意见,建议,提交bug等,都欢迎去讨论组发帖讨论:

    http://www.crifan.com/bbs/categories/char_encoding/

2012-08-09

┌────────────────────────────────────────────────────────────────────────────┐
│修订历史                                                                    │
├────────────────────────────┬────────────────────────────────────┬──────────┤
│修订 1.0                    │2011-11-02                          │crl       │
├────────────────────────────┴────────────────────────────────────┴──────────┤
│ 1. 添加了编码相关背景知识介绍                                              │
│ 2. 添加了ASCII和EASCII编码介绍                                             │
│ 3. 添加了ISO/IEC 8859相关的编码和各种单字节编码的关系                      │
│ 4. 添加了Unicode和ISO 10646的解释                                          │
│ 5. 添加了UTF-8和Unicode的区别和联系                                        │
├────────────────────────────┬────────────────────────────────────┬──────────┤
│修订 2.2                    │2012-08-09                          │crl       │
├────────────────────────────┴────────────────────────────────────┴──────────┤
│ 1. 通过Docbook发布                                                         │
│ 2. 合并了原先在zhcn_charset中的内容                                        │
│ 3. 详细解释了Code Page                                                     │
│ 4. 详细解释了ANSI编码                                                      │
│ 5. 解释BOM                                                                 │
└────────────────────────────────────────────────────────────────────────────┘

版权 © 2012 Crifan, http://crifan.com

本文章遵从:署名-非商业性使用 2.5 中国大陆(CC BY-NC 2.5)

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

目录

缩略词
正文之前

    1. 目的
    2. 本文内容
    3. 声明

1. 字符编码相关的背景知识

    1.1. 拉丁字母

        1.1.1. 我们的目标

    1.2. 什么是字符编码

2. 字符编码标准

    2.1. 只支持基本的拉丁字符的字符编码:ASCII

        2.1.1. ASCII的由来
        2.1.2. ASCII编码规则

            2.1.2.1. ASCII字符集中的功能/控制字符

                2.1.2.1.1. 什么是Function Code功能码或 Function Character功能字
                    符
                2.1.2.1.2. ASCII中的Function/Control Code功能字符的详细含义

                    2.1.2.1.2.1. 0 – NUL – NULl 字符/空字符
                    2.1.2.1.2.2. 1 – SOH – Start Of Heading 标题开始
                    2.1.2.1.2.3. 2 – STX,3 – ETX
                    2.1.2.1.2.4. 4 – EOT – End Of Transmission 传输结束
                    2.1.2.1.2.5. 5 – ENQ – ENQuiry 请求
                    2.1.2.1.2.6. 6 – ACK – ACKnowledgment 回应/响应
                    2.1.2.1.2.7. 7 – BEL – [audible] BELl
                    2.1.2.1.2.8. 8 – BS – BackSpace 退格键
                    2.1.2.1.2.9. 9 – HT – Horizontal Tab 水平制表符
                    2.1.2.1.2.10. 10 – LF – Line Feed 换行
                    2.1.2.1.2.11. 11 – VT – Vertical Tab 垂直制表符
                    2.1.2.1.2.12. 12 – FF – Form Feed 换页
                    2.1.2.1.2.13. 13 – CR – Carriage return 机器的滑动部分/底座
                        返回 -> 回车
                    2.1.2.1.2.14. 14 – SO,15 – SI
                    2.1.2.1.2.15. 16 – DLE – Data Link Escape 数据链路转义
                    2.1.2.1.2.16. 17 – DC1 – Device Control 1 / XON –
                        Transmission on
                    2.1.2.1.2.17. 18 – DC2 – Device Control 2
                    2.1.2.1.2.18. 19 – DC3 – Device Control 3 / XOFF –
                        Transmission off 传输中断
                    2.1.2.1.2.19. 20 – DC4 – Device Control 4
                    2.1.2.1.2.20. 21 – NAK – Negative AcKnowledgment 负面响应->
                        无响应, 非正常响应
                    2.1.2.1.2.21. 22 – SYN – SYNchronous idle
                    2.1.2.1.2.22. 23 – ETB – End of Transmission Block 块传输中
                        止
                    2.1.2.1.2.23. 24 – CAN – CANcel 取消
                    2.1.2.1.2.24. 25 – EM – End of Medium 已到介质末端,介质存
                        储已满
                    2.1.2.1.2.25. 26 – SUB – SUBstitute character替补/替换
                    2.1.2.1.2.26. 27 – ESC – ESCape 逃离/取消
                    2.1.2.1.2.27. 28 – FS – File Separator 文件分隔符
                    2.1.2.1.2.28. 29 – GS – Group Separator分组符
                    2.1.2.1.2.29. 30 – RS – Record Separator记录分隔符
                    2.1.2.1.2.30. 31 – US – Unit Separator 单元分隔符
                    2.1.2.1.2.31. 32 – SP – White SPace 空格键
                    2.1.2.1.2.32. 127 – DEL – DELete 删除

                2.1.2.1.3. 各种字符的标准的读法/叫法

        2.1.3. ISO 646

    2.2. 支持多种衍生拉丁字母的字符编码:EASCII和ISO 8859

        2.2.1. EASCII
        2.2.2. ISO 8859

            2.2.2.1. ISO/IEC 8859出现的背景
            2.2.2.2. ISO/IEC 8859的编码规则
            2.2.2.3. ISO/IEC 8859的特点
            2.2.2.4. ISO/IEC 6429
            2.2.2.5. ISO 8859和ISO-8859的区别和联系

                2.2.2.5.1. 原先的ISO 8859-1和我们常说的ISO 8859-1

    2.3. 各种单字节编码标准的关系
    2.4. 支持世界上几乎所有字符的字符编码:Unicode

        2.4.1. Unicode和ISO 10646的关系

            2.4.1.1. ISO 10646=UCS
            2.4.1.2. Unicode 和ISO 10646的联系
            2.4.1.3. Unicode和ISO 10646的区别

        2.4.2. Unicode编码规则
        2.4.3. Unicode字符编码所对应的存储和交换标准:UTF-8, UTF-16, UTF-32

            2.4.3.1. UTF-8
            2.4.3.2. Unicode与UTF-8之间的转换

                2.4.3.2.1. 关于UTF-8的BOM:“EF BB BF”

    2.5. 代码页Code Page

        2.5.1. 什么是代码页(Code Page)
        2.5.2. Windows中的Code Page

            2.5.2.1. Windows中的Code Page分类:ANSI和OEM

                2.5.2.1.1. Windows的ANSI Code Page表
                2.5.2.1.2. Windows的OEM Code Page表
                2.5.2.1.3. 一些常见的Code Page表

            2.5.2.2. 所有的Code Page表

    2.6. ANSI字符编码

        2.6.1. ANSI是啥
        2.6.2. ANSI编码规则
        2.6.3. ANSI (Windows 1252)编码表
        2.6.4. ANSI编码与ANSI的关系
        2.6.5. ANSI字符编码和Windows 1252

            2.6.5.1. Windows 1252和ISO 8859-1之间的区别

        2.6.6. 为何"ANSI编码"(在Windows中)被称为"本地编码"

    2.7. BOM

        2.7.1. BOM是什么
        2.7.2. 为何需要BOM
        2.7.3. BOM表

    2.8. 中文字符编码标准

        2.8.1. GB2312,CP936,GBK,GB18030,GB13000

            2.8.1.1. GB2312
            2.8.1.2. GB13000
            2.8.1.3. GBK
            2.8.1.4. GB18030

        2.8.2. 各种中文字符编码标准的关系

    2.9. 字符存储(交换)标准
    2.10. 字形和你所看到的字符的关系

参考书目
A. 编码相关的表格

    A.1. ASCII编码表(0-127)
    A.2. ISO/IEC 8859编码标准中的15种字符集
    A.3. Code Page表格

        A.3.1. 常见的ANSI和OEM的Code Page的表格

            A.3.1.1. ANSI Code Page表
            A.3.1.2. OEM Code Page表
            A.3.1.3. ANSI和OEM共有的Code Page表
            A.3.1.4. 其他一些常见的Code Page表

        A.3.2. 所有的Code Page相关的表格

    A.4. 不同编码所用的BOM

插图清单

2.1. ISO/IEC 8859的15个字符集的部分比较
2.2. ISO/IEC 8859-1字符集表
2.3. Unicode中的各种平面划分
2.4. Notepad中的各种编码
2.5. 汉字“宋”的不同字体

表格清单

2.1. ASCII中的控制字符
2.2. 各种单字节编码标准之间的关系
2.3. ISO/IEC 10646与Unicode的版本对应关系
2.4. Unicode与UTF-8之间的编码映射关系
2.5. 中文字符相关编码标准
2.6. 字符(存储)交换标准
A.1. ASCII编码表(0-127)
A.2. ISO/IEC 8859编码标准中的15种字符集
A.3. ANSI的SBCS Code Page
A.4. OEM的Code Page
A.5. ANSI和OEM共有的DBCS Code Page
A.6. 一些常见的Code Page
A.7. 微软的代码页标识符(Code Page Identifiers)
A.8. 不同编码所用的BOM

缩略词

ASCII (ASCII)

    American Standard Code for Information Interchange

    美国信息交换标准代码

BMP (BMP)

    Basic Multilingual Plane

    基本多文种平面

EBCDIC (EBCDIC)

    Extended Binary Coded Decimal Interchange Code

    扩展二进制编码十进制交换码

IANA (IANA)

    Internet Assigned Numbers Authority

    互联网号码分配局

ISO/IEC (ISO/IEC)

    International Organization for Standardization / International
    Electrotechnical Commission

    国际标准化组织和国际电工委员会

UCS (UCS)

    Universal Character Set

    通用字符集

UTF (UTF)

    Unicode Transformation Format

    Unicode转换格式

正文之前

目录

1. 目的
2. 本文内容
3. 声明

1. 目的

本文旨在讲清楚字符编码的概念和来龙去脉,和常见标准之间的关系和区别。

2. 本文内容

个人对于字符编码的理解,最开始主要是看了阮一峰的这篇文章:

【转】字符编码笔记:ASCII,Unicode和UTF-8

然后自己花了更多的时间,搜集整理了和字符编码的更详细的知识,整理出来,以供大家
参考。

其中也摘录了该贴的部分内容。

3. 声明

任何问题,意见,建议等,都欢迎一起探讨:admin (at) crifan.com。

第 1 章 字符编码相关的背景知识

目录

1.1. 拉丁字母

    1.1.1. 我们的目标

1.2. 什么是字符编码

1.1. 拉丁字母

1.1.1. 我们的目标

在介绍计算机的字符编码知识前,先来说说这个拉丁字母,估计也会有人和我一样,对于
拉丁字母和英文字母以及汉语拼音中的字母的关系,不是很清楚。

拉丁字母,也叫罗马字母,是当今世界上使用最广的字母系统。

拉丁字母,或者说基本的拉丁字母,就是你所常见的到的ABCD等26个英文字母。

原先是欧洲那边使用的,后来由于欧洲殖民主义,导致后来的美洲等地,也是用的这套字
母体系。

而其他有些地方,比如越南等,本来有自己的文字语言的,结果受西方文化的影响和由于
基督教的传播,也用拉丁字母了。

所以总的说,现在欧洲多数国家,美洲,澳洲,非洲的多数国家,都是用的拉丁字母,即
你所常见的英文字母,也是拉丁字母。而中国的汉语拼音,也是用的这个拉丁字母。

其中,欧洲很多国家,是对已有的26个基本的拉丁字母,加上连字,变音字符,弄出个衍
生拉丁字母,但是还是属于拉丁字母。

说了这么多,就是要让你知道,后面内容所提到的英文字母,其来源于拉丁字母,而且我
们汉语的汉语拼音,也是拉丁字母。

即:

  • 基本的拉丁字母 = 26个英文字母 = 汉语中的汉语拼音
  • 衍生的拉丁字母 = 从基本的26个英文字母,加上连字,变音等字符而衍生出来的拉丁
    字母 = 很多西欧国家的字母(每个国家都不太一样)

1.2. 什么是字符编码

计算机中存放的都是0和1的二进制值。8个位对应一个字节,常用16进制来表示。

而我们普通用户所希望看到的是,计算机把其所存储的对应的16进制的数值,转化为对应
的字符,包括英文和中文等其他语言的字符,然后输出到屏幕上。

而所谓编码,就是,定义了一套规则,去指定,哪些数值,对应着哪些字符。

举个最简单的例子,常见65=0x41对应的是大写字母A,97=0x61对应的是小写字母a,而这
套数值和字母之间的映射关系,说白了,就是一套规则,就叫做字符编码,即我们常说的
ASCII编码。

那有人会问了,如果我定义了一套规则,假如叫张三编码,然后故意去把ASCII中的映射关
系改变,比如97=0x61对应的是大写字母A,65=0x41对应的是小写字母a,等等,可不可以
?答案是,完全可以,不过这套规则,首先没有得到所有计算机业界的一致认同,所以,
除了你自己用,其他人不原意使用,那么也就是没了存在的价值了。

换句话说,当初ASCII之所以这么定义这套规则,就是这么定义了而已,然后大家都接受这
个标准,然后就都用这个定义了。

即,如果当初定义为0x41代表的是小写字母a,而不是大写字母A,那么现在你所看到的,
就是小写字母a就是对应着计算机中存储的0x41,而不是之前的0x61了。

所以,简单的说就是:

所谓字符编码,就是定义了一套规则,指定了计算机中存放的这么多值中的哪个值,对应
了电脑屏幕显示出来的哪个字母。

第 2 章 字符编码标准

目录

2.1. 只支持基本的拉丁字符的字符编码:ASCII

    2.1.1. ASCII的由来
    2.1.2. ASCII编码规则

        2.1.2.1. ASCII字符集中的功能/控制字符

            2.1.2.1.1. 什么是Function Code功能码或 Function Character功能字符
            2.1.2.1.2. ASCII中的Function/Control Code功能字符的详细含义

                2.1.2.1.2.1. 0 – NUL – NULl 字符/空字符
                2.1.2.1.2.2. 1 – SOH – Start Of Heading 标题开始
                2.1.2.1.2.3. 2 – STX,3 – ETX
                2.1.2.1.2.4. 4 – EOT – End Of Transmission 传输结束
                2.1.2.1.2.5. 5 – ENQ – ENQuiry 请求
                2.1.2.1.2.6. 6 – ACK – ACKnowledgment 回应/响应
                2.1.2.1.2.7. 7 – BEL – [audible] BELl
                2.1.2.1.2.8. 8 – BS – BackSpace 退格键
                2.1.2.1.2.9. 9 – HT – Horizontal Tab 水平制表符
                2.1.2.1.2.10. 10 – LF – Line Feed 换行
                2.1.2.1.2.11. 11 – VT – Vertical Tab 垂直制表符
                2.1.2.1.2.12. 12 – FF – Form Feed 换页
                2.1.2.1.2.13. 13 – CR – Carriage return 机器的滑动部分/底座返回
                    -> 回车
                2.1.2.1.2.14. 14 – SO,15 – SI
                2.1.2.1.2.15. 16 – DLE – Data Link Escape 数据链路转义
                2.1.2.1.2.16. 17 – DC1 – Device Control 1 / XON – Transmission
                    on
                2.1.2.1.2.17. 18 – DC2 – Device Control 2
                2.1.2.1.2.18. 19 – DC3 – Device Control 3 / XOFF – Transmission
                    off 传输中断
                2.1.2.1.2.19. 20 – DC4 – Device Control 4
                2.1.2.1.2.20. 21 – NAK – Negative AcKnowledgment 负面响应-> 无
                    响应, 非正常响应
                2.1.2.1.2.21. 22 – SYN – SYNchronous idle
                2.1.2.1.2.22. 23 – ETB – End of Transmission Block 块传输中止
                2.1.2.1.2.23. 24 – CAN – CANcel 取消
                2.1.2.1.2.24. 25 – EM – End of Medium 已到介质末端,介质存储已
                    满
                2.1.2.1.2.25. 26 – SUB – SUBstitute character替补/替换
                2.1.2.1.2.26. 27 – ESC – ESCape 逃离/取消
                2.1.2.1.2.27. 28 – FS – File Separator 文件分隔符
                2.1.2.1.2.28. 29 – GS – Group Separator分组符
                2.1.2.1.2.29. 30 – RS – Record Separator记录分隔符
                2.1.2.1.2.30. 31 – US – Unit Separator 单元分隔符
                2.1.2.1.2.31. 32 – SP – White SPace 空格键
                2.1.2.1.2.32. 127 – DEL – DELete 删除

            2.1.2.1.3. 各种字符的标准的读法/叫法

    2.1.3. ISO 646

2.2. 支持多种衍生拉丁字母的字符编码:EASCII和ISO 8859

    2.2.1. EASCII
    2.2.2. ISO 8859

        2.2.2.1. ISO/IEC 8859出现的背景
        2.2.2.2. ISO/IEC 8859的编码规则
        2.2.2.3. ISO/IEC 8859的特点
        2.2.2.4. ISO/IEC 6429
        2.2.2.5. ISO 8859和ISO-8859的区别和联系

            2.2.2.5.1. 原先的ISO 8859-1和我们常说的ISO 8859-1

2.3. 各种单字节编码标准的关系
2.4. 支持世界上几乎所有字符的字符编码:Unicode

    2.4.1. Unicode和ISO 10646的关系

        2.4.1.1. ISO 10646=UCS
        2.4.1.2. Unicode 和ISO 10646的联系
        2.4.1.3. Unicode和ISO 10646的区别

    2.4.2. Unicode编码规则
    2.4.3. Unicode字符编码所对应的存储和交换标准:UTF-8, UTF-16, UTF-32

        2.4.3.1. UTF-8
        2.4.3.2. Unicode与UTF-8之间的转换

            2.4.3.2.1. 关于UTF-8的BOM:“EF BB BF”

2.5. 代码页Code Page

    2.5.1. 什么是代码页(Code Page)
    2.5.2. Windows中的Code Page

        2.5.2.1. Windows中的Code Page分类:ANSI和OEM

            2.5.2.1.1. Windows的ANSI Code Page表
            2.5.2.1.2. Windows的OEM Code Page表
            2.5.2.1.3. 一些常见的Code Page表

        2.5.2.2. 所有的Code Page表

2.6. ANSI字符编码

    2.6.1. ANSI是啥
    2.6.2. ANSI编码规则
    2.6.3. ANSI (Windows 1252)编码表
    2.6.4. ANSI编码与ANSI的关系
    2.6.5. ANSI字符编码和Windows 1252

        2.6.5.1. Windows 1252和ISO 8859-1之间的区别

    2.6.6. 为何"ANSI编码"(在Windows中)被称为"本地编码"

2.7. BOM

    2.7.1. BOM是什么
    2.7.2. 为何需要BOM
    2.7.3. BOM表

2.8. 中文字符编码标准

    2.8.1. GB2312,CP936,GBK,GB18030,GB13000

        2.8.1.1. GB2312
        2.8.1.2. GB13000
        2.8.1.3. GBK
        2.8.1.4. GB18030

    2.8.2. 各种中文字符编码标准的关系

2.9. 字符存储(交换)标准
2.10. 字形和你所看到的字符的关系

2.1. 只支持基本的拉丁字符的字符编码:ASCII

2.1.1. ASCII的由来

计算机刚出现的时候,虽然是美国人发明的,但是也要面对一个问题,即如何将对应的计
算机中的数值,转化为对应的字母,而显示出来,即采用什么样的规则,而当时,各个厂
家或公司都有自己的做法,也就是说,编码规则没有统一。

但是相对来说,得到大家认可的有,IBM的EBCDIC和此处要谈的ASCII。

其中EBCDIC现在基本没人再用,而大家统一都用ASCII了。

ASCII,即American Standard Code for Information Interchange,美国信息交换标准代
码。

单独看名字,就是知道,这字符编码是设计给美国人用的。

那是因为,计算机是美国人所发明和使用的,所以计算机的早期,所设计编码标准,自然
需要先为英文字符来设计和考虑,所以此最早的字符编码ASCII可以显示常见的英文字符,
也可以这么说,也只能显示基本的英文字符。

由于ASCII编码中,不包括其他欧洲的很多国家的衍生的拉丁字母的那些字符,更不包含亚
洲,比如中国的中文字符,因此才会有后面所提到的各种其他字符集,为的就是可以让计
算机显示出自己国家的字符。

2.1.2. ASCII编码规则

ASCII的编码规则,由于最初只是为英文字母所考虑的,而英文只有26个字母,以及加上其
他大小写字母,常见的字符,常见数字等,所有的加起来,也就几十个,而一个字节8位中
前7位的理论上可以表示2^7=128个字符,所以对于设计出来的编码规则来说,只需要用一
个字节来表示,就足够了。

即ASCII编码规则中规定,用单个字节共8位来表示字符,其中最高位为0,其他7位所对于
的每一个值,映射到某个特定的字符,这样就形成了ASCII编码。

ASCII共包含了2^7=128个字符。

其中包括33个不可显示的字符和95个可显示的字符。

而对于ASCII编码规则,简单说就是:

7位的字符编码,即每个字节的最高位第8位为0,其余7位的某个值对应着某个字符。

ASCII字符集共2^7=128个字符 = 33个控制字符 + 95个可见字符。

[提 >ASCII中的可显示的字符和不可显示字符
示] ASCII中可显示的字符,也叫可打印printable字符;

    而ASCII中的值为0 – 31的那些字符,叫做不可显示的字符,也叫不可见字符,不可打
    印(non-printable)字符,由于其字符的作用是起一定的控制作用,所以常称为控制
    字符(control character),即不同的字符实现不同的功能,因此又称为功能字符(
    function code,function character)。

    即ASCII字符集中:

    不可见字符

    =不可打印(non-printable)字符

    =控制字符(control character)

    =功能字符(function code,function character)

    对于ASCII中的控制字符,都包括哪些,以及每个字符的详细含义,第 2.1.2.1 节
    “ASCII字符集中的功能/控制字符”中会有详细介绍。

2.1.2.1. ASCII字符集中的功能/控制字符

2.1.2.1.1. 什么是Function Code功能码或 Function Character功能字符

ASCII字符集,大家都知道吧,最基本的包含了128个字符。其中前32个,0-31,即
0x00-0x1F,都是不可见字符。这些字符,就叫做控制字符。

这些字符没法打印出来,但是每个字符,都对应着一个特殊的控制功能的字符,简称功能
字符或功能码Function Code。

简言之:ASCII中前32个字符,统称为Function Code功能字符。

此外,由于ASCII中的127对应的是Delete,也是不可见的,所以,此处根据笔者的理解,
也可以归为Function Code。

此类字符,对应不同的“功能”,起到一定的“控制作用”,所以,称为控制字符。

关于每个控制字符的控制功能缩写,参见表 2.1 “ASCII中的控制字符”

表 2.1. ASCII中的控制字符

十进  十六进 控制字 转义字                  说明                  Ctrl + 下列字
 制     制     符     符1                                              母2
0     00     NUL    \0      Null character(空字符)                @3
1     01     SOH            Start of Header(标题开始)             A
2     02     STX            Start of Text(正文开始)               B
3     03     ETX            End of Text(正文结束)                 C
4     04     EOT            End of Transmission(传输结束)         D
5     05     ENQ            Enquiry(请求)                         E
6     06     ACK            Acknowledgment(收到通知/响应)         F
7     07     BEL    \a      Bell(响铃)                            G
8     08     BS     \b      Backspace(退格)                       H
9     09     HT     \t      Horizontal Tab(水平制表符)            I
10    0A     LF     \n      Line feed(换行键)                     J
11    0B     VT     \v      Vertical Tab(垂直制表符)              K
12    0C     FF     \f      Form feed(换页键)                     L
13    0D     CR     \r      Carriage return(回车键)               M
14    0E     SO             Shift Out(不用切换)                   N
15    0F     SI             Shift In(启用切换)                    O
16    10     DLE            Data Link Escape(数据链路转义)        P
17    11     DC1            Device Control 1(设备控制1) /XON      Q
                            (Transmit On)
18    12     DC2            Device Control 2(设备控制2)           R
19    13     DC3            Device Control 3(设备控制3) /XOFF     S
                            (Transmit Off)
20    14     DC4            Device Control 4(设备控制4)           T
21    15     NAK            Negative Acknowledgement(拒绝接收/无  U
                            响应)
22    16     SYN            Synchronous Idle(同步空闲)            V
23    17     ETB            End of Trans the Block(传输块结束)    W
24    18     CAN            Cancel(取消)                          X
25    19     EM             End of Medium(已到介质末端/介质存储已 Y
                            满)
26    1A     SUB            Substitute(替补/替换)                 Z
27    1B     ESC    \e      Escape(溢出/逃离/取消)                [
28    1C     FS             File Separator(文件分割符)            \
29    1D     GS             Group Separator(分组符)               ]
30    1E     RS             Record Separator(记录分隔符)          ^3
31    1F     US             Unit Separator(单元分隔符)            _3
32    20     SP             White space                           [Space]4
127   7F     DEL            Delete(删除)                          ?5

1    即在C语言中或其他地方如何表示。

2    可以通过 “Ctrl+对应字母/按键”实现上述控制字符的输入

     下面列举一些你可能遇到的情况:

       • 用Ctrl+V输入[SYNC]
       • 用Ctrl+M输入[Enter]

         当然也可以直接用Enter键,但是在Windows下面,其会发送两个字符:CR和LF

         关于CR,LF,详情参考:【详解】回车换行 0x0D 0x0A CR LF r n的来龙去脉

       • 用Ctrl+Q输入XON
       • 用Ctrl+S输入XOFF

3    注意此处想要在键盘上输入这三个字符的话,是需要通过Shift加上对应字符才能输
     入的:

       • @:用Shift + 2输入
       • ^:用Shift + 6输入
       • _:用Shift + -输入

4    32=0x20,对应的是空格(Blank Space)键。不需要加Ctrl键,即可直接通过键盘上
     的空格键输入。

5    127=0x7F=删除(Delete)键;,除了可以用键盘上的删除键输入,也可以用'Ctrl+?'
     输入。

2.1.2.1.2. ASCII中的Function/Control Code功能字符的详细含义

2.1.2.1.2.1. 0 – NUL – NULl 字符/空字符

ASCII字符集中的空字符,NULL,起初本意可以看作为NOP(中文意为空操作,就是啥都不
做的意思),此位置可以忽略一个字符。

之所以有这个空字符,主要是用于计算机早期的记录信息的纸带,此处留个NUL字符,意思
是先占这个位置,以待后用,比如你哪天想起来了,在这个位置在放一个别的啥字符之类
的。

后来呢,NUL字符被用于C语言中,字符串的终结符,当一个字符串中间出现NUL / NULL,
代码里面表现为\0,的时候,就意味着这个是一个字符串的结尾了。这样就方便按照自己
需求去定义字符串,多长都行,当然只要你内存放得下,然后最后加一个\0, 即空字符,
意思是当前字符串到此结束。

2.1.2.1.2.2. 1 – SOH – Start Of Heading 标题开始

如果信息沟通交流主要以命令和消息的形式的话,SOH就可以用于标记每个消息的开始。

1963年,最开始ASCII标准中,把此字符定义为Start of Message,后来又改为现在的
Start Of Heading。

现在,这个SOH常见于主从(master-slave)模式的RS232的通信中,一个主设备,以SOH开
头,和从设备进行通信。这样方便从设备在数据传输出现错误的时候,在下一次通信之前
,去实现重新同步(resynchronize)。如果没有一个清晰的类似于SOH这样的标记,去标
记每个命令的起始或开头的话,那么重新同步,就很难实现了。

2.1.2.1.2.3. 2 – STX,3 – ETX

2 – STX – Start Of Text 文本开始

3 – ETX – End Of Text 文本结束

通过某种通讯协议去传输的一个数据(包),称为一帧的话,常会包含一个帧头,包含了
寻址信息,即你是要发给谁,要发送到目的地是哪里,其后跟着真正要发送的数据内容。

而STX,就用于标记这个数据内容的开始。接下来是要传输的数据,最后是ETX,表明数据
的结束。

其中,中间具体传输的数据内容,ASCII规范并没有去定义,其和你所用的传输协议,具体
自己要传什么数据有关。

                   帧头                               数据或文本内容
SOH(表明  ......(帧头信息,比如包含了目的 STX(表明  ......(真正要 ETX(表明
帧头开始) 地址,表明你发送给谁等等)       数据开始) 传输的数据)   数据结束

不过其中有趣的是,1963年,ASCII标准最初版本的时候,把现在的STX叫做EOA(End Of
Address),ETX叫做(End Of Message)。

这是因为,最早的时候,一个消息中,总是包含一个开始符和一个终止符。现在的新的定
义,使得可以去发送一个固定长度的命令,而只用一个SOH表明帧头开始即可,而不需要再
加上一个命令终止符或帧头结束符。

总结一下:

一般发送一个消息,包含了一个帧头和后面真正要传的数据。

而对于帧头,属于控制类的信息,这部分之前属于命令,后面的真实要传的数据属于数据
。即消息=帧头+数据。

而之前的命令都要有个开始符和结束符,这样就是:

消息

= 帧头 + 要传的数据

= 帧头开始+帧头信息+帧头结束 + 要传的数据

而现在新的定义,使得只需要:

消息

= 帧头 +要传的数据

= SOH(表明帧头开始)+帧头信息+ 要传的数据

= SOH(表明帧头开始)+帧头信息 + STX + 数据内容+ETX

就可以少用一个帧头结束符。

而如今,在很多协议中,也常见到,一个固定长度的帧头,后面紧接着就是数据了,而没
有所谓的帧头结束符之类的东西去区分帧头和数据。

2.1.2.1.2.4. 4 – EOT – End Of Transmission 传输结束

2.1.2.1.2.5. 5 – ENQ – ENQuiry 请求

2.1.2.1.2.6. 6 – ACK – ACKnowledgment 回应/响应

2.1.2.1.2.7. 7 – BEL – [audible] BELl

在ASCII字符集中,BEL,是个比较有意思的东东。

因为其原先本意不是用来数据编码的,于此相反,ASCII中的其他字符,都是用于字符编码
(即用什么字符,代表什么含义)或者起到控制设备的作用。

BEL用一个可以听得见的声音,来吸引人们的注意,其原打算即用于计算机也用于一些设备
,比如打印机等。

C语言里面也支持此BEL,用a来实现这个响铃。

2.1.2.1.2.8. 8 – BS – BackSpace 退格键

退格键的功能,随着时间变化,意义也变得不同了。

起初,意思是,在打印机和电传打字机上,往回移动一格光标,以起到强调该字符的作用
。

比如你想要打印一个a,然后加上退格键后,就成了aBS^。在机械类打字机上,此方法能够
起到实际的强调字符的作用,但是对于后来的CTR下时期来说,就无法起到对应效果了。

而现代所用的退格键,不仅仅表示光标往回移动了一格,同时也删除了移动后该位置的字
符。在C语言中,退格键可以用b表示。

2.1.2.1.2.9. 9 – HT – Horizontal Tab 水平制表符

ASCII中的HT控制符的作用是用于布局的。

其控制输出设备前进到下一个表格去处理。

而制表符Table/Tab的宽度也是灵活不固定的,只不过,多数设备上,制表符Tab的宽度都
预定义为8。

水平制表符HT不仅能减少数据输入者的工作量,对于格式化好的文字来说,还能够减少存
储空间,因为一个Tab键,就代替了8个空格,所以说省空间。

对于省空间的优点,我们现在来看,可能会觉得可笑,因为现在存储空间已足够大,一般
来说根本不会需要去省那么点可怜的存储空间。

但是,实际上在计算机刚发明的时候,存储空间(主要指的是内存)极其有限也极其昂贵
,而且像ZIP等压缩方法也还没发明呢,所以对于当时来说,对于存储空间,那是能够省一
点是一点,省任何一点,都是好的,也都是不容易的,省空间就是省钱啊。

C语言中,用t表示制表符。

2.1.2.1.2.10. 10 – LF – Line Feed 换行

LF,直译为(给打印机等)喂一行,意思就是所说的,换行。

换行字符,是ASCII字符集中,被误用的字符中的其中一个。

LF的最原始的含义是,移动打印机的头到下一行。而另外一个ASCII字符,CR(Carriage
Return)才是将打印机的头,移到最左边即一行的开始,行首。很多串口协议和MS-DOS及
Windows操作系统,也都是这么实现的。

而于此不同,对于C语言和Unix操作系统,其重新定义了LF字符的含义为新行,即LF和CR的
组合才能表达出的,回车且换行的意思。

虽然你可以争论哪种用法是错的,但是,不可否认,是从程序的角度出发,C语言和Unix对
此LF的含义实现显得就很自然,而MS-DOS的实现更接近于LF的本意。

如果最开始ASCII标准中,及定义 CF也定义newline,那样意思会清楚,会更好理理解:

LF表示物理上的,设备控制方面的移动到下一行(并没有移动到行首);

新行(newline)表示逻辑上文本分隔符,即回车换行。

不过呢,现在人们常将LF用做newline新行的功能,而大多数文本编辑软件也都可以处理单
个LF或者CR/LF的组合了。

LF在C语言中,用n表示。

2.1.2.1.2.11. 11 – VT – Vertical Tab 垂直制表符

垂直制表符,类似于水平制表符Tab,目的是为了减少布局中的工作,同时也减少了格式化
字符时所需要存储字符的空间。VT控制码用于跳到下一个标记行。

说实话,还真没看到有些地方需要用这个VT呢,因为一般在换行的时候,都是用LF代替VT
了。

2.1.2.1.2.12. 12 – FF – Form Feed 换页

设计换页键,是用来控制打印机行为的。

当打印机收到此键码的时候,打印机移动到下一页。

不同的设备的终端对此控制码所表现的行为各不同。有些会去清除屏幕,而其他有的只是
显示^L字符或者是只是新换一行而已。

Shell脚本程序Bash和Tcsh的实现方式是,把FF看作是一个清除屏幕的命令。C语言程序中
用f表示FF(换页)。

2.1.2.1.2.13. 13 – CR – Carriage return 机器的滑动部分/底座返回 -> 回车

CR回车的原意是让打印头回到左边界,并没有移动到下一行。

随着时间流逝,后来人把CR的意思弄成了Enter键,用于示意输入完毕。

在数据以屏幕显示的情况下,人们在Enter的同时,也希望把光标移动到下一行。

因此C语言和Unix操作系统,重新定义了LF的意思,使其表示为移动到下一行。当输入CR去
存储数据的时候,软件也常常隐式地将其转换为LF。

2.1.2.1.2.14. 14 – SO,15 – SI

14 – SO – Shift Out 不用切换

15 – SI – Shift In 启用切换

早在1960s年代,定义ASCII字符集的人,就已经懂得了,设计字符集不单单可以用于英文
字符集,也要能应用于外文字符集,是很重要的。

定义Shift In 和Shift Out的含义,即考虑到了此点。

最开始,其意为在西里尔语和拉丁语之间切换。

西里尔ASCII定义中,KOI-7用到了Shift字符。拉丁语用Shift去改变打印机的字体。

在此种用途中,SO用于产生双倍宽度的字符,而用SI打印压缩的字体。

2.1.2.1.2.15. 16 – DLE – Data Link Escape 数据链路转义

有时候,我们需要在正在进行的通信过程中去发送一些控制字符。但是,总有一些情况下
,这些控制字符却被看成了普通的数据流,而没有起到对应的控制效果。而ASCII标准中,
定义DLE来解决这类问题。

如果数据流中检测到了DLE,数据接收端则对其后面接下来的数据流中的字符,另作处理。

而关于具体如何处理这些字符,ASCII规范中则没有具体定义,而只是弄了个DLE去打断正
常数据的处理,告诉接下来的数据,要特殊对待。

根据Modem中的Hayes通信协议DLE定义为“无声+++无声”。

以我的观点,这样可能会更好:如果Hayes协议没有把DLE处理为嵌入通讯的无声状态,那
样就符合现存的标准了。

然而Hayes的开发者却觉得+++用的频率要远高于原始的DLE,所以才这么定义了。

2.1.2.1.2.16. 17 – DC1 – Device Control 1 / XON – Transmission on

这个ASCII控制字符尽管原先定义为DC1,但是现在常表示为XON,用于串行通信中的软件流
控制。

其主要作用为,在通信被控制码XOFF中断之后,重新开始信息传输。

用过串行终端的人应该还记得,当有时候数据出错了,按Ctrl+Q(等价于XON)有时候可以
起到重新传输的效果。

这是因为,此Ctrl+Q键盘序列实际上就是产生XON控制码,其可以将那些由于终端或者主机
方面,由于偶尔出现的错误的XOFF控制码而中断的通信解锁,使其正常通信。

2.1.2.1.2.17. 18 – DC2 – Device Control 2

2.1.2.1.2.18. 19 – DC3 – Device Control 3 / XOFF – Transmission off 传输中断

2.1.2.1.2.19. 20 – DC4 – Device Control 4

2.1.2.1.2.20. 21 – NAK – Negative AcKnowledgment 负面响应-> 无响应, 非正常响应

2.1.2.1.2.21. 22 – SYN – SYNchronous idle

2.1.2.1.2.22. 23 – ETB – End of Transmission Block 块传输中止

2.1.2.1.2.23. 24 – CAN – CANcel 取消

2.1.2.1.2.24. 25 – EM – End of Medium 已到介质末端,介质存储已满

EM用于,当数据存储到达串行存储介质末尾的时候,就像磁带或磁头滚动到介质末尾一样
。其用于表述数据的逻辑终点,即不必非要是物理上的达到数据载体的末尾。

2.1.2.1.2.25. 26 – SUB – SUBstitute character替补/替换

2.1.2.1.2.26. 27 – ESC – ESCape 逃离/取消

字符Escape,是ASCII标准的首创的,由Bob Bemer提议的。用于开始一段控制码的扩展字
符。如此,即可以不必将所有可能想得到的字符都放到ASCII标准中了。

因为,新的技术可能需要新的控制命令,而ESC可以用作这些字符命令的起始标志。

ESC广泛用于打印机和终端,去控制设备设置,比如字体,字符位置和颜色等等。

如果最开始的ASCII标准中,没有定义ESC,估计ASCII标准早就被其他标准所替代了,因为
其没有包含这些新出现的字符,所以肯定会有其他新的标准出现,用于表示这些字符的。

即,ESC给开发者提供了,可以根据需要而定义新含义的字符的可能。

2.1.2.1.2.27. 28 – FS – File Separator 文件分隔符

文件分隔符是个很有意思的控制字符,因为其可以让我们看到1960s年代的时候,计算机技
术是如何组织的。

我们现在,习惯于随即访问一些存储介质,比如RAM,磁盘,但是在定义ASCII标准的那个
年代,大部分数据还是顺序的,串行的,而不是随机访问的。此处所说的串行的,不仅仅
指的是串行通信,还指的是顺序存储介质,比如穿孔卡片,纸带,磁带等。

在串行通信的时代,设计这么一个用于表示文件分隔符的控制字符,用于分割两个单独的
文件,是一件很明智的事情。而FS的原因就在于此。

2.1.2.1.2.28. 29 – GS – Group Separator分组符

ASCII定义控制字符的原因中,其中一条就是考虑到了数据存储方面的情况。

大部分情况下,数据库的建立,都和表有关,包含了对应的记录。同一个表中的所有的记
录,属于同一类型。不同的表中的记录,属于对应的不同的类型。

而分组符GS就是用来分隔串行数据存储系统中的不同的组。值得注意的是,当时还没有使
用word的表格,当时ASCII时代的人,把他叫做组。

2.1.2.1.2.29. 30 – RS – Record Separator记录分隔符

记录分隔符RS用于分隔在一个组或表内的多个记录。

2.1.2.1.2.30. 31 – US – Unit Separator 单元分隔符

在ASCII定义中,在数据库中所存储的,最小的数据项,叫做Unit单元。而现在我们称其
field域。单元分隔符US用于分割串行数据存储环境下的不同的域。

现在大部分的数据库实现,要求大部分类型都拥有固定的长度。

尽管大部分时候可能用不到,但是对于每一个域,却都要分配足够大的空间,用于存放最
大可能的成员变量。

这样的做法,占用了大量的存储空间,而US控制码允许域具有可变的长度。在1960s年代,
数据存储空间很有限,用US这个单元分隔符,将不同单元分隔开,这样就可以实现更高效
地存储那些宝贵的数据。

另一方面,串行存储的存储效率,远低于RAM和磁盘中所实现的表格存储。我个人无法想象
,如果现在的数据,还是存储在自带或者带滚轮的磁带上,会是何种景象。

2.1.2.1.2.31. 32 – SP – White SPace 空格键

也许你会争论说,空格键是否真的能算是一个控制字符?因为现在在普通文字中使用空格
键是如此常见。

但是,既然水平制表符和退格键在ASCII中,都被叫做控制字符了,那么我觉得也很自然地
,可以把空格键(向前的空格)也叫做控制字符,毕竟,其本身并不代表一个真正的可见
的字符,而仅仅只是很常用于输出设备,用于处理位置前向移动一格,清除当前位置的内
容而已。

在很多程序中,比如字符处理程序,白空格同样可能从导致行尾转到下一行行首,而网络
浏览器将多个空格组合成单个空格输出。

所以,这更加坚定了我的想法,觉得完全可以把空格看成是一个控制字符,而不仅仅是一
个很独特的普通字符。

2.1.2.1.2.32. 127 – DEL – DELete 删除

有人也许会问,为何ASCII字符集中的控制字符的值都是很小的,即0-32,而DEL控制字符
的值却很大,是127。

这是由于这个特殊的字符是为纸带而定义的。而在那个时候,绝大多数的纸带,都是用7个
孔洞去编码数据的。

而127这个值所对应的二进制值为111 1111b,表示所有7个比特位都是高,所以,将DEL用
在现存的纸带上时,所有的洞就都被穿孔了,就把已经存在的数据都擦出掉了,就起到了
对应的删除的作用了。

2.1.2.1.3. 各种字符的标准的读法/叫法

常见ASCII字符,以及其他非常见的字符,Unicode中的字符,其他特殊字符等等,这些字
符的英文叫法,可以去Unicode官方找到:

http://www.unicode.org/charts/#symbols

比如:

ASCII字符/字母的叫法/读法如何读:

 1. C0 Control and Basic Latin Range:0000-007F
 2. Alphabetic Presentation Forms Range:FB00-FB4F
 3. CJK Compatibility Forms
 4. Fullwidth ASCII Punctuation

2.1.3. ISO 646

ASCII的字符编码是美国自己定义的标准,而其对应的国际标准叫做ISO/IEC 646。

ISO/IEC是参考了多个国家的字符编码标准,其中主要是美国ASCII标准,然后制定出来的7
位的国际字符编码标准。

所以,此处,可以简单看成美国的国家标准ASCII和国际标准ISO/IEC 646,两者是是等价
的,即:

美国的国家的字符编码标准ASCII

=国际的字符编码标准ISO/IEC 646

2.2. 支持多种衍生拉丁字母的字符编码:EASCII和ISO 8859

计算机出现之后,从美国发展到欧洲,而由于欧洲很多国家中所用到的字符中,除了基本
的美国也用的那些拉丁字母之外,还有很多衍生的拉丁字母,而且是不同的国家用到的衍
生字符都不太相同,所以欧洲人也遇到类似的问题,即如何将自己国家的那些字符,在计
算机上显示出来,这就需要设计一个合理的字符编码,把所有这些字符都囊括其中。

即设计一个新编码标准,即兼容旧的ASCII的编码,又支持欧洲多个国家的那些衍生拉丁字
母。

这样的标准有两个,一个是EASCII编码标准,一个是国际标准ISO 8859字符编码标准。

2.2.1. EASCII

将ASCII中的第八位也用上,那么就是8位的字符编码了,然后将EASCII中0xA0-0xFF这部分
比ASCII码扩充出来的编码,用来表示表格符号、计算符号、希腊字母和特殊的拉丁符号等
,这样就可以实现支持那么多欧洲的衍生拉丁字母了,也就是这个EASCII字符编码了。但
是EASCII虽然解决了这些西欧语言的字符显示问题,但是对于其他语言显示,比如中文等
,还是无法处理显示。

目前,很少使用EASCII,常用的是下面要介绍的第 2.2.2 节 “ISO 8859”编码标准。

2.2.2. ISO 8859

2.2.2.1. ISO/IEC 8859出现的背景

前面已经提到了,正是因为ASCII等字符编码中,没有包括欧洲很多国家所用到的一些扩展
的拉丁字母,比如一些重音字母,带音标的等等,所以,才设计出新的这个ISO/IEC 8859
来支持这些字符。

最终设计出来的ISO/IEC 8859的字符集,支持很多欧洲的语言,包括丹麦语、荷兰语、德
语、意大利语、拉丁语、挪威语、葡萄牙语、西班牙语,瑞典语等。

2.2.2.2. ISO/IEC 8859的编码规则

我们已经知道了,ASCII是7位的单字节编码,其中0x20-0x7E的可见字符。

而ISO/IEC 8859,是在ASCII中的普通的可见字符(0x20-0x7E)的基础上,利用了ASCII的7
位编码所没有用到的第8位,这样就编码范围就从原先ASCII的0x00-0x7F多扩展出了
0x80-0xFF,其中的0xA0-0xFF部分,被ISO/IEC 8859编码所用到。

有别于ASCII的单个独立的编码规则,ISO/IEC 8859是一组编码规则的总称,其下包含了共
15个字符集,即ISO/IEC 8859-n,其中n=1,...,11,13,...,16。

关于这15种字符集是如何分类的,可以参考:表 A.2 “ISO/IEC 8859编码标准中的15种字
符集”

这15个字符集,每一个字符集,编码取值都是0xA0-0xFF,但是对于同一个值,不同字符集
所对应的字符,都不太一样。

此处截取那15个字符集的其中一部分,以便更加直观的了解不同字符集的区别:

图 2.1. ISO/IEC 8859的15个字符集的部分比较

ISO/IEC 8859的15个字符集的部分比较

完整的字符表,请参见表 A.2 “ISO/IEC 8859编码标准中的15种字符集”

[注 ASCII编码有时候也会写成ISO/IEC 8859-1编码
意] 另外,需要注意的是,对于原先的美国的英文字母,即普通的英语,其虽然没有重音
    ,音标等字母,但是由于其本身也还是包含在ASCII中,而ISO/IEC 8859-1包括了
    ASCII,所以,很多时候,对于英文字母来说,也仍会标明为ISO/IEC 8859-1编码。

2.2.2.3. ISO/IEC 8859的特点

对于ISO/IEC 8859所包含的全部字符,我们可以看到,对于基本的拉丁字母,那都是和
ASCII一样的,因为其就是借用了ASCII中的0x20-0x7F这段的编码,对应的是那些常见的可
显示的字符,而对于0xA0-0xFF这段空间,则是对于同一个值,不同的字符集中,对应着不
同的符号。

对于ISO/IEC 8859的编码方式是设计了多个字符集,我们不难看出,其之所以这么编码,
而不是像ASCII中每个编码值,都对应唯一的一个字符,那是因为,欧洲的全部所用的字符
数很多,如果是对于全部的欧洲用的字符都用一个对应的值来表示,那么这剩下的
0xA0-0xFF,甚至是0x80-0xFF,也都不够用的,因为0x80-0xFF128个值,当然不够表示欧
洲那几百上千的不同国家的不同字符。

所以,才会去设计出这么15个字符集,然后对于同一个值,你用了ISO/IEC 8859-n,就表示
对应的字符集中的那个特定的字符。

而上述做法的好处是,可以避免去用多个字节,比如两个字节(8×2=16位,可以表示最多2
^16=65536个字符)去表示一个单独的字符,即节省了存放数据的空间。

但是缺点是,比如你写一篇文章,中间出现了多个不同语系的不同的字符,那么此文章如
果用ISO/IEC 8859来编码的话,那么就无法单独存成某一种对应的字符集,即包含多个欧
洲国家不同语系的特殊字符的数据,无法用ISO/IEC 8859的某一个单独的字符集来表示出
来,即无法在同一个文章中支持显示不同语系的不同的字符。

当然,相对于亚洲字符,即中文,日文,韩文等字符来说,另外一个如果算的上是缺点的
话,那就是没有把咱亚洲字符考虑进去。

正因此,字符编码,才会继续演化出更加通用的,包含了世界上所有的字符的字符编码标
准:Unicode。

关于Unicode的详细解释请去看:第 2.4 节 “支持世界上几乎所有字符的字符编码:
Unicode”

此处先来说说,其他几个和ISO/IEC 8859相关的内容。

2.2.2.4. ISO/IEC 6429

可以看到,对应的ASCII编码取值范围是0x0-0x7F,而ISO/IEC 8859虽然是8位的单字节的
编码,但是只是除了ASCII的0x20-0x7E之外,只是指定了0xA0-0xFF,而中间还有一段的值
,即0x80-0x9F,却没有定义。

对此部分,是对应的ISO/IEC 6429编码标准所定义的,此标准还定义了0x0-0x1F,即ASCII
中的控制字符。

即,ISO/IEC 6429是专门定义对应的控制字符的,其中,0x0-0x1F部分称为C0控制(C0
control )字符,0x80-0x9F部分称为C1控制(C1 control)字符。

对应的C0 control部分,和ASCII编码重复定义了,但是两者含义都是一样的,所以编码规
则并不冲突。

可以算是,在控制字符领域,ISO/IEC 6429在ASCII的C0 control的基础上,对于由ASCII
的7位所扩展出的8位编码中的0x80-0x9F这部分,也做出了对应的定义。

简言之:

ISO/IEC 6429

= 0x0-0x1F + 0x80-0x9F

=7位编码ASCII中的0x0-0x1F + 扩展8位编码中的0x80-0x9F

=C0 control + C1 control

2.2.2.5. ISO 8859和ISO-8859的区别和联系

ISO 8859和ISO-8859,不是同一个东西。

注意,后者是ISO和8859中间带了一个小横短线的。

前者,ISO 8859是ISO/IEC 8859标准集合的简称,对应包含了 ISO/IEC 8859-n,其中n为除
去2之外的1到16,这共15种字符集合。

ISO-8859,是ISO-8859-n的简称,是IANA根据ISO/IEC 8859-n的标准,加上对应的前面提
到的普通的ASCII字符,和ISO/IEC 6429所定义的的控制字符,所制定的标准。

其中,由于ASCII中也包含了0x00-0x1F的控制字符,所以和ISO/IEC 6429中的C0控制字符
重复了,但是两者定义都是一样的,所以从字符编码上来说,不会产生任何冲突。

因此,ISO-8859-n所以可以表示:

ISO-8859

= ISO-8859-n的简称

= ISO 8859-n + ASCII + ISO/IEC 6429

其中,n=1,...,11,13,...,16,共15种编码集合。

对应的,ISO 8859-1和ISO-8859-1两者当前也是不一样的,其区别也是:

ISO-8859-1

= ISO 8859-1 + ASCII + ISO/IEC 6429

= ISO/IEC 8859-1 + ASCII + ISO/IEC 6429

2.2.2.5.1. 原先的ISO 8859-1和我们常说的ISO 8859-1

原先的ISO 8859-1即ISO/IEC 8859-1,其编码前面已经介绍过了,此处只是给出对应的字
符表:

图 2.2. ISO/IEC 8859-1字符集表

ISO/IEC 8859-1字符集表

其中绿色的部分,就是原先ISO 8859-1中未定义的部分。而这部分,之前已经解释了,是
在另外的一个标准ISO/IEC 6429中定义的。

此处,需要注意的是,目前大家最常见的,提到最多的ISO 8859-1,实际多数都是指的是
ISO-8859-1,即,是那个,既整合了0x20-0x1F这部分的普通的可以显示的ASCII字母(基
本的拉丁字母),又包含了对应的控制字符( C0 control和C1 control),同时也包含了
欧洲多数国家所用到那些字符(扩展的拉丁字母,即ISO/IEC 8859-1中所定义的那些字符
)。

总结起来就是:

常说的ISO 8859-1

= 实际上是ISO-8859-1

= ASCII + ISO/IEC 6429 + ISO 8859-1

= ASCII + ISO/IEC 6429 + ISO/IEC 8859-1

= (0x20-0x1F) + (0x0-0x1F + 0x80-0x9F) + (0xA0-0xFF)

= ASCII中的可见字符 + C0和C1的控制字符 + 欧洲多国所用的扩展的拉丁字符

2.3. 各种单字节编码标准的关系

不论是ASCII的7位的编码,还是后期演化出来的ISO/IEC 8859的8位的编码,都还是用单个
字节就可以表示一个字符,叫做单字节编码。

各种单字节编码之间的关系,可以用下面图表来解释:

表 2.2. 各种单字节编码标准之间的关系

            单字节编码                      单个字节=8位=2^8=256个字符
                                   用到了前7位=2^7=128个字      用到了第8位
 编码标准            注释                     符
                                   0x0-0x1F 0x206-0x7E 0x7F 0x80-0x9F 0xA0-0xFF
   ASCII         =ISO/IEC 646        yes       yes     yes                 
  ISO/IEC      =C0 control + C1      yes                       yes         
   6429            control
                                                                         yes
                                                                         yes
                                                                         yes
               =ISO/IEC 8859-n                                           yes
               =                                                         yes
               ISO/IEC 8859-1                                            yes
               ......                                                    yes
 ISO 8859      ISO/IEC 8859-11                 yes                       yes
               ISO/IEC 8859-13                                           yes
               ......                                                    yes
               ISO/IEC 8859-16                                           yes
                                                                         yes
                                                                         yes
                                                                         yes
                                                                         yes
                                                                         yes
                                                                         yes
                                                                         yes
                 =ISO-8859-n                                             yes
                 =                                                       yes
                 ISO-8859-1                                              yes
                 ......                                                  yes
 ISO-8859        ISO-8859-11         yes       yes     yes     yes       yes
                 ISO-8859-13                                             yes
                 ......                                                  yes
                 ISO-8859-16                                             yes
                                                                         yes
                                                                         yes
                                                                         yes
                                                                         yes

6    0x20是空格Space,常缩写为SP。

     此空格字符,严格意义上说,属于不可显示字符,因为显示或打印出来,也看不见

2.4. 支持世界上几乎所有字符的字符编码:Unicode

好了,介绍完了ISO/IEC 8859的种种,这下可以开始介绍Unicode了。

前面已经提到了,由于随着计算机的发展,自然会发展到亚洲各国和其他一些地方,然后
这些国家也遇到同样问题,即如何把自己的国家的字符,显示到对应的屏幕上。

2.4.1. Unicode和ISO 10646的关系

Unicode这个词的中文翻译,有译为万国码,单一码,标准万国码,但是最常见的翻译还是
统一码。

2.4.1.1. ISO 10646=UCS

国际标准组织ISO,定义了对应的编码标准ISO/IEC 10646,简称为ISO 10646,此标准所定
义的字符集,称作为通用字符集(Universal Character Set,UCS)。

并不是所有的系统都需要支持像組合字符这样的的先进机制。

因此ISO 10646指定了如下三种实现级别:

 1. 级别1:不支持组合字符和諺文字母字符。
 2. 级别2:类似于级别1,但在某些文字中,允许一列固定的组合字符,因为如果没有最
    起码的几个组合字符,UCS就不能完整地表达这些语言。
 3. 级别3:支持所有的通用字符集字符,如,可以在任意一个字符上加上一个箭头或一个
    鼻音化符號

即,对于多数的实际使用中,并不一样要求你实现包括世界上所有的字符,那就不一定非
的要实现对应的第三级别,很多时候只需要实现第一级别就足够涵盖平常所用到的大部分
的字符了。

我们平时会看到UCS-2,UCS-4,就是对应的ISO 10646标准中所定义的,对应的用2个字节
或4个字节去表示同一个字符。

2.4.1.2. Unicode 和ISO 10646的联系

历史上存在两个独立的尝试创立单一字符集的组织,即国际标准化组织(ISO)和多语言软
件制造商组成的统一码联盟。

前者开发的 ISO/IEC 10646 项目,后者开发的Unicode项目。

因此最初制定了不同的标准。

1991年前后,两个项目的参与者都认识到,世界不需要两个不兼容的字符集。于是,它们
开始合并双方的工作成果,并为创立一个单一编码表而协同工作。从Unicode 2.0开始,
Unicode采用了与ISO 10646-1相同的字库和字码;ISO也承诺,ISO 10646将不会超出
U+10FFFF的UCS-4编码赋值,以使得两者保持一致。

两个项目仍都存在,并独立地公布各自的标准,但统一码联盟和ISO/IEC JTC1/SC2都同意
保持两者标准的码表兼容,并紧密配合以保证之后的扩展也一致。

其各自的标准之间的对应关系如下:

表 2.3. ISO/IEC 10646与Unicode的版本对应关系

                         ISO/IEC 10646版本                           Unicode版
                                                                         本
ISO/IEC 10646-1:1993                                                 Unicode
                                                                     1.1
ISO/IEC 10646-1:2000                                                 Unicode
                                                                     3.0
ISO/IEC 10646-2:2001                                                 Unicode
                                                                     3.2
ISO/IEC 10646:2003                                                   Unicode
                                                                     4.0
ISO/IEC 10646:2003 plus Amendment 1                                  Unicode
                                                                     4.1
ISO/IEC 10646:2003 plus Amendment 1, Amendment 2, and part of        Unicode
Amendment 3                                                          5.0
ISO/IEC 10646:2003 plus Amendments 1 to 4                            Unicode
                                                                     5.1
ISO/IEC 10646:2003 plus Amendments 1 to 6                            Unicode
                                                                     5.2
ISO/IEC 10646:2011                                                   Unicode
                                                                     6.0

2.4.1.3. Unicode和ISO 10646的区别

统一码联盟公布的Unicode标准包含了ISO/IEC 10646-1实现级别3的基本多文种平面BMP。
在两个标准里,所有的字符都在相同的位置并且有相同的名字。

ISO/IEC 10646标准,就像ISO/IEC 8859标准一样,只不过是一个简单的字符集表。它定义
了一些编码的别名,指定了一些与标准有关的术语,并包括了规范说明,指定了怎样使用
UCS连接其他ISO标准的实现,比如ISO/IEC 6429和ISO/IEC 2022。还有一些与ISO紧密相关
的,比如ISO/IEC 14651是关于UCS字符串排序的。

Unicode标准,额外定义了许多与字符有关的语义符号学。Unicode详细说明了绘制某些语
言(如阿拉伯语)表达形式的算法,处理双向文字(比如拉丁文和希伯来文的混合文字)
的算法,排序与字符串比较所需的算法,等等。

所以,可以理解为,ISO 10646中定义了编码规则,定义了哪些值对应了哪些字符,而
Unicode不仅定义了这些编码规则,还定义了其他一些关于文字处理的细节算法等内容。

即:

Unicode

= ISO 10646的编码规则 + 某些语言的细节处理算法

对于一般人来说,Unicode 和 ISO 10646,虽然两者有些细节的区别,但是我们多数不用
去关系这点细节内容,而对于字符编码规则方面,此处可以简单的理解为:

Unicode

= ISO 10646编码标准

= 标准所制定的UCS字符集

2.4.2. Unicode编码规则

为了将世界上几乎所有的字符都涵盖了,那么就要了解世界上,有哪些字符。

除了之前ASCII的拉丁字母,ISO 8859所包含的欧洲多国用的字符之外,亚洲一些国家,包
括中文,日文,韩文等,尤其是中文,包含的字符数,大概有几万个。

因此,Unicode的编码就要设计的把这么多的字符都包含在内。

Unicode的编码方式与上面提到的ISO 10646的UCS概念相对应,目前实际应用的Unicode版
本对应于UCS-2,即2字节的UCS字符集,使用16位的编码空间。每个字符占用2个字节,这
样理论上一共最多可以表示2^16=65536个字符。基本满足各种语言的使用。实际上目前版
本的Unicode尚未填充满这16位编码,保留了大量空间作为特殊使用或将来扩展。

上述16位Unicode字符构成基本多文种平面(Basic Multilingual Plane,简称BMP)。最
新(但未实际广泛使用)的Unicode版本定义了16个辅助平面,两者合起来至少需要占据21
位的编码空间,比3字节略少。但事实上辅助平面字符仍然占用4字节编码空间,与UCS-4保
持一致。未来版本会扩充到ISO 10646-1实现级别3,即涵盖UCS-4的所有字符。

UCS-4是一个更大的尚未填充完全的31位字符集,加上恒为0的首位,共需占据32位,即4字
节。理论上最多能表示2^31=2147483648=21亿左右个字符,完全可以涵盖一切语言所用的
符号。

具体的取值范围和所对应的平面空间划分,参见图 2.3 “Unicode中的各种平面划分”

图 2.3. Unicode中的各种平面划分

Unicode中的各种平面划分

Unicode中的0-0xFFF的BMP中的任何一个编码的值,称为码点(Code Point),对应用
U+hhhh来表示,其中每个h 代表一个十六进制数位。

与UCS-2编码完全相同。对应的4字节UCS-4编码后两个字节一致,前两个字节的所有位均为
0。

2.4.3. Unicode字符编码所对应的存储和交换标准:UTF-8, UTF-16, UTF-32

需要注意的是,Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个
二进制代码应该如何存储。

比如,汉字“严”的Unicode是十六进制数4E25,转换成二进制数足足有15位(
100111000100101),也就是说这个符号的表示至少需要2个字节。表示其他更大的符号,
可能需要3个字节或者4个字节,甚至更多。

这里就有两个严重的问题,第一个问题是,如何才能区别Unicode和ASCII?计算机怎么知
道三个字节表示一个Unicode中的字符,而不是分别表示三个ASCII的字符呢?第二个问题
是,我们已经知道,英文字母只用一个字节表示就够了,如果Unicode统一规定,每个符号
用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储来说
是极大的浪费,文本文件的大小会因此大出二三倍,这是无法接受的。

它们造成的结果是:

 1. 出现了Unicode的多种存储方式,也就是说有许多种不同的二进制格式,可以用来表示
    Unicode
 2. Unicode在很长一段时间内无法推广,直到互联网的出现

2.4.3.1. UTF-8

互联网的普及,强烈要求出现一种统一的编码方式。UTF-8就是在互联网上使用最广的一种
Unicode的实现方式。其他实现方式还包括UTF-16和UTF-32,不过在互联网上基本不用。

重复一遍,这里的关系是,UTF-8是Unicode的实现方式之一。

UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符
号,根据不同的符号而变化字节长度。

UTF-8的编码规则很简单,只有二条:

  • 对于单字节的符号,字节的第一位设为0,后面7位为这个符号的Unicode码。因此对于
    英语字母,UTF-8编码和ASCII码是相同的
  • 对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的
    前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的Unicode码

下表总结了编码规则,字母x表示可用编码的位。

表 2.4. Unicode与UTF-8之间的编码映射关系

Unicode符号范围(十六进制)       UTF-8编码方式(二进制)
0000 0000-0000 007F       0xxxxxxx
0000 0080-0000 07FF       110xxxxx 10xxxxxx
0000 0800-0000 FFFF       1110xxxx 10xxxxxx 10xxxxxx
0001 0000-0010 FFFF       11110xxx 10xxxxxx 10xxxxxx 10xxxxxx

下面,还是以汉字“严”为例,演示如何实现UTF-8编码。

已知“严”的Unicode是4E25(100111000100101),根据上表,可以发现4E25处在第三行的
范围内(0000 0800-0000 FFFF),因此“严”的UTF-8编码需要三个字节,即格式是
“1110xxxx 10xxxxxx 10xxxxxx”。然后,从“严”的最后一个二进制位开始,依次从后向前
填入格式中的x,多出的位补0。这样就得到了,“严”的UTF-8编码是“11100100 10111000
10100101”,转换成十六进制就是E4B8A5。

2.4.3.2. Unicode与UTF-8之间的转换

通过上一节的例子,可以看到“严”的Unicode码是4E25,UTF-8编码是E4B8A5,两者是不一
样的。它们之间的转换可以通过程序实现。

在Windows平台下,有一个最简单的转化方法,就是使用内置的记事本小程序Notepad.exe
。打开文件后,点击"文件"→"另存为"会跳出一个对话框,在最底部有一个"编码"的下拉
条:

图 2.4. Notepad中的各种编码

Notepad中的各种编码

里面有四个选项:ANSI,Unicode,Unicode big Endian 和 UTF-8:

  • ANSI是默认的编码方式。对于英文文件是ASCII编码,对于简体中文文件是GB2312编码
    (只针对Windows简体中文版,如果是繁体中文版会采用Big5码)。
  • Unicode编码指的是UCS-2编码方式,即直接用两个字节存入字符的Unicode码。这个选
    项用的Little Endian格式
  • Unicode Big Endian编码与上一个选项相对应

    关于Little Endian和Big Endian,可以参考大端(Big Endian)与小端(Little
    Endian)详解

  • UTF-8编码,也就是上一节谈到的编码方法

选择完”编码方式“后,点击”保存“按钮,文件的编码方式就立刻转换好了。

下面,举一个实例:

打开”记事本“程序Notepad.exe,新建一个文本文件,内容就是一个”严“字,依次采用ANSI
,Unicode,Unicode big Endian 和 UTF-8编码方式保存。

然后,用文本编辑软件UltraEdit的”十六进制功能“,观察该文件的内部编码方式。

  • ANSI

    文件的编码就是两个字节“D1 CF”,这正是“严”的GB2312编码,这也暗示GB2312是采用
    大头方式存储的

  • Unicode

    编码是四个字节“FF FE 25 4E”,其中“FF FE”表明是小头方式存储,真正的编码是
    4E25。

  • Unicode big Endian

    编码是四个字节“FE FF 4E 25”,其中“FE FF”表明是大头方式存储。

  • UTF-8

    编码是六个字节“EF BB BF E4 B8 A5”,前三个字节“EF BB BF”表示这是UTF-8编码,
    后三个“E4B8A5”就是“严”的具体编码,它的存储顺序与编码顺序是一致的

2.4.3.2.1. 关于UTF-8的BOM:“EF BB BF”

对于UTF-8的BOM(Byte Order Mark),即“EF BB BF”,是对于UTF-8编码,微软自己添加
的,由此,会导致和其他很多软件等不兼容。而Unicode标准中,也不推荐此给UTF-8添加
“EF BB BF”的BOM。

刚去测试了一下,在Window XP中,将中文汉字“严”在记事本中另存为UTF-8之后,用
Notepad++去查看其十六进制的值,的确是“EF BB BF E4 B8 A5”,然后手动删除了“EF BB
BF”的BOM,保存后,再去用记事本打开,发现没了BOM的UTF-8,记事本也是可以正确显示
出“严”字的。所以,结论是:

 1. 给UTF-8加“EF BB BF”的BOM,是微软自己的做法,即微软发现编码是UTF-8的话,会给
    文件最开始加上“EF BB BF”
 2. Unicode的官方标准,不推荐这种做法,即不推荐给UTF-8加“EF BB BF”的BOM
 3. 所以,其他人写软件处理文字编码的话,最好不要给UTF-8加BOM。当然,如果你非得
    要兼容微软的做法,那么去解析不同编码的文件的话,针对UTF-8编码,就要考虑这个
    特殊的的BOM了

更多关于BOM的解释,参见第 2.7 节 “BOM”

2.5. 代码页Code Page

2.5.1. 什么是代码页(Code Page)

Code Page,是字符编码的另一种说法。

Code Page包含了一个表,表中的值,用于表示针对某种语言所用的字符集。

更简单点说,就是Code Page中,用一个数字编号,表示了所要采用何种字符编码,去编解
码相应的值,用于正确显示出相应的字符。

Code Page这一概念,源于IBM,后被其他常见广泛采用,包括Microsoft,SAP,Oracle等
。

这些常见,各自定义了一套自己的Code Page,给每一个code page号,指定一个字符编码
。

比如,对于众所周知的UTF-8编码,在IBM的Code Page中编号是1208,在微软中是65001,
在SAP中是4110。

接下来,主要介绍大家最常见的Windows的Code Page

2.5.2. Windows中的Code Page

如前所述,Windows中也有自己的一套Code Page的定义。

用对应的某个数字,Code Page Number,即Code Page中的标识符(Identifier),表示相
应的字符编码。

而一般Code Page也常缩写为CP

比如,CP936表示GBK中文编码,CP65001表示UTF-8编码,CP54936表示GB18030编码,CP950
表示BIG5繁体中文等等。

[提 C#中使用当前系统默认编码处理字符
示] 对于C#来说,处理字符时涉及可能在不同环境中使用的话,那么最好用系统默认编码
    :

    StreamReader reader = new StreamReader(path, System.Text.Encoding.Default);

2.5.2.1. Windows中的Code Page分类:ANSI和OEM

Windows中的Code Page,按照引用领域来划分,可以分为两类:ANSI Code Page和 OEM
Code Page

2.5.2.1.1. Windows的ANSI Code Page表

ANSI Code Page的官网正式叫法其实是Windows Code Page。但是由于ANSI Code Page被误
用的太广泛了,索性微软也就接受了此叫法,然后就叫做ANSI Code Page了。

类似地,ANSI Code Page=ANSI Windows Code Page

[提 Windows的Code Page为何被误称为ANSI Code Page
示] Windows的Code Page中用的最广泛的是Windows 1252,其用于英语和西欧语言字符。

    Windows 1252是基于ANSI草案(ANSI draft)而设计的。

    结果,本来叫做Windows Code Page,就被很多不熟悉的人误读为ANSI Code Page

    而实际上,(作为标准制定者的)ANSI和ISO,都从来没有去标准化Windows的Code
    Page,即没有为Windows Code Page指定过任何标准。

    但是呢,由于ANSI Code Page被误用的太普遍了,导致微软官方也都承认此叫法了。

    总之,记住一点,ANSI Code Page,就是Windows Code Page,就行了。

ANSI Code Page主要是用于Windows系统中,本地编码是非Unicode的,图形用户界面(GUI
)程序。

ANSI的Code Page相关的表格,参见第 A.3.1.1 节 “ANSI Code Page表”

2.5.2.1.2. Windows的OEM Code Page表

OEM Code Page主要是用于Windows系统中的命令行界面(console)程序,虚拟DOS。

OEM Code Page可以视为是DOS和IBM PC时代的(过渡)剩余产品。

除了ANSI Code Page之外,之所以又设计出一个OEM Code Page,是因为:

 1. 兼容性

    因为作为新的图形用户界面系统的Windows,也要兼容旧的命令行程序,即向后兼容性
    。

 2. 字体和硬件的要求

    字体和旧的VGA硬件建议,文字图形界面所用的编码,最好和Code Page 437兼容。

    多数的OEM的Code Page,和(非ASCII的)后半部分的CP437,都是公用同一套代码点
    (code point)的。

    一般的OEM Code Page的后半段编码,和ANSI Code Page,完全不同。不过,对于部分
    双字节编码的,定长的Code Page(如泰语的847,越南语的1258)和多字节CJK编码的
    Code Page(如932,936,949,950)来说,ANSI和OEM的Code Page,都用的同一套编码
    。

和OEM Code Page相关的表格,参见第 A.3.1.2 节 “OEM Code Page表”

2.5.2.1.3. 一些常见的Code Page表

其中,ANSI和OEM共有的一些Code Page,可参见第 A.3.1.3 节 “ANSI和OEM共有的Code
Page表”

而其他一些常见的Code Page,可参见第 A.3.1.4 节 “其他一些常见的Code Page表”

2.5.2.2. 所有的Code Page表

除了ANSI和OEM,以及ANSI,OEM共有的Code Page之外,其他还有很多Code Page定义。

关于所有的Windows中的Code Page的定义,可在微软官网[24]中找到。

此处已收录至第 A.3.2 节 “所有的Code Page相关的表格”,以方便查阅。

2.6. ANSI字符编码

2.6.1. ANSI是啥

ANSI,本身是American National Standards Institute的缩写,中文翻译为美国国家标准
学会

ANSI是个非营利组织,其负责制定美国国家标准。

2.6.2. ANSI编码规则

ANSI字符编码的规则,或者是其所包含的字符的由来,主要是:

 1. 0-127 (0x00-0x7F)

    完全和7位编码的ASCII字符集(ASA X3.4-1963)相同

 2. 128-159 (0x80-0x9F)

    一些可打印字符

    这部分的编码,与国际编码ISO 8859-1的做法不同,ISO 8859-1是将此部分编码用于
    控制字符

 3. 160-255 (0xA0-0FF)

    参考了ISO 8859-1中的字符

由此可以看出,ANSI中很多字符,和ISO-8859中的字符,看起来非常相似。

这就导致了很多人误以为,ANSI和ISO-8859是一回事呢。

总结:

ANSI

= Windows Code Page 1252

= Windows Codepage 1252

= Windows 1252

= CP 1252

= 共256个字符

= 0-127的ASCII + 128-159的可打印字符 + 160-255的和ISO 8859-1中类似的字符

2.6.3. ANSI (Windows 1252)编码表

关于ANSI(Windows 1252)编码表格,可以参考:

[20]

[35]

2.6.4. ANSI编码与ANSI的关系

既然ANSI负责制定美国的国标,而在计算机方面,由于计算机最早是从美国最开始发展的
,相应的所用到的字符编码方面,ANSI也制定了对应的标准,所以就叫做ANSI字符编码/
ANSI字符集,英文为ANSI Code/ANSI Encoding/ANSI set/ANSI charset

2.6.5. ANSI字符编码和Windows 1252

Windows为了支持英语和西欧字符,自己设计了一个编码,对应的在Code Page号是1252,
被称为Windows 1252。

Windows 1252的设计,是参考了ANSI草案(ANSI Draft)。

而ANSI draft后来发展成为正式的国际标准:ISO 8859-1

即,Windows 1252是在其成为正式标准ISO 8859-1之前而设计的,因此很容易理解,
Windows 1252和ISO 8859-1不是完全等同的。

下面就来简要说说两者的区别。

2.6.5.1. Windows 1252和ISO 8859-1之间的区别

Windows 1252和ISO 8859-1基本等同

有点不同的是,在128-159(0x80-0x9F)的范围的值,ISO 8859-1编码为控制字符,而微软
编码为可打印字符。

[提 提示
示]  1. 类似Windows的Code Page为何被误称为ANSI Code Page,Windows 1252也被误称
        为ANSI编码,所以此处也可以说是ANSI编码和ISO 8859-1之间的区别。
     2. 而由于ISO 8859-1对应的Latin-1的西欧语言,所以此处也可以称为ANSI编码和
        ISO Latin-1之间的区别,比如[19]
     3. 微软的此种变体,有各种叫法:ANSI/Windows-1252/Windows Latin-1

        甚至有些微软的程序将其叫做Western European (Windows)。

        更有甚至,由于不清楚,而错称其为ASCII

[注 注意
意] 因此,如果你把包含了128-159范围内的ISO Latin-1编码的文件,用Windows的记事本
    Notepad去另存为为ANSI的话,则会导致文件内容被错误处理了。

    因为本身的那些128-159的字符,是控制字符,但是却被Notepad识别为可打印的字符
    了。

总之,对于Windows 1252,目前的各种叫法,可以理解为:

ANSI = Windows 1252 = CP 1252 = Windows code page 1252 = Windows Latin-1

2.6.6. 为何"ANSI编码"(在Windows中)被称为"本地编码"

先说一下本地编码,所谓本地编码,即当前Windows中的二进制的值,用何种编码去解析,
然后显示出对应的该编码中的字符。

即,当然系统使用什么类型的编码。

而ANSI编码,根据前面内容得知,只是一个普通的对应于Windows 1252的一个编码而已。
并不是其他某些编码合集的总称。

但是有时候,却又看到有人把ANSI编码解释为“本地编码”,比如[22]

其意思,就是[30]中所说的,Windows code pages有时又被称为"active code pages",
"system active code pages"。

而作为微软用A表示ANSI版本的函数,W表示Wide,Unicode版本的函数,此时所有的A版本
的函数,就都用的是当前有效的Code Page,即"本地编码"了

其中,Windows系统中,当前有且只有一个active Windows code page。

也就意味着,此处所谓的ANSI编码,就相当于之前所说的Code Page了,即当前系统采用何
种编码去解析字符

也就是你当前系统中设置的本地编码为何种编码,然后系统中,遇到需要解析的字符,就
按照你所设置的本地编码去解析了。

比如,本身对于中文GBK编码的字符,如果你本地编码设置为UTF-8,那么按照UTF-8编码去
解析出来的GBK字符,当前就是乱码了。

而只有正确设置为GBK,才能正确解析原本就是GBK编码后的字符,才能正确显示出中文。

同理,用GBK编码去解析原本用UTF-8编码后的字符,也会导致乱码。

[提 提示
示] 这种乱码问题,常常会在和编码打交道的事情中遇到

    比如Python中在命令行cmd中打印输出字符串,如果本身字符串是GBK编码的,那么你
    的cmd中的本地编码,就要设置为是936 (ANSI/OEM - Simplified Chinese GBK),这
    样中文字符才能正确显示。

    当然,如果你本身输出的字符中,即包含UTF-8编码的字符,又包含GBK编码的字符,
    那么则是无论如何设置,都是无法同时正常显示的。除非你转换为Unicode编码,然后
    让Python输出函数自动处理,才可以正确显示。

2.7. BOM

2.7.1. BOM是什么

BOM是一个Unicode字符。

BOM用于指示文件/字符流的大小端(字节序)。

不同编码所对应的BOM不同。

2.7.2. 为何需要BOM

即使对于字符进行了某种编码,可以正确读取/写入,可以正常显示字符了。

但是遇到将一个文件/字符流,从一个地方传输到另一个地方的话,就会遇到一些问题:

比如,本地是一个用UTF-16 LE编码的文件,传入到网络的另一端,接受者怎么知道你用的
是UTF-16 LE的编码,而不是UTF-16 BE呢?

如果不知道,用了错误的UTF-16 BE去解码,且不是会导致乱码问题了。

所以,就需要一个说明和解释,目前的做法就是,在文件头(字符流的开始),添加一个
BOM,Byte Order Mark,字节序的标记,用于表示此编码是LE还是BE。

2.7.3. BOM表

相关的不同编码所用的BOM,参见摘录自[36]的表 A.8 “不同编码所用的BOM”

关于UTF-8的BOM: EF BB BF,可参考第 2.4.3.2.1 节 “关于UTF-8的BOM:“EF BB BF””

2.8. 中文字符编码标准

2.8.1. GB2312,CP936,GBK,GB18030,GB13000

2.8.1.1. GB2312

1980年,中国制定了GB2312-80,一共收录了 7445 个字符,包括 6763 个汉字和 682 个
其它符号。

GB2312-80,简称为GB2312。

在 Windows 中的代码页(Code Page)是 CP936。

2.8.1.2. GB13000

1993年,国际标准Unicode 1.1版本推出,收录中国大陆、台湾、日本及韩国通用字符集的
汉字,总共有20,902个。

中国大陆订定了等同于Unicode 1.1版本的“GB 13000.1-93”,简称为GB13000。

GB13000,显然包含的GB2312已有的文字和其他很多为包含的文字,如GB 2312-80推出以后
才简化的汉字(如“啰”),部分人名用字(如中国前总理朱镕基的“镕”字),台湾及香港
使用的繁体字,日语及朝鲜语汉字等。

2.8.1.3. GBK

微软,对GB2312-80的扩展,即利用GB 2312-80未使用的编码空间,收录所有的GB
13000.1-93和Unicode 1.1之中的汉字全部字符,制定了GBK编码。

GBK 收录了 21886 个符号,它分为汉字区和图形符号区。汉字区包括 21003 个字符。

GBK 作为对 GB2312 的扩展,在现在的 Windows 系统中仍然使用代码页 CP936 表示,但
是同样的 936 的代码页跟一开始的 936 的代码页只支持 GB2312 编码不同,现在的 936
代码页支持 GBK 的编码,GBK 同时也向下兼容GB2312 编码。

所以,技术编码上,GBK兼容旧的GB2312,但是编码方式和GB13000不同,不兼容GB13000,
但是所包含文字上,算是和GB13000相同。

2.8.1.4. GB18030

GBK自身并非国家标准,只是曾由国家技术监督局标准化司、电子工业部科技与质量监督司
公布为“技术规范指导性文件”。

原始GB13000一直未被业界采用,2000年,国家出了标准GB18030-2000,简称GB18030,技
术上兼容GBK而非GB13000,取代了 GBK1.0,成了正式的国家标准。

该标准收录了 27484 个汉字,同时还收录了藏文、蒙文、维吾尔文等主要的少数民族文字
。

现在的PC平台必须支持 GB18030 ,对嵌入式产品暂不作要求。所以手机、MP3 一般只支持
GB2312。

GB18030 在 Windows 中的代码页是 CP54936。

这么多汉字编码标准的关系,总结起来就是第 2.8.2 节 “各种中文字符编码标准的关系”
中所介绍的。

2.8.2. 各种中文字符编码标准的关系

(中国大陆的标准)GB 13000.1-93

=(国际标准)Unicode 1.1

(中国大陆标准)GB2312-80

= 简称GB2312

= Windows系统中的原先的CP936

(微软制定的)GBK

= (微软在编码方面)对 GB2312 的扩展

= (微软在所包含字符方面上包含了)GB 13000.1-93 + 其他部分汉字+ 台湾和香港的繁
体 + 日语 + 朝鲜汉字

= Unicode 1.1 + 其他部分汉字+ 台湾和香港的繁体 + 日语 + 朝鲜汉字

对于GBK:

  • 在编码方面:向下兼容GB2312,但是和GB 13000不同
  • 在内容方面:等价于GB13000

微软中现在的新的CP936

= GBK

=兼容旧的GB2312

在技术编码方面上,演化顺序为:

ASCII ⇒ GB2312 ⇒ GBK ⇒ GB18030

后者对之前的,都是支持之前的编码,即向下兼容,即同一个字符,在这些编码中,都是
同样的值,后面的标准,支持更多的字符。

区分中文编码的方法是高字节的最高位不为 0。

按照程序员的称呼,GB2312、GBK 到 GB18030 都属于双字节字符集 (DBCS)

表 2.5. 中文字符相关编码标准

 编码标准             别名           标准所属           包含字符
ASCII                                国际通用  
GB2312      微软Windows中以前的CP936 中国大陆 6763 个汉字和 682 个其它符号
Unicode 1.1                          国际通用 20,902个字符
GB13000                              中国大陆 20,902个字符
GBK         微软Windows中现在的CP936 微软     21886 个符号
GB18030     微软Windows中的CP54936   中国大陆 27484 个汉字+其他少数民族字符

2.9. 字符存储(交换)标准

下表列出了常见的字符编码的标准,及其对应的交换存储时候所用的标准:

表 2.6. 字符(存储)交换标准

               字符编码标准                存储(交换/传输)标准
    包含字符        字符编码领域的叫法
                 ASCII
英文             ==ISO/IEC 646             ASCII

欧洲多国的字符   ISO 8859                   
                                           UTF1-8
通用(的任何)字符 Unicode                   UTF1-16
                                           UTF1-32
                 GB2312
                 ==GB2312-80
                 ==GB
简体中文2        ==GB03                    EUC4-CN

                 GBK
                 GB18030
                                           CCCII
                 BIG5
繁体中文         ==大五码                  CNS-11643
                 ==五大码
                                           EUC4-TW

                 JIS X 0208                Shift JIS
日文             == JIS C 6226和JIS X 0212 ISO-2022-JP
                                           EUC4-JP
                 JIS X 0213                EUC-JISX0213
韩文             KS X 1001                 EUC4-KR
                 == KS C 5601

1    UTF==Unicode Transformation Format==Unicode转换格式

2    关于中文字符编码的各种标准的演化,可以参考中文字符编码标准+Unicode+Code
     Page

3    关于GB0,另外还有其他的GBn,也是关于中文的国家标准:

      1. GB1==GB/T 12345 – 90==《信息交换用汉字编码字符集第一辅助集》
      2. GB2==GB/T 7589 – 87==《信息交换用汉字编码字符集第二辅助集》
      3. GB3==GB 13131 – 91==《信息交换用汉字编码字符集第三辅助集》
      4. GB4==GB/T 7590 – 87==《信息交换用汉字编码字符集第四辅助集》
      5. GB5==GB 13132 – 91==《信息交换用汉字编码字符集第五辅助集》

     关于GB的含义,即国标的首字母。其中,强制标准冠以“GB”,推荐标准冠以“GB/T”。

4    EUC==Extended Unix Code

2.10. 字形和你所看到的字符的关系

对于某个字符,其字形是固定的,是对应的字符编码标准,即编码集中所定义好了的。比
如,入门的“入”,这一撇一捺,是连在一起的,你不能写错了,写成左右分开的,那错写
成了八个的“八”了。而这样的字符的形状,简称字形,是编码中定义好的,你不能随便乱
写。

但是同一个字符,具体的字体,大小等,则是按照自己喜好去设置的,是留给其他软件来
处理的,比如宋体的“宋”这个汉字,不同的字体,会显示出来不同的效果:

图 2.5. 汉字“宋”的不同字体

汉字“宋”的不同字体

这样的对于同一个字符的后期处理,即想要用什么样的字体,什么样的大小来显示等,都
是后期软件,比如浏览器,微软的word等文本编辑器中去设置的。

参考书目

[1] 【转】字符编码笔记:ASCII,Unicode和UTF-8

[2] 拉丁字母

[3] 衍生拉丁字母

[4] ASCII

[5] ISO/IEC 6429

[6] ASCII字符集中的功能控制字符

[7] ISO/IEC 646

[8] ISO/IEC 8859

[9] 国家标准代码

[10] EUC

[11] EBCDIC

[12] ISO/IEC 8859-1

[13] 通用字符集

[14] 大端(Big Endian)与小端(Little Endian)详解

[15] 中文字符编码标准+Unicode+Code Page

[16] UTF-8

[17] ANSI

[18] Windows 1252

[19] Cast of Characters- ASCII, ANSI, UTF-8 and all that

[20] ANSI character set and equivalent Unicode and HTML characters

[21] ASCII vs ANSI Encoding

[22] 字符,字节和编码

[23] http://www.sttmedia.com/unicode-ansiASCII and ANSI

[24] Code Page Identifiers

[25] 什么是Unicode?什么是UTF-8?

[26] Code Page

[27] 什么是字符集、编码、代码页(code page)

[28] GBK

[29] 中文编码 GB2312||GBK||GB18030||GB13000 详解

[30] Code Pages

[31] Code Pages Supported by Windows -- Windows Code Pages

[32] Code Pages Supported by Windows -- OEM Code Pages

[33] Code Pages

[34] Windows code page

[35] Microsoft Windows Codepage 1252 (ANSI)

[36] Byte Order Mark

[37] Byte Order Mark (BOM)

附录 A. 编码相关的表格

目录

A.1. ASCII编码表(0-127)
A.2. ISO/IEC 8859编码标准中的15种字符集
A.3. Code Page表格

    A.3.1. 常见的ANSI和OEM的Code Page的表格

        A.3.1.1. ANSI Code Page表
        A.3.1.2. OEM Code Page表
        A.3.1.3. ANSI和OEM共有的Code Page表
        A.3.1.4. 其他一些常见的Code Page表

    A.3.2. 所有的Code Page相关的表格

A.4. 不同编码所用的BOM

A.1. ASCII编码表(0-127)

表 A.1. ASCII编码表(0-127)

十六进  十进  字  十六进  十进   字符   十六进  十进  字  十六进  十进    字符
  制     制   符    制     制             制     制   符    制     制
0       0     NUL 20      32    [space] 40      64    @   60      96    '
1       1     SOH 21      33    !       41      65    A   61      97    a
2       2     STX 22      34    "       42      66    B   62      98    b
3       3     ETX 23      35    #       43      67    C   63      99    c
4       4     EOT 24      36    $       44      68    D   64      100   d
5       5     ENQ 25      37    %       45      69    E   65      101   e
6       6     ACK 26      38    &       46      70    F   66      102   f
7       7     BEL 27      39    `       47      71    G   67      103   g
8       8     BS  28      40    (       48      72    H   68      104   h
9       9     HT  29      41    )       49      73    I   69      105   i
0a      10    LF  2a      42    *       4a      74    J   6a      106   j
0b      11    VT  2b      43    +       4b      75    K   6b      107   k
0c      12    FF  2c      44    ,       4c      76    L   6c      108   l
0d      13    CR  2d      45    -       4d      77    M   6d      109   m
0e      14    SO  2e      46    .       4e      78    N   6e      110   n
0f      15    SI  2f      47    /       4f      79    O   6f      111   o
10      16    DLE 30      48    0       50      80    P   70      112   p
11      17    DC1 31      49    1       51      81    Q   71      113   q
12      18    DC2 32      50    2       52      82    R   72      114   r
13      19    DC3 33      51    3       53      83    S   73      115   s
14      20    DC4 34      52    4       54      84    T   74      116   t
15      21    NAK 35      53    5       55      85    U   75      117   u
16      22    SYN 36      54    6       56      86    V   76      118   v
17      23    ETB 37      55    7       57      87    W   77      119   w
18      24    CAN 38      56    8       58      88    X   78      120   x
19      25    EM  39      57    9       59      89    Y   79      121   y
1a      26    SUB 3a      58    :       5a      90    Z   7a      122   z
1b      27    ESC 3b      59    ;       5b      91    [   7b      123   {
1c      28    FS  3c      60    <       5c      92    \   7c      124   |
1d      29    GS  3d      61    =       5d      93    ]   7d      125   }
1e      30    RS  3e      62    >       5e      94    ^   7e      126   ~
1f      31    US  3f      63    ?       5f      95    _   7f      127   [delete]

A.2. ISO/IEC 8859编码标准中的15种字符集

表 A.2. ISO/IEC 8859编码标准中的15种字符集

  ISO/IEC      英文别名                          中文解释
  8859-n
ISO/IEC     Latin-1        西欧语言
8859 -1
ISO/IEC     Latin-2        中欧语言
8859 -2
ISO/IEC     Latin-3        南欧语言。世界语也可用此字符集显示。
8859 -3
ISO/IEC     Latin-4        北欧语言
8859 -4
ISO/IEC     Cyrillic       斯拉夫语言
8859 -5
ISO/IEC     Arabic         阿拉伯语
8859 -6
ISO/IEC     Greek          希腊语
8859 -7
ISO/IEC     Hebrew         希伯来语(视觉顺序);ISO 8859-8-I是希伯来语(逻辑顺
8859 -8                    序)
ISO/IEC     Latin-5 或     它把Latin-1的冰岛语字母换走,加入土耳其语字母
8859 -9     Turkish
ISO/IEC     Latin-6 或     北日耳曼语支,用来代替Latin-4
8859 -10    Nordic
ISO/IEC     Thai           从泰国的 TIS620 标准字集演化而来
8859 -11
ISO/IEC     Latin-7 或     波罗的语族
8859 -13    Baltic Rim
ISO/IEC     Latin-8 或     凯尔特语族
8859 -14    Celtic
ISO/IEC     Latin-9        西欧语言,加入Latin-1欠缺的芬兰语字母和大写法语重音
8859 -15                   字母,以及欧元(€)符号。
ISO/IEC     Latin-10       东南欧语言。主要供罗马尼亚语使用,并加入欧元符号。
8859 -16

A.3. Code Page表格

A.3.1. 常见的ANSI和OEM的Code Page的表格

A.3.1.1. ANSI Code Page表

ANSI Code Page中,字符编码都是用的8位单字节,所以也被称为SBCS (Single Byte
Character Set) Codepages,即单字节字符集的代码页

表 A.3. ANSI的SBCS Code Page

代码页Code Page    对应的字符集Character Set
Code Page 1250  Central and East European Latin
Code Page 1251  Cyrillic
Code Page 1252  West European Latin
Code Page 1253  Greek
Code Page 1254  Turkish
Code Page 1255  Hebrew
Code Page 1256  Arabic
Code Page 1257  Baltic
Code Page 1258  Vietnamese
Code Page 874   Thai

A.3.1.2. OEM Code Page表

OEM的Code Page如下:

表 A.4. OEM的Code Page

代码页Code Page                  对应的字符集Character Set
Code Page 437   original IBM PC Code Page==USA
Code Page 720   Arabic
Code Page 737   Greek
Code Page 775   Estonian, Lithuanian and Latvian
Code Page 850   "Multilingual (Latin-1)" (Western European languages)
Code Page 852   "Slavic (Latin-2)" (Central and Eastern European languages)
Code Page 855   Cyrillic
Code Page 857   Turkish
Code Page 858   "Multilingual" with euro symbol
Code Page 860   Portuguese
Code Page 861   Icelandic
Code Page 862   Hebrew
Code Page 863   French (Quebec French)
Code Page 865   Danish/Norwegian Differs from 437
Code Page 869   Greek
Code Page 874   Thai

A.3.1.3. ANSI和OEM共有的Code Page表

ANSI和OEM共有Code Page,是一些DBCS (Double Byte Character Set) Codepages,即双
字节字符集的代码页。

表 A.5. ANSI和OEM共有的DBCS Code Page

代码页Code Page 对应的字符集Character Set
Code Page 932   Japanese
Code Page 936   GBK - Simplified Chinese
Code Page 949   Korean
Code Page 950   BIG5 - Traditional Chinese

A.3.1.4. 其他一些常见的Code Page表

其他一些常见的Code Page如下:

表 A.6. 一些常见的Code Page

 代码页Code                       对应的字符集Character Set
    Page
Code Page     UTF-16LE Unicode little-endian
1200
Code Page     UTF-16BE Unicode big-endian
1201
Code Page     UTF-7 Unicode
65000
Code Page     UTF-8 Unicode
65001
Code Page     Macintosh Roman encoding (followed by several other Mac character
10000         sets)
Code Page     Macintosh Cyrillic encoding
10007
Code Page     Macintosh Central European encoding
10029
Code Page     US-ASCII The classic US 7 bit character set with no char larger
20127         than 127
Code Page     ISO-8859-1 (followed by ISO-8859-2 to ISO-8859-15)
28591

A.3.2. 所有的Code Page相关的表格

表 A.7. 微软的代码页标识符(Code Page Identifiers)

Identifier        .NET Name                   Additional information
037        IBM037                  IBM EBCDIC US-Canada
437        IBM437                  OEM United States
500        IBM500                  IBM EBCDIC International
708        ASMO-708                Arabic (ASMO 708)
709                                Arabic (ASMO-449+, BCON V4)
710                                Arabic - Transparent Arabic
720        DOS-720                 Arabic (Transparent ASMO); Arabic (DOS)
737        ibm737                  OEM Greek (formerly 437G); Greek (DOS)
775        ibm775                  OEM Baltic; Baltic (DOS)
850        ibm850                  OEM Multilingual Latin 1; Western European
                                   (DOS)
852        ibm852                  OEM Latin 2; Central European (DOS)
855        IBM855                  OEM Cyrillic (primarily Russian)
857        ibm857                  OEM Turkish; Turkish (DOS)
858        IBM00858                OEM Multilingual Latin 1 + Euro symbol
860        IBM860                  OEM Portuguese; Portuguese (DOS)
861        ibm861                  OEM Icelandic; Icelandic (DOS)
862        DOS-862                 OEM Hebrew; Hebrew (DOS)
863        IBM863                  OEM French Canadian; French Canadian (DOS)
864        IBM864                  OEM Arabic; Arabic (864)
865        IBM865                  OEM Nordic; Nordic (DOS)
866        cp866                   OEM Russian; Cyrillic (DOS)
869        ibm869                  OEM Modern Greek; Greek, Modern (DOS)
870        IBM870                  IBM EBCDIC Multilingual/ROECE (Latin 2); IBM
                                   EBCDIC Multilingual Latin 2
874        windows-874             ANSI/OEM Thai (same as 28605, ISO 8859-15);
                                   Thai (Windows)
875        cp875                   IBM EBCDIC Greek Modern
932        shift_jis               ANSI/OEM Japanese; Japanese (Shift-JIS)
936        gb2312                  ANSI/OEM Simplified Chinese (PRC,
                                   Singapore); Chinese Simplified (GB2312)
949        ks_c_5601-1987          ANSI/OEM Korean (Unified Hangul Code)
950        big5                    ANSI/OEM Traditional Chinese (Taiwan; Hong
                                   Kong SAR, PRC); Chinese Traditional (Big5)
1026       IBM1026                 IBM EBCDIC Turkish (Latin 5)
1047       IBM01047                IBM EBCDIC Latin 1/Open System
1140       IBM01140                IBM EBCDIC US-Canada (037 + Euro symbol);
                                   IBM EBCDIC (US-Canada-Euro)
1141       IBM01141                IBM EBCDIC Germany (20273 + Euro symbol);
                                   IBM EBCDIC (Germany-Euro)
1142       IBM01142                IBM EBCDIC Denmark-Norway (20277 + Euro
                                   symbol); IBM EBCDIC (Denmark-Norway-Euro)
1143       IBM01143                IBM EBCDIC Finland-Sweden (20278 + Euro
                                   symbol); IBM EBCDIC (Finland-Sweden-Euro)
1144       IBM01144                IBM EBCDIC Italy (20280 + Euro symbol); IBM
                                   EBCDIC (Italy-Euro)
1145       IBM01145                IBM EBCDIC Latin America-Spain (20284 + Euro
                                   symbol); IBM EBCDIC (Spain-Euro)
1146       IBM01146                IBM EBCDIC United Kingdom (20285 + Euro
                                   symbol); IBM EBCDIC (UK-Euro)
1147       IBM01147                IBM EBCDIC France (20297 + Euro symbol); IBM
                                   EBCDIC (France-Euro)
1148       IBM01148                IBM EBCDIC International (500 + Euro
                                   symbol); IBM EBCDIC (International-Euro)
1149       IBM01149                IBM EBCDIC Icelandic (20871 + Euro symbol);
                                   IBM EBCDIC (Icelandic-Euro)
                                   Unicode UTF-16, little endian byte order
1200       utf-16                  (BMP of ISO 10646); available only to
                                   managed applications
1201       unicodeFFFE             Unicode UTF-16, big endian byte order;
                                   available only to managed applications
1250       windows-1250            ANSI Central European; Central European
                                   (Windows)
1251       windows-1251            ANSI Cyrillic; Cyrillic (Windows)
1252       windows-1252            ANSI Latin 1; Western European (Windows)
1253       windows-1253            ANSI Greek; Greek (Windows)
1254       windows-1254            ANSI Turkish; Turkish (Windows)
1255       windows-1255            ANSI Hebrew; Hebrew (Windows)
1256       windows-1256            ANSI Arabic; Arabic (Windows)
1257       windows-1257            ANSI Baltic; Baltic (Windows)
1258       windows-1258            ANSI/OEM Vietnamese; Vietnamese (Windows)
1361       Johab                   Korean (Johab)
10000      macintosh               MAC Roman; Western European (Mac)
10001      x-mac-japanese          Japanese (Mac)
10002      x-mac-chinesetrad       MAC Traditional Chinese (Big5); Chinese
                                   Traditional (Mac)
10003      x-mac-korean            Korean (Mac)
10004      x-mac-arabic            Arabic (Mac)
10005      x-mac-hebrew            Hebrew (Mac)
10006      x-mac-greek             Greek (Mac)
10007      x-mac-cyrillic          Cyrillic (Mac)
10008      x-mac-chinesesimp       MAC Simplified Chinese (GB 2312); Chinese
                                   Simplified (Mac)
10010      x-mac-romanian          Romanian (Mac)
10017      x-mac-ukrainian         Ukrainian (Mac)
10021      x-mac-thai              Thai (Mac)
10029      x-mac-ce                MAC Latin 2; Central European (Mac)
10079      x-mac-icelandic         Icelandic (Mac)
10081      x-mac-turkish           Turkish (Mac)
10082      x-mac-croatian          Croatian (Mac)
12000      utf-32                  Unicode UTF-32, little endian byte order;
                                   available only to managed applications
12001      utf-32BE                Unicode UTF-32, big endian byte order;
                                   available only to managed applications
20000      x-Chinese_CNS           CNS Taiwan; Chinese Traditional (CNS)
20001      x-cp20001               TCA Taiwan
20002      x_Chinese-Eten          Eten Taiwan; Chinese Traditional (Eten)
20003      x-cp20003               IBM5550 Taiwan
20004      x-cp20004               TeleText Taiwan
20005      x-cp20005               Wang Taiwan
20105      x-IA5                   IA5 (IRV International Alphabet No. 5,
                                   7-bit); Western European (IA5)
20106      x-IA5-German            IA5 German (7-bit)
20107      x-IA5-Swedish           IA5 Swedish (7-bit)
20108      x-IA5-Norwegian         IA5 Norwegian (7-bit)
20127      us-ascii                US-ASCII (7-bit)
20261      x-cp20261               T.61
20269      x-cp20269               ISO 6937 Non-Spacing Accent
20273      IBM273                  IBM EBCDIC Germany
20277      IBM277                  IBM EBCDIC Denmark-Norway
20278      IBM278                  IBM EBCDIC Finland-Sweden
20280      IBM280                  IBM EBCDIC Italy
20284      IBM284                  IBM EBCDIC Latin America-Spain
20285      IBM285                  IBM EBCDIC United Kingdom
20290      IBM290                  IBM EBCDIC Japanese Katakana Extended
20297      IBM297                  IBM EBCDIC France
20420      IBM420                  IBM EBCDIC Arabic
20423      IBM423                  IBM EBCDIC Greek
20424      IBM424                  IBM EBCDIC Hebrew
20833      x-EBCDIC-KoreanExtended IBM EBCDIC Korean Extended
20838      IBM-Thai                IBM EBCDIC Thai
20866      koi8-r                  Russian (KOI8-R); Cyrillic (KOI8-R)
20871      IBM871                  IBM EBCDIC Icelandic
20880      IBM880                  IBM EBCDIC Cyrillic Russian
20905      IBM905                  IBM EBCDIC Turkish
20924      IBM00924                IBM EBCDIC Latin 1/Open System (1047 + Euro
                                   symbol)
20932      EUC-JP                  Japanese (JIS 0208-1990 and 0121-1990)
20936      x-cp20936               Simplified Chinese (GB2312); Chinese
                                   Simplified (GB2312-80)
20949      x-cp20949               Korean Wansung
21025      cp1025                  IBM EBCDIC Cyrillic Serbian-Bulgarian
21027                              (deprecated)
21866      koi8-u                  Ukrainian (KOI8-U); Cyrillic (KOI8-U)
28591      iso-8859-1              ISO 8859-1 Latin 1; Western European (ISO)
28592      iso-8859-2              ISO 8859-2 Central European; Central
                                   European (ISO)
28593      iso-8859-3              ISO 8859-3 Latin 3
28594      iso-8859-4              ISO 8859-4 Baltic
28595      iso-8859-5              ISO 8859-5 Cyrillic
28596      iso-8859-6              ISO 8859-6 Arabic
28597      iso-8859-7              ISO 8859-7 Greek
28598      iso-8859-8              ISO 8859-8 Hebrew; Hebrew (ISO-Visual)
28599      iso-8859-9              ISO 8859-9 Turkish
28603      iso-8859-13             ISO 8859-13 Estonian
28605      iso-8859-15             ISO 8859-15 Latin 9
29001      x-Europa                Europa 3
38598      iso-8859-8-i            ISO 8859-8 Hebrew; Hebrew (ISO-Logical)
50220      iso-2022-jp             ISO 2022 Japanese with no halfwidth
                                   Katakana; Japanese (JIS)
50221      csISO2022JP             ISO 2022 Japanese with halfwidth Katakana;
                                   Japanese (JIS-Allow 1 byte Kana)
50222      iso-2022-jp             ISO 2022 Japanese JIS X 0201-1989; Japanese
                                   (JIS-Allow 1 byte Kana - SO/SI)
50225      iso-2022-kr             ISO 2022 Korean
50227      x-cp50227               ISO 2022 Simplified Chinese; Chinese
                                   Simplified (ISO 2022)
50229                              ISO 2022 Traditional Chinese
50930                              EBCDIC Japanese (Katakana) Extended
50931                              EBCDIC US-Canada and Japanese
50933                              EBCDIC Korean Extended and Korean
50935                              EBCDIC Simplified Chinese Extended and
                                   Simplified Chinese
50936                              EBCDIC Simplified Chinese
50937                              EBCDIC US-Canada and Traditional Chinese
50939                              EBCDIC Japanese (Latin) Extended and
                                   Japanese
51932      euc-jp                  EUC Japanese
51936      EUC-CN                  EUC Simplified Chinese; Chinese Simplified
                                   (EUC)
51949      euc-kr                  EUC Korean
51950                              EUC Traditional Chinese
52936      hz-gb-2312              HZ-GB2312 Simplified Chinese; Chinese
                                   Simplified (HZ)
                                   Windows XP and later: GB18030 Simplified
54936      GB18030                 Chinese (4 byte); Chinese Simplified
                                   (GB18030)
57002      x-iscii-de              ISCII Devanagari
57003      x-iscii-be              ISCII Bengali
57004      x-iscii-ta              ISCII Tamil
57005      x-iscii-te              ISCII Telugu
57006      x-iscii-as              ISCII Assamese
57007      x-iscii-or              ISCII Oriya
57008      x-iscii-ka              ISCII Kannada
57009      x-iscii-ma              ISCII Malayalam
57010      x-iscii-gu              ISCII Gujarati
57011      x-iscii-pa              ISCII Punjabi
65000      utf-7                   Unicode (UTF-7)
65001      utf-8                   Unicode (UTF-8)

A.4. 不同编码所用的BOM

表 A.8. 不同编码所用的BOM

 编码类型   BOM值(16进制)  BOM值(10进制)
UTF-8       EF BB BF      239 187 191
UTF-16 (BE) FE FF         254 255
UTF-16 (LE) FF FE         255 254
UTF-32 (BE) 00 00 FE FF   0 0 254 255
UTF-32 (LE) FF FE 00 00   255 254 0 0
            2B 2F 76 38   43 47 118 56
UTF-7       2B 2F 76 39   43 47 118 57
            2B 2F 76 2B   43 47 118 43
            2B 2F 76 2F   43 47 118 47
UTF-1       F7 64 4C      247 100 76
UTF-EBCDIC  DD 73 66 73   221 115 102 115
SCSU        0E FE FF      14 254 255
BOCU-1      FB EE 28      251 238 40
GB-18030    84 31 95 33   132 49 149 51

转载:Xlib库使用基础

Basic Graphics Programming With The Xlib Library
http://users.actcom.co.il/~choo/lupg/tutorials/xlib-programming/xlib-programming.html
1.  Preface
2. The Client And Server Model Of The X Window System
3. GUI programming - the Asynchronous Programming Model
4. Basic Xlib Notions
1. The X Display
2. The GC - Graphics Context
3. Object Handles
4. Memory Allocation For Xlib Structures
5. Events
5. Compiling Xlib-Based Programs
6. Opening And Closing The Connection To An X Server
7. Checking Basic Information About A Display
8. Creating A Basic Window - Our "hello world" Program
9. Drawing In A Window
1. Allocating A Graphics Context (GC)
2. Drawing Primitives - Point, Line, Box, Circle...
10. X Events
1. Registering For Event Types Using Event Masks
2. Receiving Events - Writing The Events Loop
3. Expose Events
4. Getting User Input
1. Mouse Button Click And Release Events
2. Mouse Movement Events
3. Mouse Pointer Enter And Leave Events
4. The Keyboard Focus
5. Keyboard Press And Release Events
5. X Events - A Complete Example
11. Handling Text And Fonts
1. The Font Structure
2. Loading A Font
3. Assigning A Font To A Graphics Context
4. Drawing Text In A Window
12. Windows Hierarchy
1. Root, Parent And Child Windows
2. Events Propagation
13. Interacting With The Window Manager
1. Window Properties
2. Setting The Window Name And Icon Name
3. Setting Preferred Window Size(s)
4. Setting Miscellaneous Window Manager Hints
5. Setting An Application's Icon
14. Simple Window Operations
1. Mapping And UN-Mapping A Window
2. Moving A Window Around The Screen
3. Resizing A Window
4. Changing Windows Stacking Order - Raise And Lower
5. Iconifying And De-Iconifying A Window
6. Getting Info About A Window
15. Using Colors To Paint The Rainbow
1. Color Maps
2. Allocating And Freeing Color Maps
3. Allocating And Freeing A Color Entry
4. Drawing With A Color
16. X Bitmaps And Pixmaps
1. What Is An X Bitmap? An X Pixmap?
2. Loading A Bitmap From A File
3. Drawing A Bitmap In A Window
4. Creating A Pixmap
5. Drawing A Pixmap In A Window
6. Freeing A Pixmap
17. Messing With The Mouse Cursor
1. Creating And Destroying A Mouse Cursor
2. Setting A Window's Mouse Cursor
--------------------------------------------------------------------------------
Preface
This tutorial is the first in a series of "would-be" tutorials about graphical programming in the X window environment. By itself, it is useless. A real X programmer usually uses a much higher level of abstraction, such as using Motif (or its free version, lesstiff), GTK, QT and similar libraries. However, we need to start somewhere. More than this, knowing how things work down below is never a bad idea.
After reading this tutorial, one would be able to write very simple graphical programs, but not programs with a descent user interface. For such programs, one of the previously mentioned libraries would be used.
--------------------------------------------------------------------------------
X窗口系统的客户/服务器模式
当初开发X窗口系统的主要目的只有一个,那就是灵活性。这个灵活性的意思就是说一件东西虽然看起来是在这工作,但却实际上是工作在很远的地方。因此,较低 等级的实现部分就必须提供绘制窗口,处理用户输入,画画,使用颜色等工作的工具。在这个要求下,决定了系统被分成了两部分,客户端和服务器端。客户端决定 做什么,服务器端执行真正的绘图和接受用户的输入并把它发给客户端。
这种模式与我们一般习惯的客户端和服务器端的概念是正好相反的。在我们的情况下,用户就坐在服务器端控制的机器前,而客户端这时却是运行在远程主机上。服 务器端控制着显示屏,鼠标和键盘。一个客户端也许正连接着服务器端,要求给它画一个窗口(或者是一堆),并要求服务器端把用户对它的窗口的输入传给它。结 果,好几个客户端可能连接到了一个服务器端上-有的在运行一个电子邮件软件,有的在运行一个网页浏览器等。当用户输入了指令给窗口,服务器端就会把指令打 包成事件传给控制那个窗口的客户端,客户端根据接受到的事件决定干什么然后发送请求让服务器端去画什么。
以上介绍的会话都是通过X消息协议传输的。该协议是实现在TCP/IP协议上的,它允许在一个网络里的客户端访问这个网络里的任何服务器端。最后,X服务 器端可以和客户端运行在同一台机器上以获得性能优化(注意,一个X协议事件可能会达到上百KB),例如使用共享内存,或者使用Unix域socket(在 一个Unix系统的两个进程间创建一个本地通道进行通信的方法)。
--------------------------------------------------------------------------------
图形用户接口(GUI)编程-异步编程模式
不像我们通常的令人愉快的程序,一个GUI程序通常使用异步编程模式,也就是下面要介绍的"事件驱动编程"。这个"事件驱动编程"的意思是说程序通常都处 于空闲状态,等待从X服务器发来的事件,等收到了事件,才根据事件做相应的事情。一个事件可能是"用户在屏幕某处x,y点击了鼠标左键",或者是"你控制 的窗口需要被重画"。因为程序要回应用户的请求,同时还需要刷新自己的请求队列,因此需要程序尽可能使用较短的事件来处理一个事件(例如,作为一条公认的 准则,不能超过200毫秒)。
这也暗示着当然存在需要程序处理很长时间才能完成的事件(例如一个到远程服务器的网络连接,或者是连接一个数据库,或者是不幸的要处理一个超大文件的复制 工作)。这都要求程序使用异步方式来处理而不是通常的同步方式。这时候就应该采用各种各样的异步编程方法来进行这些耗时的工作了,或者干脆把它们交给一个 线程或进程来进行。
根据以上的说明,一个GUI程序就应该像以下的方式来工作:
进行初始化工作
连接X服务器
进行与X相关的初始化工作
进行循环
从X服务器那里接受下一个事件
根据收到的事件发送各种绘图指令给X服务器
如果事件是个退出事件,结束循环
关闭与X服务器的连接
进行资源释放工作
--------------------------------------------------------------------------------
Xlib的基本思想
X协议是非常复杂的,为了大家不用再辛辛苦苦把时间浪费在实现它上面,就有了一个叫"Xlib"的库。这个库提供了访问任何X服务器的非常底层的手段。因 为X协议已经被标准化了,理论上客户程序使用任何Xlib的实现都可以访问任何X服务器。在今天,这看起来可能很琐碎,但如果回到那个使用字符终端和专有 绘图方法的时代,这应该是一个很大的突破吧。实际上,你很快发现围绕瘦客户机,窗口终端服务器等领域会有许多多么令人兴奋的事情。
--------------------------------------------------------------------------------
X显示
使用XLib的基本思想就是X显示。它代表了一个打开的到X服务器的连接的结构。它隐藏了一个保存有从X服务器来的事件的队列,和一个保存客户程序准备发往服务器的请求队列。在Xlib里,这个结构被命名为显示"Display"。
当我们打开了一个到X服务器的连接,库就会返回一个指向这个结构的指针。然后,我们就可以使用这个指针来使用Xlib里各种各样的函数。
--------------------------------------------------------------------------------
GC - 图形上下文
当我们进行各种绘图操作(图形,文本等)的时候,我们也许会使用许多参数来指定如何绘制,前景,背景,使用什么颜色,使用什么字体等等,等等。为了避免为 每个绘图函数设置数量惊人的参数,我们使用一个叫"GC"的图形上下文结构。我们在这个结构里设置各种绘图参数,然后传给绘图函数就行了。这应当是一个非 常方便的方法吧,尤其当我们在进行一连串操作中使用相同的参数时。
--------------------------------------------------------------------------------
对象句柄
当X服务器为我们创建了各种各样的对象的时候 - 例如窗口,绘图区和光标 - 相应的函数就会返回一个句柄。这是一个存在在X服务器空间中的对象的一个标识-而不是在我们的应用程序的空间里。在后面我们就可以使用Xlib的函数通过 句柄来操纵这些对象。X服务器维护了一个实际对象到句柄的映射表。Xlib提供了各种类型来定义这些对象。虽然这些类型实际上只是简单的整数,但我们应该 继续使用这些类型的名字 - 理由是为了可移植。
--------------------------------------------------------------------------------
Xlib结构的内存分配
Xlib的接口使用了各种类型的结构。有些可以由用户直接来分配内存,有些则只能使用专门的Xlib库函数来分配。在使用库来分配的情况,库会生成有适当 初始参数的结构。这对大家来说是非常方便的,指定初始值对于不太熟练的程序员来说是非常头疼的。记住-Xlib想要提供非常灵活的功能,这也就意味着它也 会变得非常复杂。提供初始值设置的功能将会帮助那些刚开始使用X的程序员们,同时不会干扰那些高高手们。
在释放内存时,我们使用与申请的同样方法来释放(例如,使用free()来释放malloc()申请的内存)。所以,我们必须使用XFree()来释放内存。
事件
一个叫"XEvent"的结构来保存从X服务器那里接受到的事件。Xlib提供了非常大量的事件类型。XEvent包括事件的类型,以及与事件相关的数据 (例如在屏幕什么地方生成的事件,鼠标键的事件等等),因此,要根据事件类型来读取相应的事件里的数据。这时,XEvent结构使用c语言里的联合来保存 可能的数据(如果你搞不清楚c的联合是怎么回事,那你就得花点时间再读读你的教科书了)。结果,我们就可能受到XExpose事件,一个XButton事 件,一个XMotion事件等等。
--------------------------------------------------------------------------------
编译基于Xlib的程序
编译基于Xlib的程序需要与Xlib库连接。可以使用下面的命令行:
cc prog.c -o prog -lX11
如果编译器报告找不到X11库,可以试着加上"-L"标志,像这样:
cc prog.c -o prog -L/usr/X11/lib -lX11
或者这样(针对使用X11的版本6)
cc prog.c -o prog -L/usr/X11R6/lib -lX11
在SunOs 4 系统上,X的库被放到了 /usr/openwin/lib
cc prog.c -o prog -L/usr/openwin/lib -lX11
等等,具体情况具体分析
--------------------------------------------------------------------------------
打开,关闭到一个X服务器的连接
一个X程序首先要打开到X服务器的连接。我们需要指定运行X服务器的主机的地址,以及显示器编号。X窗口允许一台机器开多个显示。然而,通常只有一个编号 为"0"的显示。如果我们想要连接本地的显示(例如进行显示的机器同时又是客户程序运行的机器),我们可以直接使用":0"来连接。现在我们举例,连接一 台地址是"simey"的机器的显示,我们可以使用地址"simey:0",下面演示如何进行连接
#include  /* defines common Xlib functions and structs. */
.
.
/* this variable will contain the pointer to the Display structure */
/* returned when opening a connection. */
Display* display;
/* open the connection to the display "simey:0". */
display = XOpenDisplay("simey:0");
if (display == NULL) {
fprintf(stderr, "Cannot connect to X server %s\n", "simey:0");
exit (-1);
}
注意,通常要为X程序检查是否定义了环境变量"DISPLAY",如果定义了,可以直接使用它来作为XOpenDisplay()函数的连接参数。
当程序完成了它的工作且需要关闭到X服务器的连接,它可以这样做:
XCloseDisplay(display);
这会使X服务器关闭所有为我们创建的窗口以及任何在X服务器上申请的资源被释放。当然,这并不意味着我们的客户程序的结束。
--------------------------------------------------------------------------------
检查一个显示的相关基本信息
一旦我们打开了一个到X服务器的连接,我们应该检查与它相关的一些基本信息:它有什么样的屏幕,屏幕的尺寸(长和宽),它支持多少颜色(黑色和白色?灰度 级?256色?或更多),等等。我们将演示一些有关的操作。我们假设变量"display"指向一个通过调用XOpenDisplay()获得的显示结 构。
/* this variable will be used to store the "default" screen of the */
/* X server. usually an X server has only one screen, so we&#39;re only */
/* interested in that screen. */
int screen_num;
/* these variables will store the size of the screen, in pixels. */
int screen_width;
int screen_height;
/* this variable will be used to store the ID of the root window of our */
/* screen. Each screen always has a root window that covers the whole */
/* screen, and always exists. */
Window root_window;
/* these variables will be used to store the IDs of the black and white */
/* colors of the given screen. More on this will be explained later. */
unsigned long white_pixel;
unsigned long black_pixel;
/* check the number of the default screen for our X server. */
screen_num = DefaultScreen(display);
/* find the width of the default screen of our X server, in pixels. */
screen_width = DisplayWidth(display, screen_num);
/* find the height of the default screen of our X server, in pixels. */
screen_height = DisplayHeight(display, screen_num);
/* find the ID of the root window of the screen. */
root_window = RootWindow(display, screen_num);
/* find the value of a white pixel on this screen. */
white_pixel = WhitePixel(display, screen_num);
/* find the value of a black pixel on this screen. */
black_pixel = BlackPixel(display, screen_num);
还有很多其它的宏来帮助我们获取显示的信息,你可以在Xlib里的参考里找到。另外还有很多相当的函数可以完成相同的工作。
--------------------------------------------------------------------------------
创建一个基本的窗口 - 我们的"Hello world"程序
在我们获得一些窗口的基本信息之后,我们就可以开始创建我们的第一个窗口了。Xlib支持好几个函数来创建窗口,它们其中的一个是XCreateSimpleWindow()。这个函数使用很少的几个参数来指定窗口的尺寸,位置等。以下是它完整的参数列表:
Display* display
指向显示结构的指针
Window parent
新窗口的父窗口的ID。
int x
窗口的左上X坐标(单位为屏幕像素)
int y
窗口的左上Y坐标(单位为屏幕像素)
unsigned int width
窗口的宽度(单位为屏幕像素)
unsigned int height
窗口的高度(单位为屏幕像素)
unsigned int border_width
窗口的边框宽度(单位为屏幕像素)
unsigned long border
用来绘制窗口边框的颜色
unsigned long background
用来绘制窗口背景的颜色
让我们创建一个简单的窗口,它的宽度是屏幕宽的1/3,高度是屏幕高的1/3,背景色是白色,边框是黑色,边框的宽度是2个像素。该窗口将会被放置到屏幕的左上角。
/* this variable will store the ID of the newly created window. */
Window win;
/* these variables will store the window&#39;s width and height. */
int win_width;
int win_height;
/* these variables will store the window&#39;s location. */
int win_x;
int win_y;
/* calculate the window&#39;s width and height. */
win_width = DisplayWidth(display, screen_num) / 3;
win_height = DisplayHeight(display, screen_num) / 3;
/* position of the window is top-left corner - 0,0. */
win_x = win_y = 0;
/* create the window, as specified earlier. */
win = XCreateSimpleWindow(display,
RootWindow(display, screen_num),
win_x, win_y,
win_width, win_height,
win_border_width, BlackPixel(display, screen_num),
WhitePixel(display, screen_num));
事实上我们创建窗口并不意味着它将会被立刻显示在屏幕上,在缺省情况下,新建的窗口将不会被映射到屏幕上,它们是不可见的。为了能让我们创建的窗口能被显示到屏幕上,我们使用函数XMapWindow():
XMapWindow(win);
如果想察看目前为止我们所举的例子的代码,请参看源程序simple-window.c。你将会发现两个新的函数 -
XFlush() 和XSync()。函数XFlush()刷新所有处于等待状态的请求到X服务器 - 非常像函数fflush()刷新所有的内容到标准输出。XSync()也刷新所有处于等待状态的请求,接着等待X服务器处理完所有的请求再继续。在一个一 般的程序里这不是必须的(据此你可以发现我们什么时候只是写一个一般的程序),但我们现在把它们提出来,尝试在有或没有这些函数的情况下程序不同的行为。
--------------------------------------------------------------------------------
在窗口里绘制
在窗口里绘图可以使用各种绘图函数 - 画点,线,圈,矩形,等等。为了能在一个窗口里绘图,我们首先需要定义各种参数 - 如线的宽度,使用什么颜色,等等。这都需要使用一个图形上下文(GC)。
--------------------------------------------------------------------------------
申请一个图形上下文(GC)
如我们已经提到的,一个图形上下文定义一些参数来使用绘图函数。因此,为了绘制不同的风格,我们可以在一个窗口里使用多个图形上下文。使用函数 XCreateGC()可以申请到一个新的图形上下文,如以下例(在这段代码里,我们假设"display"指向一个显示结构,"win"是当前创建的一 个窗口的ID):
/* this variable will contain the handle to the returned graphics context. */
GC gc;
/* these variables are used to specify various attributes for the GC. */
/* initial values for the GC. */
XGCValues values = CapButt | JoinBevel;
/* which values in &#39;values&#39; to check when creating the GC. */
unsigned long valuemask = GCCapStyle | GCJoinStyle;
/* create a new graphical context. */
gc = XCreateGC(display, win, valuemask, &values);
if (gc  0)
break;
/* ok, now draw the line... */
XDrawLine(display, win, gc, 0, 100, 400, 100);
break;
--------------------------------------------------------------------------------
获取用户输入
就目前来说,用户的输入主要从两个地方过来 - 鼠标和键盘。有各种各样的事件帮助我们来获取用户的输入 - 一个键盘上的键被按下了,一个键盘上的键被松开了,鼠标光标离开了我们的窗口,鼠标光标进入了我们的窗口等等。
--------------------------------------------------------------------------------
鼠标按键事件和松开事件
我们为我们的窗口处理的第一个事件是鼠标按钮时间。为了注册一个这样的事件类型,我们将追加以下的面具
ButtonPressMask
通知我们窗口中的任何一个鼠标键按下动作
ButtonReleaseMask
通知我们窗口中的任何一个鼠标键松开动作
在我们的事件循环中通过switch来检查以下的事件类型
ButtonPress
在我们的窗口上一个鼠标键被按下了
ButtonRelease
在我们的窗口上一个鼠标键被松开了
在事件结构里,通过"an_event.xbutton"来获得事件的类型,另外它还包括下面这些有趣的内容:
Window window
事件发送的目标窗口的ID(如果我们为多个窗口注册了事件)
int x, y
从窗口的左上坐标算起,鼠标键按下时光标在窗口中的坐标
int button
鼠标上那个标号的按钮被按下了,值可能是Button1,Button2,Button3
Time time
事件被放进队列的时间。可以被用来实现双击的处理下面的例子,将演示我们如何在鼠标的位置画点,无论我们何时收到编号为1的按钮的"鼠标按下"的事件时我 们画一个黑点,收到编号为2的按钮的"鼠标按下"的事件时我们擦掉那个黑点(例如画一个白点)。我们假设现在有两个GC,gc_draw使用下面的代码
case ButtonPress:
/* store the mouse button coordinates in &#39;int&#39; variables. */
/* also store the ID of the window on which the mouse was */
/* pressed. */
x = an_event.xbutton.x;
y = an_event.xbutton.y;
the_win = an_event.xbutton.window;
/* check which mouse button was pressed, and act accordingly. */
switch (an_event.xbutton.button) {
case Button1:
/* draw a pixel at the mouse position. */
XDrawPoint(display, the_win, gc_draw, x, y);
break;
case Button2:
/* erase a pixel at the mouse position. */
XDrawPoint(display, the_win, gc_erase, x, y);
break;
default: /* probably 3rd button - just ignore this event. */
break;
}
break;
--------------------------------------------------------------------------------
鼠标光标的进入和离开事件
另一个程序通常会感兴趣的事件是,有关鼠标光标进入一个窗口的领域以及离开那个窗口的领域的事件。有些程序利用该事件来告诉用户程序现在在焦点里面。为了注册这种事件,我们将会在函数XSelectInput()里注册几个面具。
EnterWindowMask
通知我们鼠标光标进入了我们的窗口中的任意一个
LeaveWindowMask
通知我们鼠标光标离开了我们的窗口中的任意一个
我们的事件循环中的分支检查将检查以下的事件类型
EnterNotify
鼠标光标进入了我们的窗口
LeaveNotify
鼠标光标离开了我们的窗口
这些事件类型的数据结构通过例如"an_event.xcrossing"来访问,它还包含以下有趣的成员变量:
Window window
事件发送的目标窗口的ID(如果我们为多个窗口注册了事件)
Window subwindow
在一个进入事件中,它的意思是从那个子窗口进入我们的窗口,在一个离开事件中,它的意思是进入了那个子窗口,如果是"none",它的意思是从外面进入了我们的窗口。
int x, y
从窗口的左上坐标算起,事件产生时鼠标光标在窗口中的坐标
int mode
鼠标上那个标号的按钮被按下了,值可能是Button1,Button2,Button3
Time time
事件被放进队列的时间。可以被用来实现双击的处理
unsigned int state
这个事件发生时鼠标按钮(或是键盘键)被按下的情况 - 如果有的话。这个成员使用按位或的方式来表示
Button1Mask
Button2Mask
Button3Mask
Button4Mask
ShiftMask
LockMask
ControlMask
Mod1Mask
Mod2Mask
Mod3Mask
Mod4Mask
它们的名字是可以扩展的,当第五个鼠标钮被按下时,剩下的属性就指明其它特殊键(例如Mod1一般是"ALT"或者是"META"键)
Bool focus
当值是True的时候说明窗口获得了键盘焦点,False反之
--------------------------------------------------------------------------------
键盘焦点
在屏幕上同时会有很多窗口,但同一时间只能有一个窗口获得键盘的使用。X服务器是如何知道哪一个窗口可以发送键盘事件呢?这个是通过使用键盘焦点来实现 的。在同一时间只能有一个窗口获得键盘焦点。Xlib函数里存在函数允许程序让指定窗口获得键盘焦点。用户通常使用窗口管理器来为窗口设置焦点(通常是点 击窗口的标题栏)。
一旦我们的窗口获得了键盘焦点,每个键的按下和松开都将引起服务器发送事件给我们的程序(如果已经注册了这些事件的类型)。
--------------------------------------------------------------------------------
键盘键按下和松开事件
如果我们程序控制的窗口获得了键盘焦点,它就可以接受按键的按下和松开事件。为了注册这些事件的类型,我们就需要通过函数XSelectInput()来注册下面的面具。
KeyPressMask
通知我们的程序什么时候按键被按下了
KeyPressMask
通知我们的程序什么时候按键被松开了
我们的事件循环中的分支检查将检查以下的事件类型
Window window
事件发送的目标窗口的ID(如果我们为多个窗口注册了事件)
unsigned int keycode
被按下(或松开)的键的编码。这是一些X内部编码,应该被翻译成一个键盘键符号才能方便使用,将会在下面介绍。
int x, y
从窗口的左上坐标算起,事件产生时鼠标光标在窗口中的坐标
Time time
事件被放进队列的时间。可以被用来实现双击的处理
unsigned int state
这个事件发生时鼠标按钮(或是键盘键)被按下的情况 - 如果有的话。这个成员使用按位或的方式来表示
Button1Mask
Button2Mask
Button3Mask
Button4Mask
ShiftMask
LockMask
ControlMask
Mod1Mask
Mod2Mask
Mod3Mask
Mod4Mask
它们的名字是可以扩展的,当第五个鼠标钮被按下时,剩下的属性就指明其它特殊键(例如Mod1一般是"ALT"或者是"META"键)
如我们前面所提到的,按键编码对我们来说是没有什么意义的,它是由连接着X服务器的键盘产生的硬件级编码并且是与某个型号的键盘相关的。为了能解释到底是 哪个按键产生的事件,我们把它翻译成已经被标准化了的按键符号。我们可以使用函数XKeycodeToKeysym()来完成这个翻译工作。该函数使用3 个参数:一个显示的指针,要被翻译的键盘编码,和一个索引(我们在这里使用"0")。标准的Xlib键编码可以参考文 件"X11/keysymdef.h"。在下面的例子里我们使用函数XkeycodeToKeysym来处理按键操作,我们讲演示如何以以下顺序处理按键 事件:按"1"键将会在鼠标的当前位置下画一个点。按下"DEL"键将擦除那个点。按任何字母键(a至z,大写或小写)将在标准输出里打印。其它的按键将 会被无视。假设下面的"case"段代码是在一个消息循环中。
case KeyPress:
/* store the mouse button coordinates in &#39;int&#39; variables. */
/* also store the ID of the window on which the mouse was */
/* pressed. */
x = an_event.xkey.x;
y = an_event.xkey.y;
the_win = an_event.xkey.window;
{
/* translate the key code to a key symbol. */
KeySym key_symbol = XKeycodeToKeysym(display, an_event.xkey.keycode, 0);
switch (key_symbol) {
case XK_1:
case XK_KP_1: /* &#39;1&#39; key was pressed, either the normal &#39;1&#39;, or */
/* the &#39;1&#39; on the keypad. draw the current pixel. */
XDrawPoint(display, the_win, gc_draw, x, y);
break;
case XK_Delete: /* DEL key was pressed, erase the current pixel. */
XDrawPoint(display, the_win, gc_erase, x, y);
break;
default: /* anything else - check if it is a letter key */
if (key_symbol >= XK_A && key_symbol = XK_a && key_symbol fid);
"fid"领域是一个XFontStruct结构用来为各种请求识别各种装载的字体。
--------------------------------------------------------------------------------
在一个窗口中绘制文本
我们一旦为我们的GC装载了字体,我们就可以使用例如函数XDrawString(),在我们的窗口里绘制文本。该函数可以在窗口里的一个给定位置里绘制文本。给定的位置将是从被绘制的文本的左下算起,下面是它的例子:
/* assume that win_width contains the width of our window, win_height */
/* contains the height of our window, and &#39;win&#39; is the handle of our window. */
/* some temporary variables used for the drawing. */
int x, y;
/* draw a "hello world" string on the top-left side of our window. */
x = 0;
y = 0;
XDrawString(display, win, gc, x, y, "hello world", strlen("hello world"));
/* draw a "middle of the road" string in the middle of our window. */
char* text_string = "middle of the road";
/* find the width, in pixels, of the text that will be drawn using */
/* the given font. */
int string_width = XTextWidth(font_info, text_string, strlen(text_string));
/* find the height of the characters drawn using this font. */
int fond_height = font_info->ascent + font_info->descent;
x = (win_width - string_width) / 2;
y = (win_width - font_height) / 2;
XDrawString(display, win, gc, x, y, "hello world", strlen("hello world"));
以下的说明应该可以使程序更清楚:
函数XTextWidth()被用来预测字符串的长度,当它使用指定字体进行绘制时。它被用来检查那里是开始那里是结束使它看起来占据着窗口的中央
一个字体的两个名字为"ascent"和"descent"的属性用来指定字体的高。基本上,一个字体的字符是画在一条假象的横线上的。一些字符被画在横 线上面,一些画在下面。最高的字符是被画在"font_info->ascent"线上的,最低的部分则 在"font_info->descent"线下面。因此,这两个值得和指明了字体的高度。
上面的源程序可以参考文件simple-text.c
X窗口的组织体系
当窗口被显示在X服务器上时,它们通常按照一定组织体系来排序 - 每个窗口可以有子窗口,每个子窗口又可以有自己的子窗口。让我们来查看这个组织体系的一些特性,看看它们是如何来影响例如绘画和事件等处理。
根窗口,父窗口和子窗口
每一个屏幕上都有一个根窗口。根窗口总是占据整个屏幕尺寸。这个窗口无法被销毁,改变尺寸或者图标化。当一个应用程序创建了一些窗口,它先创建至少一个顶 层窗口。在被映射到屏幕上后,这个窗口成为一个根窗口的直接子窗口。这个窗口在被映射到屏幕上之前,窗口管理器被告知什么发生了,然后,窗口管理器获得特 权成为新顶层窗口的"父亲"。这通常被用来增加一个会包含新窗口的窗口和绘制框架,标题栏,系统菜单等。
一旦一个顶层窗口(当然它实际上不是一个顶层窗口,因为窗口管理器已经成为它的父窗口了)被创建了,应用程序可以在它里面创建它的子窗口。一个子窗口只能 在它的父窗口里显示 - 如果试图把它移动到外面,出去的部分将被父窗口的边框给切掉。任何窗口都可以包含一个以上的子窗口,在这种情况下,这些子窗口将被放置在应用的内部栈上。 当一个顶层窗口被打开 - 它的所有子窗口也将随着它被打开。
以下例子演示如何在一个给定的叫"win"的窗口里打开一个子窗口。
Lets see how to create a child window inside a given window ''win''.
/* this variable will store the handle of the newly created child window. */
Window child_win;
/* these variables will store the window''s width and height. */
int child_win_width = 50;
int child_win_height = 30;
/* these variables will store the window''s location. */
/* position of the child window is top-left corner of the */
/* parent window. - 0,0.  */
int child_win_x = 0;
int child_win_y = 0;
/* create the window, as specified earlier. */
child_win = XCreateSimpleWindow(display, win, child_win_x, child_win_y,                                          child_win_width, child_win_height,  child_win_border_width,                                          BlackPixel(display, screen_num),
WhitePixel(display, screen_num));
事件传递
先前我们已经讨论了事件传递 - 如果一个窗口收到了一个它不处理的事件 - 它就把该事件发到它的父窗口去。如果那个父窗口也不处理该事件 - 那个父窗口就把该事件发到它的父窗口上去,接下来依此类推。这种行为对一个简单的Xlib程序是没什么用的,但对于抽象级更高的绘图库是有用的。这些抽象 级更高的绘图库通常把某个特定窗口的事件联系到一个函数上去。在这种情况下,发送事件到特定的窗口并用适当的函数来处理就非常有用。
与窗口管理器进行交互
在我们察看了如何创建和绘制窗口之后,我们回过头来看一下我们的窗口是如何与它们的环境 - 整个屏幕和其它窗口进行交互的。首先,我们的程序需要与窗口管理器进行交互。窗口管理器有责任装饰被绘制的窗口(例如增加框架,一个图标化的按钮,一个系 统菜单,一个标题栏),同时在窗口被图标化时绘制图标。它还管理屏幕里的窗口排列顺序以及其它可管理的任务。我们需要给它各种提示以让它以我们需要的方式 来对待我们的窗口。
窗口属性
许多与窗口管理器交流的参数都通过叫"properties"的数据来传递。X服务器把这些属性贴到各种窗口上,同时把它们存储成一种可以被各种架构的系 统所能读取的格式(记住,一个X客户程序可能运行在一台远程主机上)。属性可以是各种类型 - 数字,字符串,等等。大部分的窗口管理器提示函数使用文本属性。一个叫XStringListToTextProperty()的函数可以把C语言的字符 串转换成X文本属性,转换后的结果就可以传给各色Xlib函数。以下是一个例子:
/* This variable will store the newly created property. */
XTextProperty window_title_property;
/* This is the string to be translated into a property. */
char* window_title = "hello, world";
/* translate the given string into an X property. */
int rc = XStringListToTextProperty(&window_title, 1, &window_title_property);
/* check the success of the translation. */
if (rc == 0) { fprintf(stderr, "XStringListToTextProperty - out of memory\n");
exit(1);}
函数XStringListToTextProperty()接收一个C字符串矩阵(在我们的例子里只有一个)和一个指向XTextProperty型变 量的指针为参数,合并C字符串里的属性把值传到XTextProperty型变量里。成功时它返回一个非0值,失败时返回0(例如,没有足够的内存来完成 操作)。
设置窗口名字和图标名字
我们需要做的第一件事就是给我们的窗口设置名字。使用函数XSetWMName()。窗口管理器也许会把这个名字显示在窗口标题栏或是在任务栏上。该函数 接受3个参数:一个指向显示的指针,一个窗口句柄,和一个包含有我们设置的名字的XTextProperty变量。下面是我们如何做的:
/* assume that window_title_property is our XTextProperty, and is */
/* defined to contain the desired window title.                   */
XSetWMName(display, win, &window_title_property);
为了设置我们的窗口的图标化名字,我们将用相同的方式使用函数XSetWMIconName()。
/* this time we assume that icon_name_property is an initialized */
/* XTextProperty variable containing the desired icon name.      */
XSetWMIconName(display, win, &icon_name_property);
设置满意的窗口尺寸
在各种情况下,我们希望让窗口管理器知道我们指定的窗口尺寸以及只允许用户在我们的限定下改变窗口尺寸。例如,一个终端窗口(像xterm),我们总是要 求我们的窗口可以包含全部的行和列,因此我们就不能从中间截断我们的显示。在其它情况下,我们不希望我们的窗口可以被改变尺寸(像绝大部分的对话框窗 口),等等。我们可以依赖窗口管理器的这个尺寸信息,虽然它可能被简单的忽视掉。我们首先需要创建一个数据结构来包含该信息,填充必要的数据,然后使用函 数 XSetWMNormalHints()。下面是如何操作:
/* pointer to the size hints structure. */
XSizeHints* win_size_hints = XAllocSizeHints();
if (!win_size_hints) { fprintf(stderr, "XAllocSizeHints - out of memory\n");
exit(1);}
/* initialize the structure appropriately. */
/* first, specify which size hints we want to fill in. */
/* in our case - setting the minimal size as well as the initial size. */
win_size_hints->flags = PSize | PMinSize;
/* next, specify the desired limits.                             */
/* in our case - make the window''s size at least 300x200 pixels.*/
/* and make its initial size 400x250.                            */
win_size_hints->min_width = 300;win_size_hints->min_height = 200;
win_size_hints->base_width = 400;win_size_hints->base_height = 250;
/* pass the size hints to the window manager. */
XSetWMNormalHints(display, win, win_size_hints);
/* finally, we can free the size hints structure. */
XFree(win_size_hints);
请查看你的手册来获取尺寸提示的完整信息。
设置各种窗口管理器提示
使用函数XSetWMHints()还可以设置许多其它的窗口管理器提示。该函数使用一个XWMHints结构来传递参数给窗口管理器。下面是例子:
/* pointer to the WM hints structure. */
XWMHints* win_hints = XAllocWMHints();
if (!win_hints) {    fprintf(stderr, "XAllocWMHints - out of memory\n");
exit(1);}
/* initialize the structure appropriately. */
/* first, specify which hints we want to fill in. */
/* in our case - setting the state hint as well as the icon position hint. */
win_hints->flags = StateHint | IconPositionHint;
/* next, specify the desired hints data.                         */
/* in our case - make the window''s initial state be iconized,   */
/* and set the icon position to the top-left part of the screen. */
win_hints->initial_state = IconicState;win_hints->icon_x = 0;
win_hints->icon_y = 0;
/* pass the hints to the window manager. */
XSetWMHints(display, win, win_hints);
/* finally, we can free the WM hints structure. */
XFree(win_hints);
请查阅手册以获取全部选项的详细信息。
设置一个程序的图标
在用户图标化了我们的程序的时候,为了让窗口管理器能为我们的程序设置一个图标,我们使用上面提到的函数XSetWMHints。但是,首先我们需要创建 一个包含有图标数据的像素图。X服务器使用像素图来操作图片,将在后面介绍它的详细使用。在这里,我们只是向你展示如何为你的程序设置图标。我们假设你已 经得到了一个X bitmap格式的图标文件。教程为了方便提供了一个图标文件"icon.bmp" ,下面是代码:
/* include the definition of the bitmap in our program. */
#include "icon.bmp";
/* pointer to the WM hints structure. */
XWMHints* win_hints;
/* load the given bitmap data and create an X pixmap containing it. */
Pixmap icon_pixmap = XCreateBitmapFromData(display, win, icon_bitmap_bits,                                           icon_bitmap_width, icon_bitmap_height);
if (!icon_pixmap) {
fprintf(stderr, "XCreateBitmapFromData - error creating pixmap\n");
exit(1);}
/* allocate a WM hints structure. */
win_hints = XAllocWMHints();
if (!win_hints) {
fprintf(stderr, "XAllocWMHints - out of memory\n");
exit(1);}
/* initialize the structure appropriately. */
/* first, specify which size hints we want to fill in. */
/* in our case - setting the icon''s pixmap. */
win_hints->flags = IconPixmapHint;
/* next, specify the desired hints data.           */
/* in our case - supply the icon''s desired pixmap.*/
win_hints->icon_pixmap = icon_pixmap;
/* pass the hints to the window manager. */
XSetWMHints(display, win, win_hints);
/* finally, we can free the WM hints structure. */
XFree(win_hints);
你可以使用程序例如"xpaint"来创建使用X bitmap格式的文件。
我们提供文件simple-wm-hints.c来总结这一节,这段程序包括创建一个窗口,设置窗口管理器提示为在上面显示,以及一个简单的事件循环。它允许用户调整参数以察看提示是如何影响程序的行为的。这可以帮助你了解X程序的可移植性。
简单窗口操作
对我们的窗口,我们可以做更多的一些事情。例如,改变它们的尺寸,打开或关闭它们,图标化它们等。Xlib提供了一系列函数来完成上面提到的功能。
映射和解除一个窗口的映射
首先我们对窗口作的一对操作是映射它到屏幕上去和解除它的映射。映射一个窗口的操作将会使一个窗口显示在屏幕上,如我们在简单窗口程序例子里所看到的。解 除映射操作将会把窗口从屏幕里移除出去(虽然作为一个逻辑结点它仍然在X服务器里)。这个可以提供产生窗口被隐藏(映射解除)和再显示(映射)的效果。例 如,我们的程序里有一个对话框,我们不需要每次在需要它显示的时候都重新创建一个窗口,我们只是以映射解除的状态创建一次,在用户需要的时候简单的把它映 射到屏幕上去就行了。这比每一次都创建它和销毁它要快多了,当然,这需要在客户端和服务器端同时使用更多的内存。
你应该还记得映射操作是使用函数XMapWindow()。映射解除操作是使用函数XUnmapWindow(),下面是如何使用它们:
/* make the window actually appear on the screen. */
XMapWindow(display, win);
/* make the window hidden. */
XUnmapWindow(display, win);
除非整个窗口被其它窗口给覆盖了,一个暴露事件将在映射操作后发给应用程序。
在屏幕上移动一个窗口
我们想做的另一个操作是在屏幕里移动窗口。使用函数XMoveWindow()可以完成这个操作。它接受窗口的新坐标,使用的方法和函数XCreateSimpleWindow()是一样的。一下是调用的例子:
/* move the window to coordinates x=400 and y=100. */
XMoveWindow(display, win, 400, 100);
注意当窗口移动的时候,窗口的部分可能后被遮住或被重新暴露,这样我们就可能会收到暴露事件。
改变窗口尺寸
接下来我们要做的是改变一个窗口的尺寸。使用函数XResizeWindow()可以完成这个操作:
/* resize the window to width=200 and height=300 pixels. */
XResizeWindow(display, win, 200, 300);
我们可以合并移动和改变尺寸操作为一个操作,使用函数XMoveResizeWindow():
/* move the window to location x=20 y=30, and change its size */
/* to width=100 and height=150 pixels.                        */
XMoveResizeWindow(display, win, 20, 30, 100, 150);
改变窗口们的栈顺序 - 提升和降低
到目前为止我们已经改变了一个单独窗口的许多属性。接下来我们将看看窗口之间的属性。其中一个就是它们的栈属性。也就是说,窗口是如何在屏幕上排列的。最前面的窗口我们说它是在栈顶,最后面的窗口我们说它是在栈底。下面演示我们如何改变窗口的栈顺序:
/* move the given window to the top of the stack. */
XRaiseWindow(display, win1);
/* move the given window to the bottom of the stack. */
XLowerWindow(display, win1);
图标化和恢复一个窗口
在这里我们将要讲解的最后一个操作就是如何把一个窗口变换成图标状态和恢复它。使用函数XIconifyWindow()来把一个窗口变换成图标状态,使 用函数XMapWindow()来恢复它。为了帮助理解为什么图标化函数没有一个对应的反函数,我们必须理解当一个窗口被图标化时,实际发生的事情是那个 窗口被解除映射了,而它的图表被映射了。结果,如果想使哪个窗口在出现,我们只需要简单的映射它一下就行了。图标实际上是另一个窗口,只不过它与我们的窗 口有非常强的联系关系。下面演示如何图标化一个窗口并恢复它:
/* iconify our window. Make its icon window appear on the same */
/* screen as our window (assuming we created our window on the */
/* default screen).                                            */
XIconifyWindow(display, win, DefaultScreen(display));
/* de-iconify our window. the icon window will be automatically */
/* unmapped by this operation.                                  */
XMapWindow(display, win);
获得一个窗口的信息
与可以为窗口设置许多属性相同,我们也可以要求X服务器提供这些属性的值。例如,我们可以检查窗口现在在屏幕里什么位置,当前尺寸,是否被映射了等等。函数XGetWindowAttributes()可以帮助我们获取那些信息:
/* this variable will contain the attributes of the window. */
XWindowAttributes win_attr;
/* query the window''s attributes. */
Status rc = XGetWindowAttributes(display, win, &win_attr);
结构体XWindowAttributes包含了很多数据域,下面是它的一部分:
int x, y;
窗口的位置,相对于它的父窗口。
int width, height;
窗口的宽和高(单位,像素)。
int border_width
窗口的边框宽度
Window root;
根窗口,也就是我们的窗口在那个窗口里被显示了。
这个函数有些问题,就是它返回的是相对于父窗口的位置。这对一些窗口的操作(例如XMoveWindow)是没有什么意义的。为了解决这个问题,我们需要 使用两步的操作。首先,我们找出窗口的父窗口的ID。然后我们在使用它来确定窗口相对于屏幕的坐标。我们使用两个前面没有介绍的函数来完成这个计 算,XQueryTree()和XTranslateCoordinates()。这两个函数的功能超出了我们的需要,所以我们只关注我们需要的:
/* these variables will eventually hold the translated coordinates. */
int screen_x, screen_y;
/* creen_y contain the location of our original */
/* window, using screen coordinates. */
你可以看到Xlib有时候会让我们处理问题时变得很麻烦。
以上的内容可以参考例子window-operations.c 程序。
使用颜色来绘制彩虹
到目前为止,我们的绘制操作都只使用了黑白两色。现在我们就看看如何使用丰富的颜色来绘制。
颜色映射
首先,是没有完全足够的颜色的。屏幕控制器同时只能支持有限的颜色。因此,一个应用程序不能只是要求使用颜色“轻紫红”就盼望这个颜色能被支持。每个应用分配它自己所需要的颜色,如果全部的16或256色入口都已经在使用了,下一个颜色的分配就会失败。
结果,就介绍使用“一个颜色映射”。一个颜色映射是一个与屏幕控制器同时可以支持的颜色数相同的表。每个表中的节点都为每种颜色包含一个RGB(红,绿和 蓝)。当一个应用想在屏幕上绘制时,它并不指定使用什么颜色,而是指定使用映射表里那一个节点,因此,改变表里某个节点的值将会改变程序绘制的颜色。
为了能让程序员使用他想要的颜色来绘制,提供了颜色映射分配函数。你可以要求分配一个颜色映射节点来对应一个RGB值,然后一个节点的索引值返回给你。如 果表满了,这个操作将会失败。你可以接下来要求一个相近的颜色来满足你的需要。这意味着一个相近的颜色将会被绘制到屏幕上去。
在当今的X 服务器使用的现代显示器上,一般都可以支持上百万的颜色,上面那个限制也许看起来挺傻的,但是记住还有很多古旧的显示卡和显示器在被使用。使用颜色映射, 你可以不必考虑服务器的屏幕硬件细节就可以使用它们。在一个支持上百万的显示器上,任何颜色的分配请求都应该会成功。在一个职能支持有限颜色的显示器上可 能会使用一个相近颜色来代替你的要求,这可能不好看,但你的程序仍然能工作。
分配和释放颜色映射
当你使用Xlib绘制的时候,你可以选择屏幕的标准颜色映射,或者为你的窗口分配一个新的颜色映射。在后一种情况下,每次鼠标移动到你的窗口上时,你窗口 的颜色映射都将替换缺省的屏幕映射,然后你就会看到屏幕花一下其它的窗口颜色也改变了。实际上,这和你在使用“-install”选项运行X应用时效果一 样。
系统定义了宏DefaultColormap来获取屏幕的标准颜色映射:
Colormap screen_colormap = DefaultColormap(display, DefaultScreen(display));
上面的调用将会返回第一个屏幕的缺省颜色映射的句柄(再多余的提醒一下,一个X服务器可以同时支持数个不同的屏幕,每个屏幕都可以有自己的资源)。
另一个选项,分配一个颜色映射,像下面这样工作:
/* first, find the default visual for our screen. */
Visual* default_visual = DefaultVisual(display, DefaultScreen(display));
/* this creates a new color map. the number of color entries in this map */
/* is determined by the number of colors supported on the given screen.  */
Colormap my_colormap = XCreateColormap(display, win, default_visual, AllocNone);
注意,window参数是用来只允许X服务器为指定屏幕分配颜色映射。我们接下来可以使用分配来的颜色映射给同一个屏幕里的任意一个窗口使用。
分配和释放颜色节点
一旦我们获得了颜色映射的访问,我们就可以开始分配颜色。使用函数XAllocNameColor()和XAllocClor()来完成这个工作。首先函 数 XAllocNameColor()获得颜色的名字(例如"红","蓝","棕"等等)然后获得能使用的实际相近颜色。函数XAllocColor()访 问RGB颜色。两个函数都使用XColor结构,它有以下的一些数据域:
unsigned long pixel
颜色映射节点的索引。
unsigned short red
RGB颜色值的红色部分。
unsigned short green
RGB颜色值的绿色部分。
unsigned short blue
RGB颜色值的蓝色部分。
下面是使用的例子:
/* this structure will store the color data actually allocated for us. */
XColor system_color_1, system_color_2;
/* this structure will store the exact RGB values of the named color.  */
/* it might be different from what was actually allocated.             */
XColor exact_color;
/* allocate a "red" color map entry. */
Status rc = XAllocNamedColor(display, screen_colormap, "red", &system_color_1,                             &exact_color);
/* make sure the allocation succeeded. */
if (rc == 0) {
fprintf(stderr, "XAllocNamedColor - allocation of ''red'' color failed.\n");}
else {
printf("Color entry for ''red'' - allocated as (%d,%d,%d) in RGB values.\n",                   system_color_1.red, system_color_1.green, system_color_1.blue);}
/* allocate a color with values (30000, 10000, 0) in RGB. */
system_color_2.red = 30000;
system_color_2.green = 10000;
system_color_2.blue = 0;
Status rc = XAllocColor(display, screen_colormap, &system_color_2);
/* make sure the allocation succeeded. */
if (rc == 0) {
fprintf(stderr, "XAllocColor - allocation of (30000,10000,0) color failed.\n");}
else {
/* do something with the allocated color... */    .    .}
使用一个颜色绘制
我们在分配了希望的颜色之后,我们可以使用它们绘制文本或图形。为了达到目的,我们需要把获得的颜色设置给一些GC(图形上下文)作为前景色和背景色,然 后使用设置好的GC来进行绘制。使用函数XSetForeground()和XSetBackground()来进行,如下:
/* use the previously defined colors as the foreground and background */
/* colors for drawing using the given GC. assume my_gc is a handle to */
/* a previously allocated GC.                                         */
XSetForeground(display, my_gc, screen_color_1.pixel);
XSetForeground(display, my_gc, screen_color_2.pixel);
如你所见,这个是个使用的例子。实际的绘制工作使用我们以前介绍的绘图函数。注意,为了使用各种颜色完成绘制工作,我们可以使用两种方法。我们可以在调用 绘图函数前改变GC的值,也可以使用代表不同颜色的GC。由你自己根据情况使用哪种方法。注意,使用多个GC降消耗X服务器额外的资源,但这样可以使你的 代码显的更紧凑。
作为使用颜色绘制的例子,请察看例子程序color-drawing.c 。这是程序simple-drawing.c 的一个拷贝,我们只是添加了颜色的部分在里面。
X Bitmaps和Pixmaps
一个被称为多媒体的程序所有作的一件事情就是显示图片。在X的世界里,使用bitmaps和pixmaps来实现这个功能。在为我们的程序设置图标的介绍里我们已经使用了它们。现在让我们进一步对它们进行研究,看看在一个窗口里是如何绘制它们的。
在进入之前有一点需要注意,Xlib不能处理许多现在流行的图片格式,例如gif,jpeg或tiff。这些被留给了应用程序(或者是一些图形处理库)来转换成X服务器可以接受的x bitmaps和x pixmaps。
什么是一个X Bitmap?和X Pixmap?
一个Xbitmap是一个有X窗口系统定义的双色图形格式。在保存在一个文件里的时候,bitmap数据看起来就像一段C源程序。它包括定义图片宽高的变量,一个包含比特值得矩阵(矩阵的尺寸=宽*高),和一个可选的热点位置(将会在后面的鼠标光标的部分进行解释)。
一个X pixmap是一个X窗口系统在内存里保存图像的格式。该格式可以储存黑色和白色的图片(例如X bitmaps)也可以保存带颜色的图片。这实际上是X协议唯一能支持的图片格式,任何图片如果想被显示在屏幕上前都要先被转换成这种格式。
实际上,一个X pixmap应该被认为是一个没有被绘制到屏幕上的窗口。许多在窗口上的图形操作也可以工作于X pixmap,只不过使用X pixmap ID来代替窗口ID。事实上,如果你查看手册,你会发现所有的这些函数都是接受一个叫"可画"参数而不是一个窗口参数。因为这两种类型都是可画的,它们都 可以被用在例如函数XDrawArc(),XDrawText()等等。
从一个文件里读取一个Bitmap
在图标的程序里,我们已经看过如何从一个文件里把一个Bitmap装载到内存里。前面我们使用的方法是使用C预编译器"#include"来进行,下面我们看看如何直接从文件里读取。
/* this variable will contain the ID of the newly created pixmap.    */
Pixmap bitmap;
/* these variables will contain the dimensions of the loaded bitmap. */
unsigned int bitmap_width, bitmap_height;
/* these variables will contain the location of the hot-spot of the  */
/* loaded bitmap.                                                    */
int hotspot_x, hotspot_y;
/* this variable will contain the ID of the root window of the screen */
/* for which we want the pixmap to be created.                        */
Window root_win = DefaultRootWindow(display);
/* load the bitmap found in the file "icon.bmp", create a pixmap      */
/* containing its data in the server, and put its ID in the ''bitmap'' */
/* variable.                                                          */
int rc = XReadBitmapFile(display, root_win, "icon.bmp", &bitmap_width,
&bitmap_height, &bitmap, &hotspot_x, &hotspot_y);
/* check for failure or success. */
switch (rc) {
case BitmapOpenFailed:
fprintf(stderr, "XReadBitmapFile - could not open file ''icon.bmp''.\n");
break;
case BitmapFileInvalid:
fprintf(stderr,
"XReadBitmapFile - file ''%s'' doesn''t contain a valid bitmap.\n",                                  "icon.bmp");
break;
case BitmapNoMemory:
fprintf(stderr, "XReadBitmapFile - not enough memory.\n");
break;
case BitmapSuccess:
/* bitmap loaded successfully - do something with it... */
break;
}
注意对于给定的bitmap参数"root_win"什么作用也不起 - 读取的bitmap并不与这个窗口相联系。这个窗口句柄只是被用来指明bitmap所使用的屏幕。这是非常重要的,bitmap必须支持与屏幕相同数量的颜色,这样它才能发挥作用。
在一个窗口里绘制图形
一旦我们获得了从bitmap里生成的pixmap的句柄,我们就可以使用函数XCopyPlane()把它绘制到窗口里。这个函数可以帮助我们指定什么(一个窗口,甚至另一个pixmap)可以画到这个pixmap上去。
/* draw the previously loaded bitmap on the given window, at location   */
/* ''x=100, y=50'' in that window. we want to copy the whole bitmap, so */
/* we specify location ''x=0, y=0'' of the bitmap to start the copy from, */
/* and the full size of the bitmap, to specify how much of it to copy.  */
XCopyPlane(display, bitmap, win, gc, 0, 0, bitmap_width, bitmap_height,
100, 50, 1);
如你所见,我们可以只拷贝制定的矩形区而不是整个pixmap。另外还需要注意的是函数XCopyPlane的最后一个参数(那个结尾的"1")。该参数 指定了那个平面被从源里拷贝出来。对于bitmaps,我们通常只拷贝平面1。到了下面我们讨论颜色深度的时候你就能确切的明白为什么这么做。
创建一个Pixmap
有时我们需要创建一个没有初始化的pixmap,这样我们可以接下来在它上面绘制。这对图像绘制程序是非常有用的。另外,这对读取各种格式的图像数据也是非常有用的。
/* this variable will store the handle of the newly created pixmap. */
Pixmap pixmap;
/* this variable will contain the ID of the root window of the screen */
/* for which we want the pixmap to be created.                        */
Window root_win = DefaultRootWindow(display);
/* this variable will contain the color depth of the pixmap to create. */
/* this ''depth'' specifies the number of bits used to represent a color */
/* index in the color map. the number of colors is 2 to the power of */
/* this depth.                                                       */
int depth = DefaultDepth(display, DefaultScreen(display));
/* create a new pixmap, with a width of 30 pixels, and height of 40 pixels. */
pixmap = XCreatePixmap(display, root_win, 30, 40, depth);
/* just for fun, draw a pixel in the middle of this pixmap. */
XDrawPoint(display, pixmap, gc, 15, 20);
在一个窗口里绘制一个Pixmap
我们在获得了一个pixmap的句柄后,我们就可以使用它在窗口里绘制,使用函数XCopyArea()。
/* draw the previously loaded bitmap on the given window, at location   */
/* ''x=100, y=50'' in that window. we want to copy the whole bitmap, so */
/* we specify location ''x=0, y=0'' of the bitmap to start the copy from, */
/* and the full size of the bitmap, to specify how much of it to copy. */
XCopyPlane(display, bitmap, win, gc, 0, 0, bitmap_width, bitmap_height,                     100, 50, 1);
如你所见,我们可以拷贝指定的矩形区域而不是整个pixmap。
另外一个需要被强调注意的是 - 可以在同一个屏幕上创建不同深度的pixmap。当我们进行拷贝作业时(往一个窗口上拷贝pixmap等等),我们应该保证源和目标是用相同的深度。如果 两个的深度不一样,操作将会失败。除非我们使用前面介绍的函数XCopyPlane()可以完成这个操作。在那样一种情况下,我们拷贝指定的平面到窗口上 去,实际上指定了每一个被拷贝的颜色位。这个操作可以制作许多特殊的效果,但这超出了本文的范围。
释放一个Pixmap
最后,当我们完成了对一个pixmap的操作,我们应该释放它所占的资源。使用函数XFreePixmap()。
/* free the pixmap with the given ID. */
XFreePixmap(display, pixmap);
在释放一个pixmap之后 - 我们绝对不能再访问它。
作为总结这一章,看一下程序draw-pixmap.c 。
改变鼠标光标
我们经常看到改变鼠标光标的程序(经常被称为X光标)。例如,一个正在埋头工作的程序经常会把光标变成沙漏,提示用户需要等待才能处理新的请求。如果没有这么个提示方法,用户会认为程序已经卡住了。下面让我们看看如何为我们的窗口改变鼠标光标。
创建和销毁鼠标光标
系统提供了两个方法来创建光标。其中一个是使用系统预定义的形状,由Xlib支持。另一个是有程序提供一个bitmap来显示。
使用前一种方法时,我们使用一个特殊的字体名字"cursor"和相应的函数XCreateFontCursor()。该函数接受一个形状指示然后返回一个代表生成的光标的句柄。文件
列出了系统支持的光标类型,下面的是其中的三个:
XC_arrow
X服务器显示的普通光标。
XC_pencil
一个笔状的光标。
XC_watch
一个表状沙漏
使用这些符号来创建光标是非常简单的:
#include
/* defines XC_watch, etc. */
/* this variable will hold the handle of the newly created cursor. */
Cursor watch_cursor;
/* create a sand watch cursor. */
watch_cursor = XCreateFontCursor(display, XC_watch);
另一种创建鼠标光标的方法时使用一对pixmaps。一个pixmap定义了光标的形状,另一个是个面具,来指定前一个的什么内容被显示。其它的内容将变 成透明的。创建一个那样的光标使用函数XCreatePixmapCursor()。下面的例子里,我们将使用"icon.bmp"来创建光标。我们将假 设它已经被装载到内存里去了,并已经被转换成pixmap,返回的句柄被保存到"bitmap"变量里。我们希望它是完全透明的。也就是说,只有黑色颜色 的部分会被确实画在屏幕上。为了实现这个效果,我们将会既用它来做光标pixmap且做面具pixmap。希望你能明白为什么这样...
/* this variable will hold the handle of the newly created cursor. *
/Cursor icon_cursor;
/* first, we need to define foreground and background colors for the cursor. */
XColor cursor_fg, cursor_bg;
/* access the default color map of our screen. */
Colormap screen_colormap = DefaultColormap(display, DefaultScreen(display));
/* allocate black and while colors. */
Status rc = XAllocNamedColor(display, screen_colormap, "black", &cursor_fg,                                          &cursor_fg);
if (rc == 0) {
fprintf(stderr,            "XAllocNamedColor - cannot allocate ''black'' ??!!??\n");
exit(1);}
Status rc = XAllocNamedColor(display, screen_colormap, "white",                             &cursor_bg, &cursor_bg);
if (rc == 0) {
fprintf(stderr, "XAllocNamedColor - cannot allocate ''white'' ??!!??\n");
exit(1);}
/* finally, generate the cursor. make the ''hot spot'' be close to the */
/* top-left corner of the cursor - location (x=5, y=4). */
icon_cursor = XCreatePixmapCursor(display, bitmap, bitmap,                  &cursor_fg, &cursor_bg, 5, 4);
上面需要说明的是参数"hot spot"。当我们定义了一个光标,我们需要指明光标里的哪一个像素用来生成各种鼠标事件。通常,我们根据习惯来指定一个看起来像"hot spot"的点。例如一个箭头形状的光标,我们就会选择箭头尖为"hot spot"。
最后,我们不需要再使用光标时,我们可以使用函数XFreeCursor()来释放它的资源:
XFreeCursor(display, icon_cursor);
设置一个窗口的鼠标光标
我们在创建了光标后,就可以告诉X服务器把它贴到我们的任何窗口上去。使用函数XDefineCursor(),X服务器就会在每一次光标移进指定的窗口 时改变光标的形状。稍后我们可以使用函数XUndefinCursor()来撤销刚才的指定。这样,鼠标再移进指定的窗口时就会使用缺省的光标。
/* attach the icon cursor to our window. */
XDefineCursor(display, win, icon_cursor);
/* detach the icon cursor from our window. */
XUndefineCursor(display, win);
作为例子,请查看程序cursor.c, and see
how mouse cursors are set, changed and removed. Run the program, place the
mouse pointer over the created window, and watch.

版本控制-git简易教程

概述

在说明什么是git之前,我们需要对版本控制(Version Control)做一个基本的概述, 一般情况下,我们的源代码都是在时间和空间两个维度上进行管理并维护的,代码本身 以及组织代码的项目文件(如makefile或者vs的项目文件)都是以文件和目录的形式存 储在磁盘空间上的,这种文件管理的形式已经被大家所熟悉了,然而,我们在不同时间 段里做出的各种修改怎么管理呢?版本控制系统 (Version Control System,以下简 称VCS)就是一种记录代码修改演化的系统,他的功能就是方便你今后找回某个特定时 期(或版本)的文件、查看版本之间发生了哪些变化、对变化进行比较或者是修正致命错 误。 版本控制系统主要经历了本地版本控制,集中式版本控制到分布式版本控制的发展: 本地版本控制(Local Version Control System)顾名思义就是本地化的版本控制系 统,没有网络协作等较为先进的版本控制的概念。 集中式版本控制(Centralized Version Control System)为有一台版本控制服务器 运行在那边存放并提供一个项目中所有版本文件的服务,在很长一段时间内占据主 流,其中CVS与SVN为其代表。 分布式版本控制(Distributed Version Control System)克服了集中式版本控制可 能因为单点失败造成的巨大损失的缺点,让每一台客户端在每一次checkout操作后 都完全镜像整个版本控制中的项目。在分布式版本控制系统中,任何一台机器都可 以视为版本控制服务器。即使有一台服务器失去服务能力,其它机器与系统可以继 续协作维持版本控制系统的正常运转。git就是分布式版本控制系统。 git与其它主流的VCS最大的区别就是,在项目版本更新的过程中,git记录的并非是基 于初始文件的变化数据,而是通过一系列快照(Snapshot,就像是个小型的文件系统)来 保存记录每个文件。如果有些文件在版本更新后没有发生任何变化,那么在新的版本中 它会是一个指向最近一次更新的文件版本的链接。 此外,几乎所有git的操作都是在本 地进行的,所以,没有了”延迟”,几乎所有的操作都是瞬间完成的。例如,当你想要 查看项目历史时,不需要特地去服务器上抓取历史记录,直接在本地浏览即可。这意味 着,你可以在本地对比两个不同版本的文件的差别,可以在本地查看过去有哪些人对指 定文件作出了修改与更新,可以……几乎完全本地化的操作也让这样一种场景成为了可 能: 当有人由于没有网络连接条件但是又必须抓紧时间对自己的项目进行修改与开发 ,同时又需要有版本管理系统来记录每次他commit的历史,这时,git提供了他所有需 要的便利。 git使用SHA-1 Hash算法加密生成的40位字符串(而不是文件名)来记录代表git中的每样 东西。格式就像这样: ??6bafcdc09f3d6d416f6572f82082987a486d3098 git中的文件主要会处于三种状态,它们分别是: Committed: 文件或数据已经安全的存放在了git本地数据库中 Modified: 文件或数据已经修改但是尚未commit到数据库 Staged:文件或数据已被标记要放入到下一次commit中 这样的机制致使git的镜像会由三个部分组成(假设有一个git目录叫git-repo): Git directory: 存放项目中所有元数据以及对象的地方(git-repo/.git/) Working directory: 在这里是从git项目数据库中checkout出的一个单独的(默认 情况下是最新的)项目版本,用于对指定项目版本中的文件进行修改和编辑(git-r epo/) Staging area: 一般是存放在Git directory中的一个简单的文件,里面存放着下 一次需要commit的文件的信息(在git-repo/.git/中) 安装与配置 Git可以安装在各种操作系统下,下面以windows为例,linux系统和mac os系统的用户 可以查找github.com上的help内容。 在windows下安装git非常简单,只要依次完成下面操作即可: 第一步:下载 msysgit(http://code.google.com/p/msysgit/),安装时一般只需要 保留默认选项, 只是要注意,不要使用putty作为客户端,GitHub只支持openssh. 安 装完成后需要生成SSH Key。 第二步:配置ssh的key,在用户目录下(windows下通常是C:\Documents and Setting s\,其他系统通常都是/home/)如果已经存在ssh的 配置会存有一个.ssh目录(是隐藏目录),目录下会存有两个文件(id_rsa, id_rsa. pub),将其备份一下,然后生成新的,这里cygwin的命令序列如下 $ ssh-keygen -t rsa -C “your_mail_addr@yourmail.com” Enter file in which to save the key (/c/Users//.ssh/id_r sa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /c/Users//.ssh/id_ rsa. Your public key has been saved in /c/Users//.ssh/id_rsa. pub. The key fingerprint is: e8:ae:60:8f:38:c2:98:1d:6d:84:60:8c:9e:dd:47:81 your_mail_addr@yourmail .com 第三步:将Public Key 添加到你所需要提交的服务器上(这里以GitHub为例,其他服 务器的方式联系相关的管理员),打开你的GitHub->SSH Public Key->点击”Add ano ther public key”, 将你的public key(id_rsa.pub)的内容拷贝到GitHub中。 第四步:测试配置是否成功,同样的,在cygwin里如此输入: $ ssh git@github.com ERROR: Hi You’ve successfully authenticated, but GitHu b does not provide shell access Connection to github.com closed. 看到以上信息表示操作成功,接下来,我们可以学习一些基本使用。 本地基本操作 首先我们要初始化一个Repository,如果完全是新建一个项目,我们可以: mkdir test cd test git init 这个时候,test目录下就会多出一个叫做.git的目录,该目录下会有个config文件,他 的内容如下: [core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true 我们需要加入自己的用户信息(注意,该信息必须与你配置ssh的时候的信息保持一致 ): [user] name = your_username email = your_mail_addr@yourmail.com 当然,用户信息我们完全可以用命令行来配置,在git init命令之前输入如下命令: git config –global user.name “your_username” git config –global user.email “your_mail_addr@yourmail.com” 如果项目目录已经存在,我们可以直接: cd existing_git_repo git init 接下来就是一些基本的操作了…… 首先要用的是:git status,该命令用于查看目前git镜像的状态,在使用git的过程中 ,我们将会反复的使用到这个工具: $ git status # On branch master # # Initial commit # nothing to commit (create/copy files and use “git add” to track) 可以看出,git status给出了相当详细的信息,第一行中首先给出的是git的分支(bra nch)状态信息,接着,git会告诉你现在还没有东西提交到镜像中,建议先使用命令gi t add来对文件进行追踪。所以,接下来我们介绍git add命令, 假设在的工作目录中 使用python语言写一个helloworld的小程序,将代码保存完成后,我们得到一个hello .py文件,然后,我们希望将这个文件被git镜像追踪(track)到,那么只需要: $ git add hello.py 这样,我们就将hello.py加入到了git镜像中去进行版本控制,再次使用git status来 查看目前的镜像状态: $ git status # On branch master # # Initial commit # # Changes to be committed: # (use “git rm –cached …” to unstage) # # new file: hello.py # 注意这里它提到了changes to be committed,意思是该文件已经处于staged状态,接 下去你可以根据自己的需要将其提交(commit),或者如果你觉得这是一个误操作,该文 件不应当被提交,你可以通过git rm –cached命令来取消它的staged状态(你会发现s tatus信息中给出了精确的提示)。 现在,我们通过命令git commit将hello.py提交: $ git commit 这时,会出现一个带有status信息的文本给你编辑(使用什么编辑器取决于你对git的配 置),在以”#”开头的注释行下输入一些文本,用于注释此次提交,方便于其他代码协 作者的维护与理解! 你也可以通过命令参数-m来直接输入注释内容,加快提交速度: $ git commit -m “comment here” 至此,你的文件hello.py已经处于tracked状态!。 现在,我们有一个hello.py在镜像中了,接下来,假设发现了一个小错误,额,比如一 个循环的条件写错了,于是我们需要针对文件完成修改,之后,当我们再次git statu s的时候: # On branch master # Changed but not updated: # (use “git add …” to update what will be committed)# (use “git che # ckout — …” to discard changes in working directory) # # modified: hello.py # no changes added to commit (use “git add” and/or #“git commit -a”) git status显示,hello.py被修改过了,如果你想要提交,需要再次git add该文件, 或者,可以直接使用git commit -a跳过add的步骤,直接提交(尚未track的文件必须先 git add才能进行提交)。 在提示中,还有提到说,如果你想撤销对hello.py的修改,就可以使用git checkout命 令来实现,这里的情况会是: $ git checkout – hello.py 如果你这么做了,你就会发现,你的hello.py又回到了之前没有被修改过的时候的状态 。 在Unix/Unix-like系统中,几乎都会有一个小巧的对比文件不同的工具叫做diff,在g it中也有这么一个工具,来详细比较你修改后准备提交的文件与修改前的状态的不同之 处,恩,也许你猜到了,这个命令就是git diff。 现在我们尝试着再次修改一下hello.py,然后运行git diff: $ git diff diff –git a/ hello.py b/ hello.py index befc634..a86316b 100644 — a/hello.py +++ b/hello.py @@ -1,3 +1,4 @@ +/* new comment line */ if __name__ == “__main__”: print “hello, world!” 通过git的diff工具,很容易了解到我们更改了哪些地方。这个时候,如果我们想撤销 之前的更改,可以使用git reset命令: $ git reset HEAD hello.py $ git status # On branch master # Untracked files: # (use “git add …” to include in what will be committed) # # hello.py nothing added to commit but untracked files present (use “git add” to track) 再次git status,hello.py又回到了untracked状态! 接下来,如果需要修改README.txt(通常是项目的自述文件)的文件名(千万要记得我 们的文件在git的镜像中进行版本控制管理,不要鲁莽的直接使用unix命令mv或者rm来 对git镜像中的文件进行普通的文件操作),我们该使用git mv命令: $ git mv README.txt tutorial.txt $ git status # On branch master # Changes to be committed: # (use “git reset HEAD …” to unstage) # # renamed: README.txt -> tutorial.txt # $ git commit -a -m “renamed a file” [master 55ce30d] renamed a file 1 files changed, 0 insertions(+), 0 deletions(-) rename README.txt => tutorial.txt (100%) 可以看到,在提交变更后,README.txt在文件系统以及git镜像中都被成功地重命名为 了tutorial.txt。同样的,你可以unstage来撤销对该文件的重命名,the choice is yours! 如果我们不再需要tutorial.txt这个文件,我们可以将其从git镜像中删除,git中删除 文件的命令是git rm: $ git rm tutorial.txt rm ‘tutorial.txt’ $ git status # On branch master # Changes to be committed: # (use “git reset HEAD …” to unstage) # # deleted: tutorial.txt # $ git commit -a -m ” deleted a file” [master 7d81981] deleted a file 1 files changed, 0 insertions(+), 1 deletions(-) delete mode 100644 tutorial.txt 正如之前所提到的,这些操作都是可以恢复的,因为git是版本控制系统,所以自然而 然的就会有一套版本历史管理机制。 我们可以用工具git log提供了查看git镜像的commit历史: $ git log commit 7d819818530ce89322019ba5000723c973eb0420 Author: ghosTM55 Date: Sun Mar 14 15:26:22 2010 +0800 deleted a file commit 55ce30d88fb5c81d20bdf86e2034053613fed632 Author: ghosTM55 Date: Sun Mar 14 15:11:39 2010 +0800 renamed a file commit 2ed9f1f9bd1a7561cd7e57dcdbd7f2cda54668fb Author: ghosTM55 Date: Sun Mar 14 14:58:11 2010 +0800 a little change 基本上可以清楚地看到,git详细记录了每次commit的信息(checksum值、提交者信息、 提交时间)。 下面简单的说一下分支管理的操作,git的分支管理是异常的简单和方便,可以用git branch命令进行非常直观的操作: 首先可以在工作目录下查看当前的项目存在多少分支: $ git branch * master test 可以清楚地看到,目前该项目存在两个分支master和test,带*的是当前分支(我们可 以用命令git checkout test切换到test反之)。接下来我们为项目添加一个分支: $ git branch new $ git branch * master new test 我们看到新分支new已经添加进git的镜像里了,如果要删除该分支,我们可以如此: $ git branch -d new Deleted branch new (was 63c0da1). $ git branch * master test 这时候我们就看到new已不在镜像中,当我们需要将指定分支中的代码合并到默认分支 的时候,可以使用命令: 远程基本操作 (这部分引用一个网络blog:http://archive.cnblogs.com/a/2050324/) 要参与任何一个 Git 项目的协作,必须要了解该如何管理远程仓库。远程仓库是指托 管在网络上的项目仓库,可能会有好多个,其中有些你只能读,另外有些可以写。同他 人协作开发某 个项目时,需要管理这些远程仓库,以便推送或拉取数据,分享各自的 工作进展。管理远程仓库的工作,包括添加远程库,移除废弃的远程库,管理各式远程 库分 支,定义是否跟踪这些分支,等等。本节我们将详细讨论远程库的管理和使用。 查看当前的远程库 要查看当前配置有哪些远程仓库,可以用 git remote 命令,它会列出每个远程库的简 短名字。在克隆完某个项目后,至少可以看到一个名为 origin 的远程库,Git 默认使 用这个名字来标识你所克隆的原始仓库: $ git clone git://github.com/schacon/ticgit.git Initialized empty Git repository in /private/tmp/ticgit/.git/ remote: Counting objects: 595, done. remote: Compressing objects: 100% (269/269), done. remote: Total 595 (delta 255), reused 589 (delta 253) Receiving objects: 100% (595/595), 73.31 KiB | 1 KiB/s, done. Resolving deltas: 100% (255/255), done. $ cd ticgit $ git remote origin 也可以加上 -v 选项(译注:此为 –verbose 的简写,取首字母),显示对应的克隆 地址: $ git remote -v origin git://github.com/schacon/ticgit.git 如果有多个远程仓库,此命令将全部列出。比如在我的 Grit 项目中,可以看到: $ cd grit $ git remote -v bakkdoor git://github.com/bakkdoor/grit.git cho45 git://github.com/cho45/grit.git defunkt git://github.com/defunkt/grit.git koke git://github.com/koke/grit.git origin git@github.com:mojombo/grit.git 这样一来,我就可以非常轻松地从这些用户的仓库中,拉取他们的提交到本地。请注意 ,上面列出的地址只有 origin 用的是 SSH URL 链接,所以也只有这个仓库我能推送 数据上去(我们会在第四章解释原因)。 添加远程仓库 要添加一个新的远程仓库,可以指定一个简单的名字,以便将来引用,运行 git remo te add [shortname] [url]: $ git remote origin $ git remote add pb git://github.com/paulboone/ticgit.git $ git remote -v origin git://github.com/schacon/ticgit.git pb git://github.com/paulboone/ticgit.git 现在可以用字串 pb 指代对应的仓库地址了。比如说,要抓取所有 Paul 有的,但本地 仓库没有的信息,可以运行 git fetch pb: $ git fetch pb remote: Counting objects: 58, done. remote: Compressing objects: 100% (41/41), done. remote: Total 44 (delta 24), reused 1 (delta 0) Unpacking objects: 100% (44/44), done. From git://github.com/paulboone/ticgit * [new branch] master -> pb/master * [new branch] ticgit -> pb/ticgit 现在,Paul 的主干分支(master)已经完全可以在本地访问了,对应的名字是 pb/ma ster,你可以将它合并到自己的某个分支,或者切换到这个分支,看看有些什么有趣的 更新。 从远程仓库抓取数据 正如之前所看到的,可以用下面的命令从远程仓库抓取数据到本地: $ git fetch [remote-name] 此命令会到远程仓库中拉取所有你本地仓库中还没有的数据。运行完成后,你就可以在 本地访问该远程仓库中的所有分支,将其中某个分支合并到本地,或者只是取出某个分 支,一探究竟。(我们会在第三章详细讨论关于分支的概念和操作。) 如果是克隆了一个仓库,此命令会自动将远程仓库归于 origin 名下。所以,git fet ch origin 会抓取从你上次克隆以来别人上传到此远程仓库中的所有更新(或是上次 fetch 以来别人提交的更新)。有一点很重要,需要记住,fetch 命令只是将远端的数 据拉到本地仓库,并不自动合并到当前工作分支,只有当你确实准备好了,才能手工合 并。(说 明:事先需要创建好远程的仓库,然后执行:git remote add [仓库名] [仓 库url],git fetch [远程仓库名],即可抓取到远程仓库数据到本地,再用git merge remotes/[仓库名]/master就可以将远程仓库merge到本地当前branch。这种分支方式 比较适合独立-整合开发,即各自开发测试好后 再整合在一起。比如,Android的Fram ework和AP开发。 可以使用–bare 选项运行git init 来设定一个空仓库,这会初始化一个不包含工作目 录的仓库。 $ cd /opt/git $ mkdir project.git $ cd project.git $ git –bare init 这时,Join,Josie 或者Jessica 就可以把它加为远程仓库,推送一个分支,从而把第 一个版本的工程上传到仓库里了。) 如果设置了某个分支用于跟踪某个远端仓库的分支(参见下节及第三章的内容),可以 使用 git pull 命令自动抓取数据下来,然后将远端分支自动合并到本地仓库中当前分 支。在日常工作中我们经常这么用,既快且好。实际上,默认情况下 git clone 命令 本质上就是自动创建了本地的 master 分支用于跟踪远程仓库中的 master 分支(假设 远程仓库确实有 master 分支)。所以一般我们运行 git pull,目的都是要从原始克 隆的远端仓库中抓取数据后,合并到工作目录中当前分支。 推送数据到远程仓库 项目进行到一个阶段,要同别人分享目前的成果,可以将本地仓库中的数据推送到远程 仓库。实现这个任务的命令很简单: git push [remote-name] [branch-name]。如果 要把本地的 master 分支推送到 origin 服务器上(再次说明下,克隆操作会自动使用 默认的 master 和 origin 名字),可以运行下面的命令: $ git push origin master 只有在所克隆的服务器上有写权限,或者同一时刻没有其他人在推数据,这条命令才会 如期完成任务。如果在你推数据前,已经有其他人推送了若干更新,那 你的推送操作 就会被驳回。你必须先把他们的更新抓取到本地,并到自己的项目中,然后才可以再次 推送。有关推送数据到远程仓库的详细内容见第三章。 查看远程仓库信息 我们可以通过命令 git remote show [remote-name] 查看某个远程仓库的详细信息, 比如要看所克隆的origin 仓库,可以运行: $ git remote show origin * remote origin URL: git://github.com/schacon/ticgit.git Remote branch merged with ‘git pull’ while on branch master master Tracked remote branches master ticgit 除了对应的克隆地址外,它还给出了许多额外的信息。它友善地告诉你如果是在 mast er 分支,就可以用git pull 命令抓取数据合并到本地。另外还列出了所有处于跟踪状 态中的远端分支。 实际使用过程中,git remote show 给出的信息可能会像这样: $ git remote show origin * remote origin URL: git@github.com:defunkt/github.git Remote branch merged with ‘git pull’ while on branch issues issues Remote branch merged with ‘git pull’ while on branch master master New remote branches (next fetch will store in remotes/origin) caching Stale tracking branches (use ‘git remote prune’) libwalker walker2 Tracked remote branches acl apiv2 dashboard2 issues master postgres Local branch pushed with ‘git push’ master:master 它告诉我们,运行 git push 时缺省推送的分支是什么(译注:最后两行)。它还显示 了有哪些远端分支还没有同步 到本地(译注:第六行的 caching 分支),哪些已同步 到本地的远端分支在远端服务器上已被删除(译注:Stale tracking branches 下面的 两个分支),以及运行 git pull 时将自动合并哪些分支(译注:前四行中列出的 is sues 和 master 分支)。(此命令也可以查看到本地分支和远程仓库分支的对应关系。 ) 远程仓库的删除和重命名 在新版 Git 中可以用 git remote rename 命令修改某个远程仓库的简短名称,比如想 把 pb 改成 paul,可以这么运行: $ git remote rename pb paul $ git remote origin paul 注意,对远程仓库的重命名,也会使对应的分支名称发生变化,原来的 pb/master 分 支现在成了paul/master。 碰到远端仓库服务器迁移,或者原来的克隆镜像不再使用,又或者某个参与者不再贡献 代码,那么需要移除对应的远端仓库,可以运行 git remote rm 命令: $ git remote rm paul $ git remote origin 推荐书籍 版本控制之道——使用Git 副标题: —使用Git 作者: Travis Swicegood 译者: 董越 / 付昭伟 / 等译 出版社: 电子工业出版社 出版年: 2010年5月 Git权威指南 作者: 蒋鑫 出版社: 机械工业出版社华章公司 出版年: 2011-6

用51单片机简单做了个无线测温装置

主要设备和实现的功能:

两个51单片机开发板,和两个NRF905无线模块,一个开发板作为从机,利用机上的ds18测温度,然后通过NRF905发送给主机,主机通过NRF905接收从机温度信息,在12864液晶屏上显示的同时,通过串口将数据发送个pc。

效果如图:

 

 

linux下用C编写的基于smtp的邮件发送程序

我的webf服务器是动态ip,因此需要检测服务器ip的变化然后更新域名的ip,打算检测到ip变化后自动发送邮件告知自己,于是上网搜了下编程发送邮件的方法,大部分是基于vc的,我找了个例子修改成了linux下的程序。这个程序是基于smtp协议的,smtp是基于tcp的,有兴趣的可以先自己看看smtp协议,这样看程序会更容易理解。

这个程序目前126的邮箱经测试成功发送,新浪的虽然显示发送成功但是总也收不到,有空再慢慢实验吧,其他邮箱还未测试。

程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <unistd.h>
#include <netinet/in.h>
#include <limits.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <ctype.h>

// Function prototype
void StreamClient(char *szServer, short nPort, char *szMessage);
// Helper macro for displaying errors

////////////////////////////////////////////////////////////

int main(int argc, char **argv)
{
char hostname[255];
int nRet;
short nPort;
nPort =25;
StreamClient("smtp.126.com", nPort, "AUTH LOGIN\r\n");
return 0;
}

////////////////////////////////////////////////////////////

void StreamClient(char *szServer, short nPort, char *szMessage)
{

int sockfd;
char buffer[1024];
struct sockaddr_in server_addr;
struct hostent *host;

if((host=gethostbyname(szServer))==NULL)/*取得主机IP地址*/
{
fprintf(stderr,"Gethostname error, %s\n", strerror(errno));
exit(1);
}

if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1)/*建立SOCKET连接*/
{
fprintf(stderr,"Socket Error:%s\a\n",strerror(errno));
exit(1);
}

/* 客户程序填充服务端的资料 */
bzero(&server_addr,sizeof(server_addr));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(nPort);
server_addr.sin_addr=*((struct in_addr *)host->h_addr);

/* 客户程序发起连接请求 */
if(connect(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1)/*连接网站*/
{
fprintf(stderr,"Connect Error:%s\a\n",strerror(errno));
exit(1);
}

printf("\nStream Client connecting to server: %s on port: %d",szServer, nPort);

char szBuf[1024];
memset(szBuf,0,1024);
int nRet;
// strcpy(szBuf, "From the Client");
char buf[350]="0";
char server[250];
gethostname(server,250);
strcpy(buf, "HELO ");

strcat(buf, szServer);
strcat(buf, " \r\n");
printf("%s:::%d",buf,strlen(buf));

//
// Wait for a reply
//
nRet = recv(sockfd,szBuf,sizeof(szBuf)+1,0);  printf("\nData received OVER DATA: %s", szBuf);

nRet = send(sockfd, buf, strlen(buf), 0);printf("\nsend %s",buf);
nRet = recv(sockfd, szBuf, sizeof(szBuf), 0); printf("\nData received2: %s", szBuf);

//发送准备登陆信息
nRet = send(sockfd, "AUTH LOGIN\r\n", strlen("AUTH LOGIN\r\n"), 0);
nRet = recv(sockfd, szBuf, sizeof(szBuf), 0); printf("\nData received LOGIN: %s", szBuf);

//发送用户名和密码,这里的用户名和密码必须用base64进行转码,发送转码以后的字符串,对于126邮箱来说用户名是@前面的字符串
nRet = send(sockfd,"转码后的用户名\r\n", strlen("转码后的用户名\r\n"),0);printf("\nData Send USERNAME");
nRet = recv(sockfd, szBuf, sizeof(szBuf),0);printf("\nData received USERNAME: %s", szBuf);printf("\nData Send PASS");

//发送用户密码
nRet = send(sockfd,"转码后的密码\r\n", strlen("转码后的密码\r\n"), 0);
//printf("\nData Send PASS");
nRet = recv(sockfd, szBuf, sizeof(szBuf),0); printf("\nData received USERPASSWORD: %s", szBuf);

//发送[发送邮件]的信箱(改成你的邮箱!),该邮箱要与用户名一致,否则发送不成功
send(sockfd,"MAIL FROM: <server_ip_alert@126.com>\r\n",strlen("MAIL FROM: <server_ip_alert@126.com>\r\n"),0);printf("\nsend MAIL FROM\n");
nRet = recv(sockfd, szBuf, sizeof(szBuf), 0);printf("\nData received MAILFROM: %s", szBuf);

//发送[接收邮件]的邮箱
nRet= send(sockfd,"RCPT TO: <tootoogo@hotmail.com>\r\n",strlen("RCPT TO: <tootoogo@hotmail.com>\r\n"),0);printf("\nsend RCPT TO\r\n");
nRet = recv(sockfd, szBuf, sizeof(szBuf), 0);   printf("\nData received TO MAIL: %s", szBuf);
/*
nRet= send(sockfd,"RCPT TO: <server_ip_alert@hotmail.com>\r\n",strlen("RCPT TO: <server_ip_alert@hotmail.com>\r\n"),0);printf("\nsend RCPT TO\r\n");
nRet = recv(sockfd, szBuf, sizeof(szBuf), 0);   printf("\nData received TO MAIL: %s", szBuf);
*/
char MailData[] ="From: \"server_ip_alert@sina.com\"<server_ip_alert@sina.com>\r\nTo: tootoogo@hotmail.com\r\nSubject: IP Address\r\n\r\n"; //主题可以改成别的

//各诉邮件服务器,准备发送邮件内容

send(sockfd,"DATA\r\n", strlen("DATA\r\n"),0);printf("\nsend DATA\n");
//  nRet = recv(sockfd, szBuf, sizeof(szBuf)+1, 0); printf("\nData receivedSEND DATA: %s", szBuf);
//发送邮件标题
send(sockfd,MailData, strlen(MailData),0);
//发送邮件内容
send(sockfd,"test!!!!\r\n", strlen("abcdefg\r\n"),0);//我随便写了几个字符进行测试
//发送邮件结束
send(sockfd,"\r\n.\r\n", strlen("\r\n.\r\n"),0);
//接收邮件服务器返回信息
nRet = recv(sockfd,szBuf,sizeof(szBuf),0);  printf("\nData received OVER DATA: %s", szBuf);
send(sockfd,"QUIT\r\n", strlen("QUIT\r\n"),0);
nRet = recv(sockfd,szBuf,sizeof(szBuf),0);  printf("\nData received QUIT: %s", szBuf);

//
// Display the received data
//
//printf("\nData received3: %s", szBuf);

close(sockfd);
return;
}

转载:opencv2.1的arm移植

OpenCV是Intel支持的开源计算机视觉库。它由一系列C函数和少量C++类构成,实现了图像处理和计算机视觉方面的很多通用算法。它不依赖于其它的外部库—尽管也可以使用某些外部库。

 

OpenCV使用BSD License,对非商业应用和商业应用都可以免费使用。

 

OpenCV的主要应用环境是Windows和Linux,对于嵌入式系统如arm-linux,很少有资料提到,因此将我在arm-linux编译过程记录下来和大家分享。
预备工作:

 

a. 安装交叉编译链
我使用的是arm-linux-gcc 4.3.2,解压到目录/usr/local/arm/4.3.2,然后在环境变量PATH中增加/usr/local/arm/4.3.2/bin

b. 安装CMake
OpenCV 2.0之后的版本,必须使用CMake创建Makefile。我使用的CMake版本是2.8.2,解压到目录/usr/local/cmake- 2.8.2-Linux-i386,然后在环境变量PATH中增加/usr/local/cmake-2.8.2-Linux-i386/bin
编译OpenCV:


1、解压OpenCV 2.1到/usr/local/OpenCV-2.1.0目录下

 

2、创建/usr/local/opencv-arm/目录,作为CMake编译arm版本的工作目录

 

3、在X环境下,运行cmake-gui
选择源代码目录:/usr/local/OpenCV-2.1.0
选择Build目录:/usr/local/opencv-arm/
点击Configure,保持generator为Unix Makefiles,选择Specify options for cross-compiling,点击Next
Operating System填写arm-inux
C Compilers填写/usr/local/arm/4.3.2/bin/arm-linux-gcc
C++ Compilers填写/usr/local/arm/4.3.2/bin/arm-linux-g++
程序库的Target Root填写/usr/local/arm/4.3.2/,然后点击Finish

修改默认配置,默认安装目录为/usr/local,对于交叉编译的库来说并不合适,所以我把CMAKE_INSTALL_PREFIX变量改为/usr/local/arm/lib/opencv/
另外,我没有安装tiff图像的支持,因此去掉WITH_TIFF

点击Generate生成Makefile

 

4、在终端界面中,进入目录/usr/local/opencv-arm,运行make编译opencv

 

编译时发现如下错误:
Linking CXX executable ../../bin/opencv_createsamples
../../lib/libcxcore.so: undefined reference to `clock_gettime'
../../lib/libcxcore.so: undefined reference to `pthread_key_create'
../../lib/libcxcore.so: undefined reference to `pthread_getspecific'
../../lib/libcxcore.so: undefined reference to `pthread_setspecific'

原因是cmake不认识我定义的arm-linux系统标记,没有加上库pthread和rt的链接选项

 

5、修改CMakeCache.txt,CMAKE_EXE_LINKER_FLAGS原来为空,加上-lpthread -lrt,重新编译,错误消除

 

6、运行make install,将opencv生成的库和头文件安装到目录/usr/local/arm/lib/opencv/

 

测试OpenCV库

 

1、首先确认一下库是否已编译正确及其安装位置

 

查看头文件:

 

    1. [root@localhost opencv-arm]# ls /usr/local/arm/lib/opencv/include/opencv/
    2. cvaux.h    cvcompat.h  cv.hpp        cvtypes.h  cvvidsurv.hpp  cxcore.h    cxerror.h  cxmat.hpp  cxoperations.hpp  highgui.h    ml.h
    3. cvaux.hpp  cv.h        cvinternal.h  cvver.h    cvwimage.h     cxcore.hpp  cxflann.h  cxmisc.h   cxtypes.h         highgui.hpp

 

 

查看库文件:

  1. [root@localhost opencv-arm]# ls /usr/local/arm/lib/opencv/lib
  2. libcv.a  libcvaux.a  libcvaux.so  libcv.so  libcxcore.a  libcxcore.so  libhighgui.a  libhighgui.so  libml.a  libml.so2、 写个简单的测试程序,打开摄像头并创建一个窗口显示 
      1. // test.cpp
      2. #include <cv.h>
      3. #include <cxcore.h>
      4. #include <highgui.h>
      5. int main()
      6. {
      7.         CvCapture* capture = NULL;
      8.         IplImage* frame = NULL;
      9.         if( !(capture = cvCaptureFromCAM(-1)))
      10.         {
      11.                 fprintf(stderr, "Can not open camera./n");
      12.                 return -1;
      13.         }
      14.         cvNamedWindow("video", 1);
      15.         while(frame = cvQueryFrame( capture ) )
      16.         {
      17.                 cvShowImage("video", frame);
      18.         }
      19.         cvDestroyWindow("video");
      20.         cvReleaseCapture(&capture);
      21.         return 0;
      22. }

    #include <cv.h>
    #include <cxcore.h>
    #include <highgui.h>

    int main()
    {
    CvCapture* capture = NULL;
    IplImage* frame = NULL;

    if( !(capture = cvCaptureFromCAM(-1)))
    {
    fprintf(stderr, "Can not open camera./n");
    return -1;
    }

    cvNamedWindow("video", 1);

    while(frame = cvQueryFrame( capture ) )
    {
    cvShowImage("video", frame);
    }

    cvDestroyWindow("video");
    cvReleaseCapture(&capture);
    return 0;
    }

    3、编译链接测试程序

     

    arm-linux-g++ -I/usr/local/arm/lib/opencv/include/opencv/ -L/usr/local/arm/lib/opencv/lib/ -lcv -lcxcore -lhighgui -lpthread -lrt -o test test.cpp

     

    4、复制程序到嵌入式系统中运行

     

    首先复制主机/usr /local/arm/lib/opencv/lib/下面的几个.so文件到嵌入式Linux系统的/lib/目录下,再复 制我们编译的test到嵌入式系统/opt/myworks/目录下(并确保文件test属性为可执行),如果test可正常运行没有报告缺少库文件,说 明我们编译的arm-linux版OpenCV库已经可以正常使用。

弄到了一套DevKit8000 arm开发套件,打算玩玩arm呵呵

性能还是很强悍的,基于OMAP3530处理器,板载128MByte DDR SDRAM及128MByte NAND Flash 支持DVI-D/S-Video/TFT-LCD三种显示输出,可输出高清信号 外扩USB OTG,串口,网口,摄像头,音频,SD/MMC,键盘等接口 支持WinCE 6.0及Linux2.6.28系统

 

 

OpenCV cvVideoWriter使用方法和例子

CvVideoWriter* cvCreateVideoWriter ( const char* filename, int fourcc, double fps, CvSize frame_size, int is_color=1 );
filename 输出视频文件名。
fourcc为 四个字符用来表示压缩帧的codec 例如:
CV_FOURCC('P','I','M','1') = MPEG-1 codec
CV_FOURCC('M','J','P','G') = motion-jpeg codec
CV_FOURCC('M', 'P', '4', '2') = MPEG-4.2 codec
CV_FOURCC('D', 'I', 'V', '3') = MPEG-4.3 codec
CV_FOURCC('D', 'I', 'V', 'X') = MPEG-4 codec
CV_FOURCC('U', '2', '6', '3') = H263 codec
CV_FOURCC('I', '2', '6', '3') = H263I codec
CV_FOURCC('F', 'L', 'V', '1') = FLV1 codec
若编码器代号为 -1,则运行时会弹出一个编码器选择框.
fps 被创建视频流的帧率。
frame_size 视频流的大小。
is_color 如果非零,编 码器将希望得到彩色帧并进行编码;否则,是灰度帧(只有在Windows下支持这个标志)。
函数cvCreateVideoWriter 创建视频写入器结构。

 实例程序

example.c:

编译使用(在archlinux 3.2.8-1下顺利编译)

g++ `pkg-config opencv --cflags --libs` -o a.out example.c

#include "cv.h"
#include "highgui.h"
#include "cxcore.h"
#include <stdio.h>
IplImage* myframe,*frame;
CvVideoWriter * pVW=NULL;

CvCapture* capture=0;

int main(int argc, char* argv[])
{

capture= cvCaptureFromFile("1.avi");//open 1.avi
pVW=cvCreateVideoWriter("tmp.avi",CV_FOURCC('U', '2', '6', '3') ,25,size,1);
while(1)

{

frame=NULL

frame = cvQueryFrame(capture);

if(frame==NULL)break;

cvWriteFrame(pVW,frame);

 

}

return 0;

}

第 1 页,共 7 页12345...最旧 »