I have credit card number, let's say 5940043543536. And for security purposes I only want to display the first four digits.
How would one do that in erlang?
A string in Erlang is just a list of integers, so you can use lists:sublist/3:
1> String = "5940043543536".
"5940043543536"
2> lists:sublist(String, 1, 4).
"5940"
Note that the position argument starts from 1 and not 0.
In case you are receiving binary (instead of string)
binary:part(<<"123455678901234">>, 1, 4).
<<"2345">>
or if you need get last four digits
binary:part(<<"123455678901234">>, {byte_size(<<"123455678901234">>), -4}).
<<"1234">>
newer versions of Erlang have built in string functions. For your case
1> string:slice("123455678901234", 1, 4).
"1234"
there is a string:substring function too, which works the same way, but it has been depreciated for slice.
You can try use pattern matching:
1> String = "5940043543536".
"5940043543536"
2> [A,B,C,D|_] = String.
"5940043543536"
3> [A,B,C,D].
"5940"
Or you can create your own function, eg:
1> String = "5940043543536".
"5940043543536"
2> GetDigits = fun F(_, Acc, 0) -> lists:reverse(Acc);
F([H|T], Acc, N) -> F(T, [H|Acc], N - 1) end.
#Fun<erl_eval.43.91303403>
3> GetDigits(String, [], 4).
"5940"
Related
I'm running into a problem when writing some simple erlang code for an old Advent of Code task.
The following program is supposed to read lines, group characters in a string by occurrence and then count the number of lines that have a repeat of three characters.
count_occurrences([], Map) -> Map;
count_occurrences([H | T], Map) ->
count_occurrences(T, maps:put(H, maps:get(H, Map, 0) + 1, Map)).
count(Line, Count) ->
Map = count_occurrences(Line, #{}),
case lists:member(3, maps:values(Map)) of
true -> Count + 1;
false -> Count
end.
run() ->
{ok, Binary} = file:read_file("data.txt"),
Lines = binary:split(Binary, <<"\n">>, [global]),
Result = lists:foldl(fun count/2, 0, Lines),
Result.
However, I get this error message:
10> c(day2).
{ok,day2}
11> day2:run().
** exception error: no function clause matching day2:count_occurrences(<<"bpacnmelhhzpygfsjoxtvkwuor">>,#{}) (day2.erl, line 5)
in function day2:count/2 (day2.erl, line 10)
in call from lists:foldl/3 (lists.erl, line 1263)
I don't understand why <<"bpacnmelhhzpygfsjoxtvkwuor">>,#{} doesn't match the second "count_occurrences" function clause - a string is the same as a list, right? Why doesn't it match [H | T]?
Check out this example:
-module(a).
-compile(export_all).
go([_H|_T], _X) ->
"First arg was a list";
go("a", _X) ->
"First arg was a string";
go(<<"a">>, _X) ->
"First arg was a binary".
In the shell:
5> a:go(<<"a">>, #{a=>1, b=>2}).
"First arg was a binary"
and:
6> a:go("a", #{a=>1, b=>2}).
"First arg was a list"
a string is the same as a list, right?
Yes, a double quoted string is a shortcut for creating a list of integers where the integers in the list are the ascii codes of the characters. Hence, the second function clause above will never match:
a.erl:6: Warning: this clause cannot match because a previous clause
at line 4 always matches
But....a binary, such as <<"abc">> is NOT a string, and therefore a binary is not a shortcut for creating a list of integers.
8> "a" =:= [97].
true
Okay, you knew that. But, now:
9> "a" =:= <<"a">>.
false
10> <<"a">> =:= <<97>>.
true
11> "a" =:= <<97>>.
false
And, finally:
13> <<"abc">> =:= <<97, 98, 99>>.
true
The last example shows that specifying a double quoted string inside a binary is just a shortcut for specifying a comma separated list of integers inside a binary--however specifying a double quoted string inside a binary does not somehow convert the binary to a list.
Note that you can also iterate through a binary with only slightly different syntax:
count_occurrences(<<>>, Map) -> Map;
count_occurrences(<<H, T/binary>>, Map) ->
count_occurrences(T, maps:put(H, maps:get(H, Map, 0) + 1, Map)).
By default, H is assumed to be a byte, but you can add modifiers to specify how many bits you want to select, and more. See the documentation for the Bit Syntax.
You get this error cuz function count_occurrences/2 expect first argument list - [<<"bpacnmelhhzpygfsjoxtvkwuor">>] or "bpacnmelhhzpygfsjoxtvkwuor" but was put binary - <<"bpacnmelhhzpygfsjoxtvkwuor">>. Double check input data Line in function count/2 of module day2.erl at line 10:
1> is_list([]).
true
2> is_list("").
true
3> is_list(<<"">>).
false
4> is_list(binary_to_list(<<"">>)).
true
I have a list like List = [{0,12},{0,12},{-1,0},{0,12},{0,4},{1,2}] and a string Str = "https://www.youtube.com/watch?v=WQfdwsPao9E", now I've to find all the substrings using start and end point from list.
I want substrings to be returned in a List like ["https://www","https://www",..]
I tried using this:
C=lists:map(fun({X,Y}) -> string:sub_string(Str,X,Y) end,List)
1> List = [{0,12},{0,12},{-1,0},{0,12},{0,4},{1,2}].
[{0,12},{0,12},{-1,0},{0,12},{0,4},{1,2}]
2> Str = "https://www.youtube.com/watch?v=WQfdwsPao9E".
"https://www.youtube.com/watch?v=WQfdwsPao9E"
3> Len = length(Str).
43
4> [string:sub_string(Str,max(1,X),min(Len,Y)) || {X,Y} <- List].
["https://www.","https://www.",[],"https://www.","http",
"ht"]
5>
you may have to adjust the indexes in the string to fit exactly to your need.
[edit] It looks like I didn't interpret correctly what is the meaning of the tuple. I think it is {Fist_Char_Index, Char_Number}, or {-1,0} if no match is found. So you should use:
[string:sub_string(Str,X+1,X+Y) || {X,Y} <- List, {X,Y} =/= {-1,0}].
I have a problem, I don't know why the program doesn't work correctly.
When I run the program and I insert atom c, then the program calls function io:read() forever.
Thank you for your help and I'm sorry for my English.
-module(temperature).
-export([run/0, convert/2]).
run() ->
run(true).
run(true) ->
{ok, Choice} = io:read("Convert to degrees Celsius or convert to degrees Fahrenheit? c/f :"),
{ok, Temp} = io:read("Insert temperature: "),
{UnitTemp, Convert} = convert(Choice, Temp),
io:format("The converted temperature: ~f ~s\n", [Convert, UnitTemp]),
{ok, Continue} = io:read("New temperature? true/false :"),
run(Continue);
run(false) ->
ok.
convert(c, Fahrenheit) -> {'Celsius', 5 * (Fahrenheit - 32) / 9};
convert(f, Celsius) -> {'Fahrenheit', 9 * Celsius / 5 + 32}.
io:read reads a term, so it does not stop, until you finish your term with ..
1> io:read("Enter term: ").
Enter term: {foo, bar}.
{ok,{foo,bar}}
2> io:read("This will give error: ").
This will give error: }foo
This will give error: .
{error,{1,erl_parse,["syntax error before: ","'}'"]}}
So you can simply type c..
Alternatively, you can use io:get_chars/2 if you don't want to type the dot. The first argument is prompt and second one is number of chars to read, so in your case, it will be:
io:get_chars("prompt ", 1).
prompt c
"c"
Remember, that after typing c, you still have to hit enter and now, you should pattern match on string "c" instead of atom c.
How to convert this string format "{hari, localost}" into this: {"hari", "localost"}, in Erlang?
I tried to convert this format with lot of trial and error method, but I can't get the solution.
I guess you need to convert from string, so you can use the modules erl_scan and erl_parse:
1> erl_scan:string("{hari, localost}"++".").
{ok,[{'{',1},
{atom,1,hari},
{',',1},
{atom,1,localost},
{'}',1},
{dot,1}],
1}
2> {ok,Term} = erl_parse:parse_term(Tokens).
{ok,{hari,localost}}
3>Conv = fun({X, Y}) -> {atom_to_list(X), atom_to_list(Y)} end.
#Fun<erl_eval.6.80484245>
4> Conv(Term).
{"hari","localost"}
5>
Note 1 the function erl_parse:parse_term/1 will work only if Terms is a valid expression, it is why I had to add a "." at the end of the input.
Note 2 yo can directly transform to the final expression if you quote the terms in the input expression:
1> {ok,Tokens,_} = erl_scan:string("{\"hari\", \"localost\"}.").
{ok,[{'{',1},
{string,1,"hari"},
{',',1},
{string,1,"localost"},
{'}',1},
{dot,1}],
1}
2> {ok,Term} = erl_parse:parse_term(Tokens).
{ok,{"hari","localost"}}
3>
I have this variable Code in erlang which has this value "T00059"
I want to extract this value 59 from Code.
I try to extract with this code this value "00059".
NewCode = string:substr(Code, 2, length(Code)),
Now I want to know how can we eliminate the first zero before the first integer not null. I mean how can we extract "59"?
For example if I have this value "Z00887" I should have in the final this value 887.
You can simply do (output from an interactive erlsession):
1> Code = "Z00887",
1> {NewCode, _Rest} = string:to_integer(string:substr(Code, 2, length(Code))),
1> NewCode.
887
(My answer in test with loop in erlang goes into more detail regarding the same problem)
This code will skip starting zeros. If you want to save them change $1 to $0
extract_integer([]) -> [];
extract_integer([H|T]) when (H >= $1) and (H =< $9) -> [H] ++ T;
extract_integer([_H|T]) -> extract_integer(T).