Files
fc/server/tools/gen_battle/core/util.erl
T
2026-05-23 22:10:14 +08:00

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>如 [&lt;&lt;"字节"&gt;&gt;, 123, asd, "assd"] 输出 &lt;&lt;"字节123asdassd"&gt;&gt;</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.