224 lines
8.6 KiB
Erlang
224 lines
8.6 KiB
Erlang
|
|
%%----------------------------------------------------
|
||
|
|
%% 工具包
|
||
|
|
%%
|
||
|
|
%% @author yeahoo2000@gmail.com
|
||
|
|
%% @end
|
||
|
|
%%----------------------------------------------------
|
||
|
|
-module(util).
|
||
|
|
-export(
|
||
|
|
[
|
||
|
|
template/2
|
||
|
|
,replace/3
|
||
|
|
,template_replace/2
|
||
|
|
,to_string/1
|
||
|
|
,to_list/1
|
||
|
|
,term_to_string/1
|
||
|
|
,string_to_term/1
|
||
|
|
,term_to_bitstring/1
|
||
|
|
,all_to_binary/1
|
||
|
|
,to_binary/1
|
||
|
|
,datetime_to_seconds/1
|
||
|
|
,rm_duplicates/2
|
||
|
|
,multi_exec/3
|
||
|
|
,multi_exec/4
|
||
|
|
,sync_multi_exec/3
|
||
|
|
,sync_multi_exec/4
|
||
|
|
,is_exists_fun/2
|
||
|
|
]
|
||
|
|
).
|
||
|
|
-include("common.hrl").
|
||
|
|
|
||
|
|
%% 判断模块是否存在指定方法
|
||
|
|
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 将日期转换unix时间戳
|
||
|
|
-spec datetime_to_seconds(DateTime) -> false | Seconds when
|
||
|
|
DateTime :: {{Y, M, D}, {H, M, S}},
|
||
|
|
Y :: pos_integer(),
|
||
|
|
M :: 1..12,
|
||
|
|
D :: 1..31,
|
||
|
|
H :: 0..23,
|
||
|
|
M :: 0..59,
|
||
|
|
S :: 0..59,
|
||
|
|
Seconds :: pos_integer().
|
||
|
|
datetime_to_seconds({Year, Month, Day, Hour, Minute, Second}) ->
|
||
|
|
datetime_to_seconds({{Year, Month, Day}, {Hour, Minute, Second}});
|
||
|
|
datetime_to_seconds(DateTime) ->
|
||
|
|
case calendar:local_time_to_universal_time_dst(DateTime) of
|
||
|
|
[] -> false;
|
||
|
|
[_, Udate] ->
|
||
|
|
calendar:datetime_to_gregorian_seconds(Udate) - 719528 * 24 * 3600;
|
||
|
|
[Udate] ->
|
||
|
|
calendar:datetime_to_gregorian_seconds(Udate) - 719528 * 24 * 3600
|
||
|
|
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(unicode:characters_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 将任意类型的数据转成string()类型
|
||
|
|
-spec to_string(any()) -> string().
|
||
|
|
to_string(X) -> lists:flatten(io_lib:format("~w", [X])).
|
||
|
|
|
||
|
|
%% @doc 将任意类型的数据转成list()类型(主要用于控制台打印).
|
||
|
|
%% <div>注意:tuple类型有特殊处理</div>
|
||
|
|
-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) -> to_string(X);
|
||
|
|
to_list(X) when is_list(X) -> X.
|
||
|
|
|
||
|
|
%% @doc term序列化,term转换为string格式
|
||
|
|
-spec term_to_string(term()) -> string().
|
||
|
|
term_to_string(Term) -> io_lib:format("~w", [Term]).
|
||
|
|
|
||
|
|
%% @doc term序列化,term转换为bitstring
|
||
|
|
-spec term_to_bitstring(term()) -> bitstring().
|
||
|
|
term_to_bitstring(Term) -> list_to_bitstring(term_to_string(Term)).
|
||
|
|
|
||
|
|
%% @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 将列里的不同类型转行成字节型
|
||
|
|
%% @todo 貌似不够高效,可以优化下
|
||
|
|
%% <div>如 [<<"字节">>, 123, asd, "assd"] 输出 <<"字节123asdassd">></div>
|
||
|
|
-spec all_to_binary(list()) -> binary().
|
||
|
|
all_to_binary(List) -> all_to_binary(List, []).
|
||
|
|
all_to_binary([], Result) -> list_to_binary(Result);
|
||
|
|
all_to_binary([P | T], Result) when is_list(P) ->
|
||
|
|
all_to_binary(T, lists:append(Result, P));
|
||
|
|
all_to_binary([P | T], Result) when is_integer(P) ->
|
||
|
|
all_to_binary(T, lists:append(Result, integer_to_list(P)));
|
||
|
|
all_to_binary([P | T], Result) when is_binary(P) ->
|
||
|
|
all_to_binary(T, lists:append(Result, binary_to_list(P)));
|
||
|
|
all_to_binary([P | T], Result) when is_float(P) ->
|
||
|
|
all_to_binary(T, lists:append(Result, float_to_list(P)));
|
||
|
|
all_to_binary([P | T], Result) when is_atom(P) ->
|
||
|
|
all_to_binary(T, lists:append(Result, atom_to_list(P))).
|
||
|
|
|
||
|
|
%% @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
|
||
|
|
%% 去除列表中的重复项
|
||
|
|
%% @end
|
||
|
|
%%--------------------------------------------------------------------
|
||
|
|
-spec rm_duplicates(list(), Acc :: list())-> Acc :: list().
|
||
|
|
rm_duplicates([], L) -> L;
|
||
|
|
rm_duplicates([[S] | T], L) ->
|
||
|
|
case lists:member(S, L) of
|
||
|
|
true -> rm_duplicates(T, L);
|
||
|
|
false -> rm_duplicates(T, [S | L])
|
||
|
|
end;
|
||
|
|
rm_duplicates([S | T], L) ->
|
||
|
|
case lists:member(S, L) of
|
||
|
|
true -> rm_duplicates(T, L);
|
||
|
|
false -> rm_duplicates(T, [S | L])
|
||
|
|
end.
|