Juliano Costa
Juliano Costa

Reputation: 2733

Reading files in Erlang

I have an Erlang app that fetches some key/value pair from files.
In one of the files, instead of the key/value pairs, I have a path to a different folder, in which it has the key/value pairs.

The reading of the files are working fine, but whenever I have to read the path from a file and just then read the subsequent file, the app is complaining that the file is nonexistent.

If I jump into the container and check, I can see the file and the subsequent file. So I guess I'm missing something.

Here is what I have:

get_kvs("keyvalue.properties"), %% <-- This works

{ok, PathToFile} = file:read_file("pathtofile.properties"),
get_kvs(PathToFile), %% <-- This crashes

Files:

key_1=val_1
key_2=val_2
key_3=val_3
/data/myfolder/hidden_keyvalue.properties
extra_key1=extra_val1
extra_key2=extra_val2
extra_key3=extra_val3

And the get_metadata function:

get_metadata(FileName) ->
    io:format(FileName),
    {ok, MetadataFile} = file:read_file(FileName),
    io:format(MetadataFile),
    Lines = binary:split(MetadataFile, <<"\n">>, [trim, global]),
    make_tuples(Lines, []).

make_tuples([Line|Lines], Acc) ->
    [Key, Value] = binary:split(Line, <<"=">>),
    make_tuples(Lines, [{Key, Value}|Acc]);
make_tuples([], Acc) -> lists:reverse(Acc).

Whenever running it I can see that the PathToFile is being properly populated, but when I try to read the path I get the error below:

keyvalue.propertiesextra_key_1=extra_val_1
extra_key_2=extra_val_2
extra_key_3=extra_val_3
/data/myfolder/hidden_keyvalue.properties
=CRASH REPORT==== 23-Mar-2022::07:46:30.612093 ===
  crasher:
    initial call: cowboy_stream_h:request_process/3
    pid: <0.735.0>
    registered_name: []
    exception error: no match of right hand side value {error,enoent}
      in function  hello_handler:get_metadata/1 (/data/apps/hello_server/src/hello_handler.erl, line 40)
      in call from hello_handler:child_call/1 (/data/apps/hello_server/src/hello_handler.erl, line 28)

Any ideas of what am I missing?

Upvotes: 1

Views: 247

Answers (1)

Nalin Ranjan
Nalin Ranjan

Reputation: 1782

After the OP has been modified to reflect the actual point of failure by removing try-catch, the error seems to be {error, enoent }which means The file does not exist. However, the same function is working in some scenarios and not when the path of the file to be read is itself taken from another file.

Just make sure there are mo additional characters, like newlines or non-printable characters after the content of the file which should actually be a valid path.

For example, when I tried with a value as such, <<"hidden_keyvalue.properties\n\n">>, then read_file gave me same result as {error, enoent}.

So it could be possible that the content of the file from which paths are read has additional non-printable characters at the end.


Ignore (Wrong Assumption)

I tried with a local setup and I think this line inside make_tuples is causing that behavior.

[Key, Value] = binary:split(Line, <<"=">>) %% <--- This match will succeed only if there are exactly 2 elements in the list produced by binary:split(...).

Given we are doing inside get_metadata

Lines = binary:split(MetadataFile, <<"\n">>, [trim, global]), %% <-- This splits the contents of the keyvalue.properties file into a List
make_tuples(Lines, [])

From the provided text, it appears that the content of keyvalue.proiperties file is ...

key_1=val_1
key_2=val_2
key_3=val_3
/data/myfolder/hidden_keyvalue.properties %% <-- This isn't a valid key-value pair

And with that last line, make_tuples while doing a match will fail the following way...

[Key, Value] = binary:split(<<"/data/myfolder/hidden_keyvalue.properties">>, <<"=">>).
** exception error: no match of right hand side value [<<"/data/myfolder/hidden_keyvalue.properties">>]

That pattern-match expression requires exactly 2 elements on the right-hand side(the Term/Value side). Since the line with path-entry in the keyvalue.properties does not have an =, so the split produces a list with 1 element, and so the match fails.

To address this...

  1. We can change the format of keyvalue.properties so that every line is a valid key-value pair, but not sure how feasible it will be in the context of the program.
  2. Or, we can change that pattern-match form of list so that it can accept non-exact number of terms while matching
make_tuples([Line|Lines], Acc) ->
    %% [Key|Value] is at least 1 element list, [Key, Value] is exactly 2 elements list
    [Key|Value] = binary:split(Line, <<"=">>), %% <-- Now Value becomes a list of binary
    make_tuples(Lines, [{Key, get_value(Value)}|Acc]); %% <-- Additional get_value function to extract 'Value binary' from the head of the 'Value List' produced in the previous line
make_tuples([], Acc) -> lists:reverse(Acc).

get_value([]) ->
    <<>>;
get_value([V|_]) -> V. 

Assumptions

get_kvs invokes get_metadata which in turn invokes make_tuples.

keyvalue.properties file content has both valid key-value pairs and also some non-key-value entries

Upvotes: 2

Related Questions