1. 程式人生 > >第十次CCF認證 第三題 Markdown 字串處理

第十次CCF認證 第三題 Markdown 字串處理

Markdown 是一種很流行的輕量級標記語言(lightweight markup
language),廣泛用於撰寫帶格式的文件。例如以下這段文字就是用 Markdown 的語法寫成的:

這些用 Markdown 寫成的文字,儘管本身是純文字格式,然而讀者可以很容易地看出它的文件結構。同時,還有很多工具可以自動把
Markdown 文字轉換成 HTML 甚至 Word、PDF 等格式,取得更好的排版效果。例如上面這段文字通過轉化得到的 HTML
程式碼如下所示:

本題要求由你來編寫一個 Markdown 的轉換工具,完成 Markdown 文字到 HTML 程式碼的轉換工作。簡化起見,本題定義的
Markdown 語法規則和轉換規則描述如下:   ●區塊:區塊是文件的頂級結構。本題的 Markdown 語法有 3
種區塊格式。在輸入中,相鄰兩個區塊之間用一個或多個空行分隔。輸出時刪除所有分隔區塊的空行。
  ○段落:一般情況下,連續多行輸入構成一個段落。段落的轉換規則是在段落的第一行行首插入 <p>

,在最後一行行末插入 </p>
  ○標題:每個標題區塊只有一行,由若干個 # 開頭,接著一個或多個空格,然後是標題內容,直到行末。#
的個數決定了標題的等級。轉換時,# Heading 轉換為 <h1>Heading</h1>## Heading 轉換為
<h2>Heading</h2>,以此類推。標題等級最深為 6。   ○無序列表:無序列表由若干行組成,每行由 *
開頭,接著一個或多個空格,然後是列表專案的文字,直到行末。轉換時,在最開始插入一行 <ul>,最後插入一行
</ul>;對於每行,* Item 轉換為 <li>Item</li>
。本題中的無序列表只有一層,不會出現縮排的情況。
  ●行內:對於區塊中的內容,有以下兩種行內結構。   ○強調:_Text_ 轉換為
<em>Text</em>。強調不會出現巢狀,每行中 _ 的個數一定是偶數,且不會連續相鄰。注意 _Text_
的前後不一定是空格字元。   ○超級連結:[Text](Link) 轉換為 <a href="Link">Text</a>。超級連結和強調可以相互巢狀,但每種格式不會超過一層。 輸入格式
  輸入由若干行組成,表示一個用本題規定的 Markdown 語法撰寫的文件。 輸出格式   輸出由若干行組成,表示輸入的 Markdown
文件轉換成產生的 HTML 程式碼。 樣例輸入

Hello

Hello, world! 樣例輸出

Hello

Hello, world!

評測用例規模與約定
  本題的測試點滿足以下條件:   ●本題每個測試點的輸入資料所包含的行數都不超過100,每行字元的個數(包括行末換行符)都不超過100。
  ●除了換行符之外,所有字元都是 ASCII 碼 32 至 126 的可列印字元。   ●每行行首和行末都不會出現空格字元。
  ●輸入資料除了 Markdown 語法所需,內容中不會出現
#*_[]()<>& 這些字元。   ●所有測試點均符合題目所規定的
Markdown 語法,你的程式不需要考慮語法錯誤的情況。   每個測試點包含的語法規則如下表所示,其中“√”表示包含,“×”表示不包含。

思路:
理清題意,分情況思考,總共三種區塊型別,兩種行內處理,分5個模組來做,方便除錯執行。
三區塊型別分為三種state,程式初始化時,state=0表示不確定狀態,需根據輸入語句改變state值, 當輸入語句為空行時,state恢復為0
1,2,3分別對應標題、段落、無序列表。state確認之後,除非變為0,否則不變。變為0後輸出區塊型別結束符
##注意事項:

  1. 標題只有一行
  2. "在字串中表示是要加轉義符\的。
  3. 從檔案中讀入時,最後一個是沒有讀入的,所以就得自己做判斷,是否輸入已經結束,然後輸出結尾

程式碼:

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

int state=0;//表示區塊型別
string str;

int turn();//判斷是否為空行,做段落和無序列表的結尾輸出 
void first(); //判斷區塊型別,負責標題輸出和段落以及無序列表的開頭輸出 
void block(); //開頭空格及符號處理 
void header();//標題處理 
void paragraph();//段落處理 
void lists();//無序列表處理 
void stress();//強調 

int turn(){ //判斷是否為空行,做段落和無序列表的結尾輸出 
	if(str==""){
		if(state==2) cout<<"</p>"<<endl; 
		else if(state==3) cout<<"</ul>"<<endl;
		state=0;
		return 1;
	}
	return 0; 
}
void stress(string &str){
	int pos,pos2,pos3;
	pos2=str.find("<a");
	pos3=str.find("</a");
	pos=str.find("_");
	if(pos>pos2 && pos<pos3) return;
	while(pos>=0 ){
		str.replace(pos,1,"<em>");
		pos=str.find("_");
		str.replace(pos,1,"</em>");
		pos=str.find("_"); 
	}
} 
void link(){
	string str1,str2,str3;
	int pos,pos1,pos2,pos3;
	pos=str.find("[");
	pos1=str.find("]");
	if(pos>=0){
		pos2=str.find("(");
		pos3=str.find(")");
		str2=str.substr(pos2+1,pos3-pos2-1);
		str3 =str.substr(pos+1,pos1-pos-1);
		stress(str3);
		str1 += "<a href=\"" + str2 + "\">" + str3 + "</a>";
		str.replace(pos,pos3-pos+1,str1);
	}
}
void block(){//開頭空格及符號處理 
	while((str[0]==' ' || str[0]=='#' || str[0]=='*')){
		str.erase(0,1);
	} 
} 
void header(){//標題處理 
	int i=0;
	while(str[i]=='#'){
		i++;
	}
	block();
	link();
	stress(str);
	cout<<"<h"<<i<<">"<<str<<"</h"<<i<<">"<<endl;
}
void paragraph(){
	block();
	link();
	stress(str);
	cout<<endl<<str;
} 
void lists(){
	block();
	link();
	stress(str);
	cout<<"<li>"<<str<<"</li>"<<endl;
} 
void first(char c){ //判斷區塊型別,負責標題輸出和段落以及無序列表的開頭輸出 
	if(c=='#'){ //標題 
		header();
		state=0;
	}
	else if(c=='*'){ //list 
		cout<<"<ul>"<<endl;
		lists();
		state=3;
	} 
	else{ //文字 
		block();
		link(); 
		stress(str);
		cout<<"<p>"<<str;
		state=2;
	} 
	return;
} 

void deal(){
	if(turn()==1) return;//先判斷輸入是否為空行,是的話做相應的段落和無序列表的結尾輸出,無相應的,則直接跳過 
	if(state==2){
		paragraph();
	}else if(state==3){
		lists();
	}
	if(state==0)first(str[0]);//判斷是否有輸入,有輸入的話,輸出相應的開頭及輸出 
}
 
int main(){
	freopen("data.txt","r",stdin);
	while(getline(cin,str)){ 	
		deal();
	}
	if(state==2) cout<<"</p>"<<endl; 
	else if(state==3) cout<<"</ul>"<<endl;
}

很遺憾這個程式碼只能拿90分,還有10分應該是卡在超連結和強調的相互巢狀處理上。拿別人滿分程式碼跟自己程式碼一起測了不少巢狀樣例,發現都沒什麼區別。不知道巢狀到底是怎樣的巢狀法。很難受