Erlang實戰建立文本索引
為文本建立索引是文本信息處理的一個重要的任務,給定一個由英文單詞構成的文件,為文件中所有單詞建立索引,記錄每個單詞出現的行號和每行出現的次數,并將索引存入一個文件。在Erlang實戰練習(六)中我強調了當時建立文本索引的時候太粗糙,一是使用了盡量規避的進程字典的方式;二是分詞使用的是正則表達式,不夠靈活。本文將改進我以前建立文本索引的方式,使用ETS來存儲單詞及其索引列表,同時拆分詞使用Erlang提供的string:token模塊,更加靈活和可移植性。
word_index.erl文件的總體結構如下:
- -module(word_index).
- -export([start/2]).
- -import(re, [run/2,replace/4]).
- -import(string,[substr/3]).
- %% start兩個參數:FileIn表示要建立索引的文本文件,FileOut表示索引保存的目標文件start(FileIn,FileOut) ->
- {_First,Second} = file:open(FileIn,read),%% 只讀打開FileIn文件
- if
- _First =:= ok ->
- LineList = readFile(Second,0),%% 函數readFile/2的功能是將文本以行為單位,存入列表
- %io:format("~nfile contents:~p~n",[LineList]),
- TableID = ets:new(index,[ordered_set]),%% ets:new創建一個“鍵值”搜索表,
- 存儲鍵值映射元祖,設置表名為index,表的類型為ordered_set
- index(FileOut,LineList,TableID);%% 為文本中的每一行建立單詞索引
- _First =/= ok ->
- io:format("Open file error: file doesn't exist!")
- end.
readFile/2函數代碼如下:
- %% 讀取文本每一行,以{Line,LineNo}為元組存入列表中readFile(S, LineNo) -> readFile(S,LineNo,[]).
- readFile(S, LineNo, Ret) ->
- UpdateLineNo = LineNo +1,
- OneLine = io:get_line(S,''),%% 讀取文件中的一行內容 if
- OneLine =:= eof ->
- io:format("Read file EOF!"),
- file:close(S),
- lists:reverse(Ret);
- OneLine =/= eof ->
- readFile(S,UpdateLineNo, [{OneLine,UpdateLineNo} | Ret])
- end.
index/3函數代碼如下:
- index(File,LineList,TableID) ->
- if
- length(LineList) =:= 0 ->
- ToList = ets:tab2list(TableID),
- io:format("index is:~n~p~n",[ToList]),
- writeToFile(File,ToList),
- io:format("create index success! ");
- length(LineList) =/= 0 ->
- First = lists:nth(1,LineList),
- processOneLine(First,TableID),
- index(File,lists:delete(First, LineList), TableID)
- end.
- %% 處理一行文本processOneLine(OneLine, TableID) ->
- {Element, LineNo } = OneLine,
- %io:format("Line no:~p~n",[LineNo]),
- Words = string:tokens(Element,"\n\t "),
- matchWords(Words,LineNo,TableID).
- matchWords([], LineNo, TableID) ->
- io:format("process line(~p) success!~n",[LineNo]);
- matchWords(Words, LineNo, TableID) ->
- %io:format("Words:~p~n",[Words]),
- Word = lists:nth(1,Words),
- _Value = ets:lookup(TableID,Word),%%返回值為匹配Word的元組列表 if
- length(_Value) =:= 0 -> %% Word還未被索引,直接插入此Word索引 ets:insert(TableID,{Word,[{LineNo,1}]} );
- length(_Value) =/= 0 -> %% Word已被索引,更新Word索引列表 KVs = lists:nth(1,_Value),
- Value = element(2,KVs),
- ets:insert(TableID,{Word, insertRec(Value,LineNo) } )
- end,
- matchWords(lists:delete(Word, Words), LineNo, TableID).
- %% 處理行號與出現次數元組列表insertRec(List,LineNo) -> insertRec(List,LineNo,length(List)).
- insertRec(List, LineNo, 0) ->
- [{LineNo, 1} |List];
- insertRec(List, LineNo, Ret) ->
- First = lists:nth(Ret,List),
- {LN, Num} = First,
- if
- LN =:= LineNo ->
- Temp = lists:delete(First, List),
- [{LineNo, Num+1} | Temp];
- LN =/= LineNo ->
- insertRec(List, LineNo, Ret-1)
- end.
- %% 將索引寫入文件writeToFile(File,ToList) ->
- {ok,S} = file:open(File,write),
- lists:foreach(fun(X) -> io:format(S,"~p.~n",[X]) end, ToList),
- file:close(S).
至此,我已經將使用ets存儲鍵值大型表來存儲單詞索引列表的程序講完了,大家自己回去動手試驗吧。本文是繼續上文的一個續篇,是一種改進的建立文本索引方式。以后我還好繼續通過實戰練習來探討Erlang的學習與總結思考,謝謝大家的關注。
原文:http://www.cnblogs.com/itfreer/archive/2012/05/07/Erlang_in_practise_index.html
【編輯推薦】