[新手編程訓練項目]007——C語言文本文件的含通配符的模糊單詞搜索

日期:2017年11月30日

任務目標:編寫一個程序可以對文本文件進行含通配符的模糊單詞搜索,其中‘*’表示多個位置字符,‘?’表示單個未知字符,並可以使用字母以及上述兩種符號組合搜索。

編程環境:Windows 10 Enterprise、Visual Studio 2017 Enterprise、編譯字符集Unicode


注:由於筆者水平並不高,以下代碼可能包含bug。


首先是頭文件:functions.h

#pragma once

#ifndef __FUNCTIONS_H
#define	__FUNCTIONS_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#define MAX_LINE 1023

//單詞結構體
struct word_type
{
	char* word_store;				//指向動態字符串存儲的指針
	int len;						//單詞長度
};

//不定長輸入
char* get_str(void);
//輸入函數
void input_info(char** text_str, char** addr_str);
//讀取一個單詞
void read_word(FILE* fp, char* src_str);

bool match(char* str1, char* pattern);

#endif /* __FUNCTIONS_H */
其中布爾函數使用了stdbool.h頭文件


以下是主函數文件中的內容:main.c

#pragma warning(disable:4996) //VS 編譯聲明?

#include<functions.h>

int main()
{
	//初始模糊字段
	char* src_str = NULL;
	//文本地址
	char* tgt_str = NULL;
	printf("##### program initiating #####\n");

	input_info(&src_str, &tgt_str);
	printf("模糊字段:%s\n文本地址:%s\n", src_str, tgt_str);
	
	FILE *fp;
	if ((fp = fopen(tgt_str, "r")) == NULL)
	{
		printf("Open failure....\n");
		exit(1);
	}

	read_word(fp, src_str);

	fclose(fp);

	printf("##### running complete #####\n");
	system("pause");
	return 0;
}

void input_info(char** text_str, char** addr_str)
{
	printf("輸入模糊字段\n");
	*text_str = get_str();

	printf("輸入文本地址\n"); 
	*addr_str = get_str();
}

char* get_str(void)											
{
	char *str=NULL;
	int j = 1;

	str = (char*)malloc(sizeof(char)*(j + 1));
	if (NULL == str)
	{
		exit(1);
	}

	while ((str[j-1] = getchar()) != '\n')
	{
		j++;
		str = (char*)realloc(str, j);
		if (NULL == str)
		{
			exit(1);
		}
	}
	str[j - 1] = '\0';

	return(str);
}

其中get_str()函數是使用realloc()函數改進的變長字符串輸入函數,是前幾個訓練項目的修改。

下面是讀取文件函數

void read_word(FILE* fp,char* src_str)
{
	char *token;
	const char seps[9] = " ,.:\t\n\"\'";
	char temp[MAX_LINE+1];
	int i = 0;
	bool answer;

	while (fgets(temp, MAX_LINE, fp)!=NULL)
	{	
		//fgets(temp, MAX_LINE, fp);
		token = strtok(temp, seps);
		while (token != NULL)
		{
			//printf("%s\n", token);
			answer = match(token, src_str);
			if (answer == true)
				printf("true:%s\n", token);
			token = strtok(NULL, seps);
		}
	}
}

使用了strtok()函數,據VS說這函數不安全??

反正我是用了,暫時還不管那麼多,這個就是使用分隔符字符串將目標字符串按分隔符分開並將分割完的字符串地址返回,是需要循環的,具體使用參見MSDN上的例子


最後也是最重要的部分:模糊匹配函數,這個函數我是參考了網上的代碼,並做出了一定修改,使得可以對長度進行匹配控制,就自己的幾次測試來看沒有出現問題,如果出現問題請一定告知我

bool match(char* str1, char* pattern)
{
	if (str1 == NULL)		//待匹配字符串指向空,返回false
		return false;
	if (pattern == NULL)		//模式字符串指向空,返回false
		return false;
	int len1 = strlen(str1);		//待匹配字符串的長度
	int len2 = strlen(pattern);			//模式字符串的長度
	int mark = 0;		//用於分段標記,'*'分隔的字符串
	int p1 = 0, p2 = 0;		//待匹配/模式的偏移量
	int flag_s = 0, flag_q = 0;			//如果出現* 和?則標識置位

	while ((p1<len1) && (p2<len2))			//在長度範圍內運行
	{
		if (pattern[p2] == '?')			//如果模式中有‘?’通配符,則:同時偏移1,並進入下一循環
		{
			flag_q = 1;
			p1++;
			p2++;
			continue;
		}
		if (pattern[p2] == '*')//如果當前是*號,則mark前面一部分已經獲得匹配,//從當前點開始繼續下一個塊的匹配
		{
			flag_s = 1;
			p2++;
			mark = p2;
			continue;
		}
		if (str1[p1] != pattern[p2])
		{
			if (p1 == 0 && p2 == 0)
			{
				//如果是首字符,特殊處理,不相同即匹配失敗
				return false;
			}
			if (flag_s == 1)
			{
				p1 -= p2 - mark - 1;
				p2 = mark;
				continue;
			}
			else
			{
				return false;
			}
			
		}
		//此處處理相等的情況
		p1++;
		p2++;
	}
	if (p2 == len2)
	{
		if (p1 == len1)
		{
			//兩個字符串都結束了,說明模式匹配成功
			return true;
		}
		if (pattern[p2 - 1] == '*')
		{
			//str1還沒有結束,但pattern的最後一個字符是*,所以匹配成功
			return true;
		}
		else
		{
			return false;
		}
	}
	while (p2<len2)
	{
		//pattern多出的字符只要有一個不是*,匹配失敗
		if (pattern[p2] != '*')
			return false;
		p2++;
	}
	if ((len2 != len1) && (flag_s == 0))			//如果沒有出現*但是長度不一致則返回false
	{
		return false;
	}
	return true;
}

我加的註釋還是比較多的= =

可能不是很規範= =

最後放個github的地址,如果有需要可以去下載全部文件,如果有bug請告訴我,畢竟是練習,以提高水平爲主。

https://github.com/phantomT/Training-program-004-blur_search







發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章