%%---------------------------------------------------- % 通常工具包 %% @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()类型(主要用于控制台打印). %%
注意:tuple类型有特殊处理
-spec to_list(any()) -> list(). to_list(X) when is_integer(X) -> integer_to_list(X); to_list(X) when is_float(X) -> float_to_list(X); to_list(X) when is_atom(X) -> atom_to_list(X); to_list(X) when is_binary(X) -> binary_to_list(X); to_list(X) when is_pid(X) -> pid_to_list(X); to_list(X) when is_function(X) -> erlang:fun_to_list(X); to_list(X) when is_port(X) -> erlang:port_to_list(X); to_list(X) when is_tuple(X) -> do_tuple(tuple_to_list(X), []); to_list(X) when is_list(X) -> X. do_tuple([], L) -> ["{" | lists:reverse(["}" | L])]; do_tuple([T], L) -> do_tuple([], [to_list(T) | L]); do_tuple([H | T], L) -> S = to_list(H), S1 = [S | ", "], do_tuple(T, [S1 | L]). %% 转换成数值 to_integer(V) when is_list(V) -> list_to_integer(V); to_integer(V) when is_binary(V) -> list_to_integer(binary_to_list(V)); to_integer(V) when is_atom(V) -> list_to_integer(atom_to_list(V)); to_integer(V) when is_integer(V) -> V. %% @doc term反序列化,string转换为term -spec string_to_term(String) -> {error, Reason} | {ok, term()} when String :: undefined | string() | bitstring(), Reason :: term(). string_to_term(undefined) -> {ok, undefined}; string_to_term("undefined") -> {ok, undefined}; string_to_term(String) when is_bitstring(String) -> string_to_term(binary_to_list(String)); string_to_term(String) -> case erl_scan:string(String ++ ".") of {ok, Tokens, _} -> erl_parse:parse_term(Tokens); {error, Err, _} -> {error, Err} end. %% @doc term序列化,term转换为string格式 -spec term_to_string(term()) -> string(). term_to_string(Term) -> io_lib:format("~w", [Term]). %% @doc 将Val值转换为binary格式(8位二进制) %% @todo 貌似没有什么用,考虑删除掉 -spec to_binary(integer()) -> binary(). to_binary(Val) when is_integer(Val) -> list_to_binary(integer_to_list(Val)); to_binary(Val) when is_float(Val) -> list_to_binary(float_to_list(Val)); to_binary(Val) when is_list(Val) -> list_to_binary(Val); to_binary(Val) when is_binary(Val) -> Val; to_binary(_Val) -> <<>>. %% @doc 建立指定目录 -spec make_dir(string()) -> atom(). make_dir(Dir) -> case filelib:is_dir(Dir) of false -> file:make_dir(Dir); _ -> skip end. %%-------------------------------------------------------------------- %% @doc %% 彩色输出 %% @end %%-------------------------------------------------------------------- -spec printf(T :: atom(), F :: string(), A :: list())-> ok. printf(Tag, F, A) when is_list(F) -> {NewF, NewA} = case string:to_lower(erlang:system_info(otp_release)) of R when R > "r16" orelse R > "16" -> case Tag of info -> %% 绿色 {"~s" ++ F ++ "~s", [?GREEN] ++ A ++ [?DEF_COLOR]}; error -> %% 红色 {"~s" ++ F ++ "~s", [?RED] ++ A ++ [?DEF_COLOR]}; warning -> %% 黄色 {"~s" ++ F ++ "~s", [?YELLOW] ++ A ++ [?DEF_COLOR]}; purple -> %% 紫色 {"~s" ++ F ++ "~s", [?PURPLE] ++ A ++ [?DEF_COLOR]} end; _ -> {F, A} end, ?P(NewF, NewA); printf(_, F, A) -> ?P(F, A).