【数字图像处理】<纯C++>读取、裁剪、缩放、旋转和存储8位bmp灰度图像
2013-12-09 11:31
791 查看
C++实现以下功能:1、实现对硬盘中的8位BMP图像的读取和存储;2、实现对指定矩形框区域图像的剪切;3、实现对任意形状图像的任意比例缩放;4、实现对任意形状图像的任意角度选装。 程序编辑环境:在visual c++6.0中建立console application工程,win32平台,无MFC,空Project 程序运行说明: 1、添加文件Ex1.cpp、Ex1.h 2、编译、运行 3、按程序提示完成操作(注意文件名称的大小写) 4、进入文件夹,查看Cutresult.bmp(裁剪结果)、Scalresult.bmp(缩放结果)和Rotresult.bmp(旋转结果)文件。 [code]感谢以下三篇博文:http://blog.csdn.net/xiajun07061225/article/details/5813726http://blog.csdn.net/wangyadong/article/details/3549284http://blog.csdn.net/xiajun07061225/article/details/6633938 以下代码是参照以上内容做适当修改完成的。 头文件Ex1.h内容如下: typedef unsigned char BYTE;typedef unsigned short WORD;typedef unsigned int DWORD;typedef long LONG; //位图文件头定义;//其中不包含文件类型信息(由于结构体的内存结构决定,//要是加了的话将不能正确读取文件信息)typedef struct tagBITMAPFILEHEADER{// WORD bfType;//文件类型,必须是0x424D,即字符“BM” DWORD bfSize;//文件大小 WORD bfReserved1;//保留字 WORD bfReserved2;//保留字 DWORD bfOffBits;//从文件头到实际位图数据的偏移字节数}BITMAPFILEHEADER; typedef struct tagBITMAPINFOHEADER{ DWORD biSize;//信息头大小 LONG biWidth;//图像宽度 LONG biHeight;//图像高度 WORD biPlanes;//位平面数,必须为1 WORD biBitCount;//每像素位数 DWORD biCompression; //压缩类型 DWORD biSizeImage; //压缩图像大小字节数 LONG biXPelsPerMeter; //水平分辨率 LONG biYPelsPerMeter; //垂直分辨率 DWORD biClrUsed; //位图实际用到的色彩数 DWORD biClrImportant; //本位图中重要的色彩数}BITMAPINFOHEADER; //位图信息头定义 typedef struct tagRGBQUAD{ BYTE rgbBlue; //该颜色的蓝色分量 BYTE rgbGreen; //该颜色的绿色分量 BYTE rgbRed; //该颜色的红色分量 BYTE rgbReserved; //保留值}RGBQUAD;//调色板定义 //像素信息typedef struct tagIMAGEDATA{ BYTE blue;///8位灰度图用其中1个 //BYTE green; //BYTE red;}IMAGEDATA; 源文件Ex1.cpp内容如下: // Ex1.cpp : Defines the entry point for the console application.// #include <stdio.h>#include "Ex1.h"#include "stdlib.h"#include "math.h"#include <iostream> #define PI 3.14159//圆周率宏定义#define LENGTH_NAME_BMP 30//bmp图片文件名的最大长度 using namespace std; //变量定义BITMAPFILEHEADER strHead; //位图文件头RGBQUAD strPla[256];//256色调色板BITMAPINFOHEADER strInfo; //显示位图文件头信息void showBmpHead(BITMAPFILEHEADER pBmpHead){ cout<<"读取的位图文件信息:"<<endl; cout<<"文件大小:"<<pBmpHead.bfSize<<endl;// cout<<"保留字_1:"<<pBmpHead.bfReserved1<<endl;// cout<<"保留字_2:"<<pBmpHead.bfReserved2<<endl; cout<<"实际位图数据的偏移字节数:"<<pBmpHead.bfOffBits<<endl;} void showBmpInforHead(tagBITMAPINFOHEADER pBmpInforHead){// cout<<"读取的位图信息头:"<<endl; cout<<"结构体的长度:"<<pBmpInforHead.biSize<<endl; cout<<"位图宽:"<<pBmpInforHead.biWidth<<endl; cout<<"位图高:"<<pBmpInforHead.biHeight<<endl; cout<<"biPlanes平面数:"<<pBmpInforHead.biPlanes<<endl; cout<<"biBitCount采用颜色位数:"<<pBmpInforHead.biBitCount<<endl; cout<<"压缩方式:"<<pBmpInforHead.biCompression<<endl; cout<<"biSizeImage实际位图数据占用的字节数:"<<pBmpInforHead.biSizeImage<<endl; cout<<"X方向分辨率:"<<pBmpInforHead.biXPelsPerMeter<<endl; cout<<"Y方向分辨率:"<<pBmpInforHead.biYPelsPerMeter<<endl; cout<<"使用的颜色数:"<<pBmpInforHead.biClrUsed<<endl; cout<<"重要颜色数:"<<pBmpInforHead.biClrImportant<<endl<<endl;} int max(int i,int j){ if(i>=j) return i; else return j; }int main(){ char strFile[LENGTH_NAME_BMP]; //bmp文件名 WORD bfType_w=0x4d42; IMAGEDATA *imagedata = NULL; //动态分配存储原图片的像素信息的二维数组 IMAGEDATA *imagedataCut = NULL;//动态分配存储裁剪后的图片的像素信息的二维数组 IMAGEDATA *imagedataScal = NULL;//动态分配存储缩放后的图片的像素信息的二维数组 IMAGEDATA *imagedataRot = NULL;//动态分配存储旋转后的图片的像素信息的二维数组 int width,height;//图片的宽度和高度 float ExpScalValue=0; ////期望的缩放倍数(允许小数) int FloatToIntwidth,FloatToIntheight;/////小数变成整数(float To Int) int RotateAngle=90;//要缩放的角度,默认90 cout<<"请输入所要读取的文件名(w.bmp或者06.bmp或者07.bmp):"<<endl; cin>>strFile; FILE *fpi,*fpw; fpi=fopen(strFile,"rb"); if(fpi != NULL) { //先读取文件类型 WORD bfType; fread(&bfType,1,sizeof(WORD),fpi); if(0x4d42!=bfType)///if !=BMP { cout<<"the file is not a bmp file!"<<endl; return NULL; } printf("\n读取到的文件是%s。\n\n",strFile); //读取bmp文件的文件头和信息头 fread(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpi); showBmpHead(strHead);//显示文件头 fread(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpi); showBmpInforHead(strInfo);//显示文件信息头 //读取调色板 for(unsigned int nCounti=0;nCounti<strInfo.biClrUsed;nCounti++) { fread((char *)&(strPla[nCounti].rgbBlue),1,sizeof(BYTE),fpi); fread((char *)&(strPla[nCounti].rgbGreen),1,sizeof(BYTE),fpi); fread((char *)&(strPla[nCounti].rgbRed),1,sizeof(BYTE),fpi); fread((char *)&(strPla[nCounti].rgbReserved),1,sizeof(BYTE),fpi); } } else { cout<<"file open error!"<<endl; return NULL; } width = strInfo.biWidth; height = strInfo.biHeight; imagedata = (IMAGEDATA*)malloc(width * height * sizeof(IMAGEDATA));///为原始图像分配存储空间 //初始化原始图片的像素数组 for(int i = 0;i < height;++i) { for(int j = 0;j < width;++j) { (*(imagedata + i * width + j)).blue = 0; //(*(imagedata + i * width + j)).green = 0; //(*(imagedata + i * width + j)).red = 0; } } //读出图片的像素数据 读取时一次一行 读高度数的行 fread(imagedata,sizeof(struct tagIMAGEDATA) * width,height,fpi);/////size_t fread( void *buffer, size_t size, size_t count, FILE *stream ); fclose(fpi); //////////=====图片裁剪处理======////////// int leftdownx,leftdowny,rightupx,rightupy;/////用户输入数值 int Rleftdownx,Rleftdowny,Rrightupx,Rrightupy;/////转换成实际可以使用数值 cout<<"请输入要裁剪的矩形区域的左下角和右上角的坐标(连续四个整数值,如50 50 300 300):"<<endl; cin>>leftdownx; cin>>leftdowny; cin>>rightupx; cin>>rightupy; if(leftdownx<=0||leftdowny<=0)////将用户输入的矩形框限定在原图像中 { Rleftdownx=0; Rleftdowny=0; } else { Rleftdownx=leftdownx; Rleftdowny=leftdowny; } if(rightupx>=width) { Rrightupx=width; } else { Rrightupx=rightupx; } if(rightupy>=height) { Rrightupy=height; } else { Rrightupy=rightupy; } int CutWidth=Rrightupx-Rleftdownx; ////////图像每一行的字节数必须是4的整数倍 CutWidth = (CutWidth * sizeof(IMAGEDATA) + 3) / 4 * 4;////矩形框实际宽度 int CutHeight=Rrightupy-Rleftdowny;////矩形框实际高度 imagedataCut = (IMAGEDATA*)malloc(CutWidth * CutHeight * sizeof(IMAGEDATA));///为裁剪后图像分配存储空间 //初始化裁剪后图片的像素数组 一个字节一个字的写入 for( i = 1;i < CutHeight ;++i) { for(int j = 0;j < CutWidth ;++j) { (*(imagedataCut + i * CutWidth + j)).blue = 0; } } imagedata+=(Rleftdowny)*width+Rleftdownx;////原始图像数据数组指针移动到矩形框的左下角。 for(int k = 0;k < CutHeight;++k)////裁剪区域数据提取 { for(int l = 0;l < CutWidth;++l) *(imagedataCut + k * CutWidth + l) = *(imagedata+ k * width + l + Rleftdownx);////此式子一定要注意写法。主要是注意二维数组指针的用法。 } //保存bmp图片 if((fpw=fopen("Cutresult.bmp","wb"))==NULL) { cout<<"create the bmp file error!"<<endl; return NULL; } fwrite(&bfType_w,1,sizeof(WORD),fpw); fwrite(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpw); strInfo.biWidth = CutWidth; strInfo.biHeight = CutHeight; fwrite(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpw); //保存调色板数据 for(unsigned int nCounti=0;nCounti<strInfo.biClrUsed;nCounti++) { fwrite(&strPla[nCounti].rgbBlue,1,sizeof(BYTE),fpw); fwrite(&strPla[nCounti].rgbGreen,1,sizeof(BYTE),fpw); fwrite(&strPla[nCounti].rgbRed,1,sizeof(BYTE),fpw); fwrite(&strPla[nCounti].rgbReserved,1,sizeof(BYTE),fpw); } //保存像素数据 for(i =0;i < CutHeight;++i) { for(int j = 0;j < CutWidth;++j) { fwrite( &((*(imagedataCut + i * CutWidth + j)).blue),1,sizeof(BYTE),fpw); //fwrite( &((*(imagedataRot + i * FloatToIntwidth + j)).green),1,sizeof(BYTE),fpw); //fwrite( &((*(imagedataRot + i * FloatToIntwidth + j)).red),1,sizeof(BYTE),fpw); } } printf("裁剪变换完成,请查看Cutresult.bmp文件。\n\n"); fclose(fpw); //释放内存 //delete[] imagedata;///不能释放imagedata,里面还有数据。 delete[] imagedataCut; ///////===========图片裁剪处理结束======//////// ///////==========重新打开文件======//////// cout<<"请输入所要读取的文件名(Cutresult.bmp):"<<endl; cin>>strFile; fpi=fopen(strFile,"rb"); if(fpi != NULL) { //先读取文件类型 WORD bfType; fread(&bfType,1,sizeof(WORD),fpi); if(0x4d42!=bfType)///if !=BMP { cout<<"the file is not a bmp file!"<<endl; return NULL; } printf("\n读取到的文件是%s。\n\n",strFile); //读取bmp文件的文件头和信息头 fread(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpi); showBmpHead(strHead);//显示文件头 fread(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpi); showBmpInforHead(strInfo);//显示文件信息头 //读取调色板 for(unsigned int nCounti=0;nCounti<strInfo.biClrUsed;nCounti++) { fread((char *)&(strPla[nCounti].rgbBlue),1,sizeof(BYTE),fpi); fread((char *)&(strPla[nCounti].rgbGreen),1,sizeof(BYTE),fpi); fread((char *)&(strPla[nCounti].rgbRed),1,sizeof(BYTE),fpi); fread((char *)&(strPla[nCounti].rgbReserved),1,sizeof(BYTE),fpi); } width = strInfo.biWidth; height = strInfo.biHeight; imagedata = (IMAGEDATA*)malloc(width * height * sizeof(IMAGEDATA));///为原始图像分配存储空间 //初始化原始图片的像素数组 for(int i = 0;i < height;++i) { for(int j = 0;j < width;++j) { (*(imagedata + i * width + j)).blue = 0; //(*(imagedata + i * width + j)).green = 0; //(*(imagedata + i * width + j)).red = 0; } } //读出图片的像素数据 读取时一次一行 读高度数的行 fread(imagedata,sizeof(struct tagIMAGEDATA) * width,height,fpi);///// fclose(fpi); } else { cout<<"file open error!"<<endl; return NULL; } //图片缩放处理 cout<<"请输入要缩放的倍数:"<<endl; cin>>ExpScalValue; ///如果ExpScalValue含有小数,需要整数化 ///对期望的缩放结果取整 FloatToIntwidth=(int)(ExpScalValue*width); FloatToIntheight=(int)(ExpScalValue*height); //图像每一行的字节数必须是4的整数倍 FloatToIntwidth = (FloatToIntwidth * sizeof(IMAGEDATA) + 3) / 4 * 4; imagedataScal = (IMAGEDATA*)malloc(FloatToIntwidth * FloatToIntheight * sizeof(IMAGEDATA));///为缩放后图像分配存储空间 //初始化缩放后图片的像素数组 一个字节一个字的写入 for( i = 1;i < FloatToIntheight ;++i) { for(int j = 0;j < FloatToIntwidth ;++j) { (*(imagedataScal + i * FloatToIntwidth + j)).blue = 0; //(*(imagedataRot + i * FloatToIntwidth + j)).green = 0; //(*(imagedataRot + i * FloatToIntwidth + j)).red = 0; } } int pre_i,pre_j,after_i,after_j;//缩放前后对应的像素点坐标 for( i=0;i<FloatToIntheight;++i) { for(int j=0;j<FloatToIntwidth;++j) { after_i=i; after_j=j; pre_i = (int)(after_i/ExpScalValue);/////取整,插值方法为:最邻近插值(近邻取样法) pre_j = (int)(after_j/ExpScalValue); if(pre_i >= 0 && pre_i < height && pre_j >= 0 && pre_j < width)//在原图范围内 *(imagedataScal + i * FloatToIntwidth + j) = *(imagedata + pre_i * width + pre_j); } } //保存bmp图片 if((fpw=fopen("Scalresult.bmp","wb"))==NULL) { cout<<"create the bmp file error!"<<endl; return NULL; } fwrite(&bfType_w,1,sizeof(WORD),fpw); fwrite(&strHead,1,sizeof(tagBITMAPFILEHEADER),fpw); strInfo.biWidth = FloatToIntwidth; strInfo.biHeight = FloatToIntheight; fwrite(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fpw); //保存调色板数据 for( nCounti=0;nCounti<strInfo.biClrUsed;nCounti++) { fwrite(&strPla[nCounti].rgbBlue,1,sizeof(BYTE),fpw); fwrite(&strPla[nCounti].rgbGreen,1,sizeof(BYTE),fpw); fwrite(&strPla[nCounti].rgbRed,1,sizeof(BYTE),fpw); fwrite(&strPla[nCounti].rgbReserved,1,sizeof(BYTE),fpw); } //保存像素数据 for(i =0;i < FloatToIntheight;++i) { for(int j = 0;j < FloatToIntwidth;++j) { fwrite( &((*(imagedataScal + i * FloatToIntwidth + j)).blue),1,sizeof(BYTE),fpw); //fwrite( &((*(imagedataRot + i * FloatToIntwidth + j)).green),1,sizeof(BYTE),fpw); //fwrite( &((*(imagedataRot + i * FloatToIntwidth + j)).red),1,sizeof(BYTE),fpw); } } printf("缩放变换完成,请查看Scalresult.bmp文件。\n\n"); fclose(fpw); //释放内存 delete[] imagedata; delete[] imagedataScal;////////==============图像缩放处理结束==============/////// ///////===============图片旋转处理====================/////// cout<<"请输入所要读取的文件名(Scalresult.bmp):"<<endl; cin>>strFile; FILE *fRor,*fRow; int AnyX=0;////为适应任意形状做的长宽调整参数 fRor=fopen(strFile,"rb"); if(fRor != NULL){ //先读取文件类型 WORD bfType; fread(&bfType,1,sizeof(WORD),fRor); if(0x4d42!=bfType) { cout<<"the file is not a bmp file!"<<endl; return NULL; } printf("\n读取到的文件是%s。\n\n",strFile); //读取bmp文件的文件头和信息头 fread(&strHead,1,sizeof(tagBITMAPFILEHEADER),fRor); showBmpHead(strHead);//显示文件头 fread(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fRor); showBmpInforHead(strInfo);//显示文件信息头 //读取调色板 for(unsigned int nCounti=0;nCounti<strInfo.biClrUsed;nCounti++) { fread((char *)&(strPla[nCounti].rgbBlue),1,sizeof(BYTE),fRor); fread((char *)&(strPla[nCounti].rgbGreen),1,sizeof(BYTE),fRor); fread((char *)&(strPla[nCounti].rgbRed),1,sizeof(BYTE),fRor); fread((char *)&(strPla[nCounti].rgbReserved),1,sizeof(BYTE),fRor); } width = strInfo.biWidth; height = strInfo.biHeight; AnyX=max(width,height);///适合任意形状图形 //图像每一行的字节数必须是4的整数倍 width = (width * sizeof(IMAGEDATA) + 3) / 4 * 4; //imagedata = (IMAGEDATA*)malloc(width * height * sizeof(IMAGEDATA)); imagedata = (IMAGEDATA*)malloc(width * height);// imagedataRot = (IMAGEDATA*)malloc(2 * width * 2 * height * sizeof(IMAGEDATA)); imagedataRot = (IMAGEDATA*)malloc(2 * AnyX * 2 * AnyX * sizeof(IMAGEDATA)); //初始化原始图片的像素数组 for(int i = 0;i < height;++i) { for(int j = 0;j < width;++j) { (*(imagedata + i * width + j)).blue = 0; //(*(imagedata + i * width + j)).green = 0; //(*(imagedata + i * width + j)).red = 0; } } //初始化旋转后图片的像素数组 //for( i = 0;i < 2 * height;++i) for( i = 0;i < 2 * AnyX;++i) { for(int j = 0;j < 2 * AnyX;++j) { (*(imagedataRot + i * 2 * AnyX + j)).blue = 0; //(*(imagedataRot + i * 2 * width + j)).green = 0; //(*(imagedataRot + i * 2 * width + j)).red = 0; } } //读出图片的像素数据 fread(imagedata,sizeof(struct tagIMAGEDATA) * width,height,fRor); fclose(fRor); } else { cout<<"file open error!"<<endl; return NULL; } //图片旋转处理 double angle;//要旋转的弧度数 int midX_pre,midY_pre,midX_aft,midY_aft;//旋转所围绕的中心点的坐标 midX_pre = width / 2; midY_pre = height / 2; midX_aft = AnyX; midY_aft = AnyX; cout<<"请输入要旋转的角度(0度到360度,逆时针旋转):"<<endl; cin>>RotateAngle; angle = 1.0 * RotateAngle * PI / 180; /* 图像旋转的几何公式 x2=x1*cos(θ2)-y1*sin(θ2); y2=x1*sin(θ2)+y1*cos(θ2); */// for( i = 0;i < 2 * height;++i) for( i = 0;i < 2 * AnyX;++i) { for(int j = 0;j < 2 * AnyX;++j) { after_i = i - midX_aft;//坐标变换,如果不变换坐标,以(0,0)为参考点旋转后图片将转出坐标外。 after_j = j - midY_aft; pre_i = (int)(cos((double)angle) * after_i - sin((double)angle) * after_j) + midX_pre ; /////取整,插值方法为:最邻近插值(近邻取样法) pre_j = (int)(sin((double)angle) * after_i + cos((double)angle) * after_j) + midY_pre; /*after_i = i; after_j = j; ////坐标不变换 pre_i = (int)(cos((double)angle) * after_i - sin((double)angle) * after_j) ; pre_j = (int)(sin((double)angle) * after_i + cos((double)angle) * after_j) ;*/////坐标不变换效果 if(pre_i >= 0 && pre_i < height && pre_j >= 0 && pre_j < width)//在原图范围内 *(imagedataRot + i * 2 * AnyX + j) = *(imagedata + pre_i * AnyX + pre_j); } } //保存bmp图片 if((fRow=fopen("Rotresult.bmp","wb"))==NULL) { cout<<"create the bmp file error!"<<endl; return NULL; } bfType_w=0x4d42; fwrite(&bfType_w,1,sizeof(WORD),fRow);//fRow自动+=2; fwrite(&strHead,1,sizeof(tagBITMAPFILEHEADER),fRow); strInfo.biWidth = 2 * AnyX;//width; strInfo.biHeight = 2 * AnyX;//height; fwrite(&strInfo,1,sizeof(tagBITMAPINFOHEADER),fRow); //保存调色板数据 for( nCounti=0;nCounti<strInfo.biClrUsed;nCounti++) { fwrite(&strPla[nCounti].rgbBlue,1,sizeof(BYTE),fRow); fwrite(&strPla[nCounti].rgbGreen,1,sizeof(BYTE),fRow); fwrite(&strPla[nCounti].rgbRed,1,sizeof(BYTE),fRow); fwrite(&strPla[nCounti].rgbReserved,1,sizeof(BYTE),fRow); } //保存像素数据 for( i =0;i < 2 * AnyX;++i) { //for(int j = 0;j < 2 * width;++j) for(int j = 0;j < 2 * AnyX;++j) { fwrite( &((*(imagedataRot + i * 2 * AnyX + j)).blue),1,sizeof(BYTE),fRow); //fwrite( &((*(imagedataRot + i * 2 * width + j)).green),1,sizeof(BYTE),fpw); //fwrite( &((*(imagedataRot + i * 2 * width + j)).red),1,sizeof(BYTE),fpw); } } printf("旋转变换完成,请查看Rotresult.bmp文件。\n\n"); fclose(fRow); //释放内存 delete[] imagedata; delete[] imagedataRot;/////==================图像旋转处理结束============/////////// return 1;} /*程序用到的图片从谷歌图片里可以搜到,只要是8位BMP位图即可。 将名字改成06.bmp、07.bmp或w.bmp*/
相关文章推荐
- 【数字图像处理】<纯C++>读取、裁剪、缩放、旋转和存储8位bmp灰度图像
- 【数字图像处理】YUV420转RGB并BMP存储<纯C++实现>
- 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现
- 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现
- 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现(转载)
- 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现
- 【数字图像处理】C++读取、旋转和保存bmp图像文件编程实现
- MATLAB批量读取文件夹下的图像,并对图像进行裁剪为指定大小,旋转,缩放,加噪声处理,rgb2hsv,批量保存
- 【数字图像】C++8位和24位BMP位图的平滑、锐化、二值化处理,以及24位真彩图的灰度化
- C++读取、旋转和保存bmp图像文件编程实现
- <PY><PIL>图像的透明和尺寸处理--为wp8.1生成图标
- 1023 Have Fun with Numbers (20)(vector<int> di,di读取string里的数字)
- C/C++ BMP(24位真彩色)图像处理(4)------图像の旋转
- Python-OpenCv之图像基本处理-平移,缩放,旋转,翻转,裁剪及仿射变换
- Java图片缩略图裁剪水印缩放旋转压缩转格式-Thumbnailator图像处理
- C/C++ BMP(24位真彩色)图像处理(4)------图像の旋转
- 数字图像处理基础(十一)---缩放、角度旋转和仿射变换及代码实现
- Java图片缩略图裁剪水印缩放旋转压缩转格式-Thumbnailator图像处理
- 【数字图像处理】六.MFC空间几何变换之图像平移、镜像、旋转、缩放具体解释
- mysql connector c++ {realStr=<读取字符串的字符时出错。> }