完成一个简单的c/c++工程

说明

什么是C++

在C的基础上,一九八三年贝尔实验室的Bjarne Strou-strup推出了C++。 C++进一步扩充和完善了C语言,成为一种面向 对象的程序设计语言。这是官方解释,翻译成人话就是:C 的plus版。向下兼容C,既可以用来操作底层,也可以面向对象编程。个人更喜欢C++。

功能

查找UTF - 8 编码的txt文档中含有特定汉字的句子,以文件形式输出。这个工程是C++ 编写的,但是本人当时没空优化代码。所以没用用到“类”。有C基础的朋友,可放心大胆食用。

前期工作

需求

:查找UTF - 8 编码的txt文档中含有特定汉字的句子,以文件形式输出。
老师是个老工程师,有点东西,他说他以前用C实现过,于是需求就来了。

需求分析

要输出含有特定汉字的句子,就要对句中的每个汉字与目标汉字进行比较,类似于 if(’ a ’ == ’ a ') ,只不过要比较的不是 ’ a ’ 而是 “我”,这个工程是在Linux下的,我建的txt默认为utf - 8 形式,其实什么编码形式都没关系,原理是一样的。
要比较某句话,首先要将句子从文件中读出,之后再才能判断,老师给我们的提示是:C有一个函数可以按行读取文本文件。一行行读出之后判断有没有,但这就有个问题,如果这句话一行写不完,或者一行有两句话,这就很尴尬,又得动脑想。比较懒又爱浪的我不喜欢这种方法。
我的解决方法是:一个字一个字读,一次读一句,读进来顺便判断,如果有目标文字,等到这一句完结,将这句话输出,如果没有,将这个链表删除,下一句开始新建一个链表(后来我觉得可以不用删除,覆盖就可以,不够就新增。等链表长到特定长度就删除后面一部分。)因为我之前写过链表,所以稍作修改就可以拿来用,既快捷,又浪。

写程序

核心代码

UTF-8 中的汉字,5万多是占三个字节,6万多占4个字节。
占用 3 个字节的范围
U+2E80 - U+2EF3 : 0xE2 0xBA 0x80 - 0xE2 0xBB 0xB3 共 115 个
U+2F00 - U+2FD5 : 0xE2 0xBC 0x80 - 0xE2 0xBF 0x95 共 213 个
U+3005 - U+3029 : 0xE3 0x80 0x85 - 0xE3 0x80 0xA9 共 36 个
U+3038 - U+4DB5 : 0xE3 0x80 0xB8 - 0xE4 0xB6 0xB5 共 7549 个
U+4E00 - U+FA6A : 0xE4 0xB8 0x80 - 0xEF 0xA9 0xAA 共 44138 个
U+FA70 - U+FAD9 : 0xEF 0xA9 0xB0 - 0xEF 0xAB 0x99 共 105 个
合计: 52156 个
占用 4 个字节的范围
U+20000 - U+2FA1D : 0xF0 0xA0 0x80 0x80 - 0xF0 0xAF 0xA8 0x9D 共 64029 个
合计: 64029 个
UTF-8编码的每个多字节符号,每个字节全部是由 0x80 到 0xFD 的十六进制数表示的。所以可以将数字、字母以及许多ASCII中常用的符号区分开。
区分三字节和四字节字符只需要看第一个字节的大小就好了。
我采用的读取方是按字节读取,一次读取一个char也就是8位。那么,按照需求最多只要读取4个char就可以了。所以需要一个判断读取长度的函数。(我工程里没有,只能读取3个字节,算是个bug,所以我要在线改bug了)

int JudgeLongth( char  JudgeLongth_temp ){
 std::ifstream file;
 file.open(name.c_str());
 file >> JudgeLongth_temp;
 if((JudgeLongth_temp >= 0) || (JudgeLongth_temp <= 127)){
		return 1;
	}
else if((JudgeLongth_temp >= 226) || (JudgeLongth_temp <= 239)){
 		return 3;
 	}
else {  return 4 ; }
}

上面的代码可以实现根据读取一个字节,判断完整读出该字符需要几个字节。然后再读就可以读出一个汉字了。(由于上面的代码是在写此博客时写的,懒得改程序,所以程序均按3字节处理)

void OutPutData(const std::string name){
 int l = 0;
 int a = 0;
 std::ifstream file;         //定义一个ifstream对象,其中包括可按字节读取数据的成员函数
 file.open(name.c_str());    //将目标文件赋值给该对象*
 while(IfRead){       //当Ifread == StopRead   时跳出
  file >> temp;		// 从文件中读出一个字节给临时变量temp
  if((int(temp)>=127)||(int(temp)<=0)){   //判断是不是要读取三个字节,测试文档中只有汉字,所以为了省事只判断了是不是3字节汉字
   CNCharRead[l] = temp ;         //将临时变量中的数据存进长度为3的数组
   l++;
   if(l >= 3){                              //当存满时将长度为3的数组存如链表的结点
    l = 0;
    AddChainNode(CNCharRead);
    if(JudgeCnChar(CNCharRead,CNCharInput)) {  //判断读出的汉字是否为目标汉字
     Target = Exist ; 
    }
    if(JudgeCnChar(CNCharRead,FullStop)){    //如果遇到句子停止符号,判断是否出现过目标汉字
     if(Target){                    //如果出现过读出这句话并重设target 标志
        ReadAll();
        Target = NotExist;    
     }    
     DeleteAll();               //否则删除整个链表 
    }
    if(JudgeCnChar(CNCharRead,FileEnd)) {     //如果遇到文件末尾将 StopRead 赋值给Ifread 下次循环退出。 
     IfRead = StopRead ;  
    }
   }   
  } 
 }
 file.close();         //关闭文件
}

上面的函数就可以读出单个汉字,同时也是工程的核心代码。(我忘记了我用到了“类”,打脸了,不过凑活看吧)

编写头文件

多文件编程是开发工程必备的,这种方式将一个功能或者模块以一个头文件和一个C/C++文件的形式实现,方便大工程工作的分配,如果那一快遇到bug,可以更好的修改。而且当你不希望他人拿到工程源码时,可以将C/C++源码编译成库文件配合头文件给他人使用,这样别人只知道你有什么功能,但看不到功能是如何实现的。

#ifndef __CHAIN_H__
#define __CHAIN_H__

#include <iostream>

void AddChainNode(const char data[3]);
void ReadAll();
void DeleteAll();

struct node{
	 char a[3];
	 struct node *previous;
	 struct node *next;
	 struct node *middle;
};

#endif
#ifndef __FINDSENTENCE_H__
#define __FINDSENTENCE_H__

#include <fstream>
#include <string>
#include <locale>
#include <cstdlib>

#define Success   1
#define Failed    0
#define Exist    1
#define NotExist  0
#define Same      1
#define NotSame   0
#define KeepRead  1
#define StopRead  0

int IfExist(const std::string name);
int JudgeCnChar(const char Input[3] ,const char Read[3]);
void CreatFile();
void OutPutData(const std::string name);
void GetCnChar();
void SaveData();
void GetPunctuation();

#endif

以上两个头文件第一个是链表实现的头文件,第二个是查询句子功能的头文件,在查询头文件中宏定义了各种标志,核心代码中的KeepRead、NotExist 等本质都是0或1。

头文件实现

链表部分

struct node *p = NULL;  //define middle pointer
struct node *h = NULL;  //define hander pointer
struct node *t = NULL;  //define teil pointer

int longth = 0;
char stop = '0' ;

void AddChainNode(const char data[3]){
 if((h == NULL)&&(t == NULL)){
	  p = new node;
	  p->a[0] = data[0];
	  p->a[1] = data[1];
	  p->a[2] = data[2]; 
	  h = p;
	  t = p;
	  longth++;
	 }
else{
	  p = new node;
	  p->a[0] = data[0];
	  p->a[1] = data[1];
	  p->a[2] = data[2];
	  t->next = p;
	  t->next->previous = t;
	  t = p;
	  longth++;
	 }
}

链表添加节点并保存数据的实现

void ReadAll(){
 std::ofstream file;
 struct node *read;
 file.open("output.txt",std::ios::app);
 read = h;
 for(int i = 0 ; i < longth ; i++ ){
  file << read->a[0] ;
  file << read->a[1] ;
  file << read->a[2] ;
  read = read->next;
 }
 file << "\n";
 file.close();
}

将链表中的数据输出到输出文件中

查询部分

int JudgeCnChar(const char Input[3] ,const char Read[3]){
 if((Read[0] == Input[0])&&
    (Read[1] == Input[1])&&
    (Read[2] == Input[2])){ 
  return Same;
 } 
 else{
  return NotSame;
 }
}

判断读取到的字符是否与目标字符相等

void GetCnChar(){
 std::cout<<"please enter the chinese character data\n";
 std::cin >> CNCharInput;
}

读取目标字符

结语

把这些东西合起来就是实现了这个简单的需求,但是…老师他竟然不看!!!那我秀谁啊f***。如果路过的大佬觉得哪里不对请指正,我会改的,如果觉得这代码需要优化,那就算了,我比较懒。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值