%%---------------------------------------------------- % 通常工具包 %% @author whjing2011@gmail.com %%---------------------------------------------------- -module(util). -export([ is_exists_fun/2 ,multi_exec/3 ,multi_exec/4 ,sync_multi_exec/3 ,sync_multi_exec/4 ,template/2 ,template_replace/2 ,batch_replace/2 ,replace/3 ,to_string/1 ,to_list/1 ,to_binary/1 ,to_integer/1 ,string_to_term/1 ,term_to_string/1 ,make_dir/1 ,printf/3 ]). -include("common.hrl"). % @doc 判断模块是否存在指定方法 -spec is_exists_fun(atom(), tuple()) -> true | false. is_exists_fun(Mod, Fun) -> {_, FunList} = lists:keyfind(exports, 1, Mod:module_info()), [Fun1 || Fun1 <- FunList, Fun1 =:= Fun] =/= []. %% @doc 批量处理 不保证顺序 -spec multi_exec(list(), Fun::function(), ProcNum::pos_integer()) -> [Ret::any()]. multi_exec(List, Fun, ProcNum) -> multi_exec(List, Fun, ProcNum, undefined). multi_exec(List, Fun, ProcNum, RetFun) -> multi_exec(0, ProcNum, 1, List, Fun, RetFun, []). multi_exec(0, _ProcNum, _Num, [], _Fun, _RetFun, RetList) -> RetList; multi_exec(N, 1, Num, [I | List], Fun, RetFun, RetList) -> Ret = Fun(Num, I), multi_exec(N, 1, Num + 1, List, Fun, RetFun, do_exce_ret(Ret, RetFun, RetList)); multi_exec(N, ProcNum, Num, [I | List], Fun, RetFun, RetList) when N < ProcNum -> Pid = self(), spawn(fun() -> Pid ! {self(), Pid, catch Fun(Num, I)} end), multi_exec(N + 1, ProcNum, Num + 1, List, Fun, RetFun, RetList); multi_exec(N, ProcNum, Num, List, Fun, RetFun, RetList) -> Pid = self(), receive {_FromPid, Pid, Ret} -> multi_exec(N - 1, ProcNum, Num, List, Fun, RetFun, do_exce_ret(Ret, RetFun, RetList)) end. %% @doc 批量处理 保证顺序 -spec sync_multi_exec(list(), Fun::function(), ProcNum::pos_integer()) -> [Ret::any()]. sync_multi_exec(List, Fun, ProcNum) -> sync_multi_exec(List, Fun, ProcNum, undefined). sync_multi_exec(List, Fun, ProcNum, RetFun) -> sync_multi_exec(0, queue:new(), ProcNum, 1, List, Fun, RetFun, []). sync_multi_exec(0, _Queue, _ProcNum, _Num, [], _Fun, _RetFun, RetList) -> RetList; sync_multi_exec(N, Queue, 1, Num, [I | List], Fun, RetFun, RetList) -> Ret = Fun(Num, I), sync_multi_exec(N, Queue, 1, Num + 1, List, Fun, RetFun, do_exce_ret(Ret, RetFun, RetList)); sync_multi_exec(N, Queue, ProcNum, Num, [I | List], Fun, RetFun, RetList) when N < ProcNum -> Pid = self(), P = spawn(fun() -> Pid ! {self(), Pid, catch Fun(Num, I)} end), sync_multi_exec(N + 1, queue:in(P, Queue), ProcNum, Num + 1, List, Fun, RetFun, RetList); sync_multi_exec(N, Queue, ProcNum, Num, List, Fun, RetFun, RetList) -> Pid = self(), {{value, FromPid}, NewQueue} = queue:out(Queue), receive {FromPid, Pid, Ret} -> %% 先执行的先接收 sync_multi_exec(N - 1, NewQueue, ProcNum, Num, List, Fun, RetFun, do_exce_ret(Ret, RetFun, RetList)) end. do_exce_ret(false, _RetFun, RetList) -> RetList; do_exce_ret(error, _RetFun, RetList) -> RetList; do_exce_ret(skip, _RetFun, RetList) -> RetList; do_exce_ret(Ret, undefined, RetList) -> [Ret | RetList]; do_exce_ret(Ret, RetFun, RetList) -> case RetFun(Ret) of false -> RetList; {ok, NewRet} -> [NewRet | RetList]; _ -> [Ret | RetList] end. %% @doc 替换模板变量 -spec template(string(), [{atom(), string()}]) -> string() | {error, term()}. template(File, Vars) -> case file:read_file(File) of {error, Reason} -> {error, Reason}; {ok, Content} -> V = [{lists:concat(["{{", K, "}}"]), to_list(V)} || {K, V} <- Vars], template_replace(bitstring_to_list(Content), V) end. template_replace(Text, []) -> Text; template_replace(Text, [{K, V} | L]) -> %% 用re:replace性能差到不能看... %% T = re:replace(Text, K, V, [caseless, global]), T = replace(Text, K, V), template_replace(T, L). %% @doc 替换字符串 %% @todo 有必要再优化下性能 -spec replace(string(), string(), string()) -> string(). replace([], _Search, _Replace) -> ""; replace(Str, Search, Replace) -> replace(Str, Search, Replace, length(Search), []). replace(Str, Search, Replace, Len, Rtn) -> case string:str(Str, Search) of 0 -> Rtn ++ Str; P -> S = string:substr(Str, 1, P - 1) ++ Replace, replace(string:substr(Str, P + Len), Search, Replace, Len, Rtn ++ S) end. %% @doc 大批量替换字符串(优先替换最长表达式) -spec batch_replace(string(), list()) -> string(). batch_replace(Text, []) -> Text; batch_replace(Text, Vars) -> KeyM = init_batch_replace_index(Vars, #{}), do_batch_replace(Text, [], KeyM). do_batch_replace([], Body, _KeyM) -> lists:reverse(lists:flatten(Body)); do_batch_replace(S = [I | T], Body, KeyM) -> case do_batch_replace_find(S, [], KeyM, []) of {true, {Rest, S1}} -> S2 = lists:reverse(maps:get({s,lists:reverse(S1)}, KeyM)), do_batch_replace(Rest, [S2 | Body], KeyM); false -> do_batch_replace(T, [I | Body], KeyM) end. do_batch_replace_find([], _Head, _KeyM, []) -> false; do_batch_replace_find([], _Head, _KeyM, [Buff | _]) -> {true, Buff}; do_batch_replace_find([I | T], Head, KeyM, Buffs) -> NewHead = [I | Head], case maps:get(NewHead, KeyM, undefined) of continue -> do_batch_replace_find(T, NewHead, KeyM, Buffs); over -> {true, {T, NewHead}}; include -> do_batch_replace_find(T, NewHead, KeyM, [{T, NewHead}]); _ -> do_batch_replace_find([], [], KeyM, Buffs) end. init_batch_replace_index([], M) -> M; init_batch_replace_index([{S1,S2} | T], M) -> M1 = maps:put({s,S1}, S2, M), NewM = do_init_batch_replace_index(S1, [], M1), init_batch_replace_index(T, NewM). do_init_batch_replace_index([], _Head, M) -> M; do_init_batch_replace_index([I], Head, M) -> NewHead = [I | Head], case maps:get(NewHead, M, undefined) of continue -> maps:put(NewHead, include, M); over -> M; include -> M; _ -> maps:put(NewHead, over, M) end; do_init_batch_replace_index([I | T], Head, M) -> NewHead = [I | Head], case maps:get(NewHead, M, undefined) of include -> do_init_batch_replace_index(T, NewHead, M); over -> NewM = maps:put(NewHead, include, M), do_init_batch_replace_index(T, NewHead, NewM); continue -> do_init_batch_replace_index(T, NewHead, M); _ -> NewM = maps:put(NewHead, continue, M), do_init_batch_replace_index(T, NewHead, NewM) end. %% @doc 将任意类型的数据转成string()类型 -spec to_string(any()) -> string(). to_string(X) -> lists:flatten(io_lib:format("~w", [X])). %% @doc 将任意类型的数据转成list()类型(主要用于控制台打印). %%