以下內容涵蓋一些常用的 file io 函數,內容包含了(1) 函式庫的組織 (2) 讀取檔案 (3) 寫入檔案 (4) 目錄操作 (5) 取得檔案資訊 (6) 字元編碼
函式庫的組織
檔案處理的函數分散在四個模組
- file
包含開啟、關閉、讀取、寫入檔案、列出目錄的函數 - filename
可以用 platform independent 的方式處理檔名,所以相同的程式碼可在不同作業系統上運作 - filelib
是 file 的擴充,可用來列出檔案,檢查檔案型別。這些函數大多是利用 file 裡面的函數寫出來的。 - io
用來處理開啟的檔案、剖析資料、寫入格式化的資料
file 模組檔案操作的函數
函數 | 說明 |
---|---|
change_group | 改變檔案的群組 |
change_owner | 改變檔案的owner |
change_time | 改變修改時間或上次取用的時間 |
close | 關閉檔案 |
consult | 從檔案讀取 erlang term |
copy | 複製檔案內容 |
del_dir | 刪除目錄 |
delete | 刪除檔案 |
eval | 估算檔案內的 erlang expressions |
format_error | 根據錯誤原因,傳回其說明字串 |
get_cwd | 目前的工作目錄 |
list_dir | 取得目錄內的檔案清單 |
make_dir | 建立新目錄 |
make_link | 建立 hard link |
make_symlink | 建立 symbolic link |
open | 開啟檔案 |
position | 設定檔案內的位置 |
pread | 讀取檔案的特定位置 |
pwrite | 寫入檔案的特定位置 |
read | 讀取檔案 |
read_file | 讀取整個檔案 |
read_file_info | 取得檔案資訊 |
read_link | 查詢 link 指向何處 |
read_link_info | 取得 link 或檔案的資訊 |
rename | 改檔名 |
script | 估算並傳回檔案內的 erlang expressions 的值 |
set_cwd | 設定工作目錄 |
sync | 以檔案的實體媒介,同步化一個檔案的記憶體內狀態 |
truncate | 截斷一個檔案 |
write | 寫入檔案 |
write_file | 寫入整個檔案 |
write_file_info | 改變檔案的資訊 |
讀取檔案
以下以 data1.dat 檔案為例,採用不同的方式讀取檔案
data1.dat 內容
{person, "john", "chen",
[{occupation, programmer}, {favorite, coding}]}.
{cat, {name, "money"}, {owner, "john"}}.
file:consult
讀取檔案內所有 erlang terms
1> file:consult("data1.dat").
{ok,[{person,"john","chen",
[{occupation,programmer},{favorite,coding}]},
{cat,{name,"money"},{owner,"john"}}]}
file:open, io:read
先用 file:open 開啟檔案,然後用 io:read 一次讀取一個 erlang term,最後用 file:close 關閉檔案。
4> {ok, S} = file:open("data1.dat", read).
{ok,<0.38.0>}
5> io:read(S, '').
{ok,{person,"john","chen",
[{occupation,programmer},{favorite,coding}]}}
6> io:read(S, '').
{ok,{cat,{name,"money"},{owner,"john"}}}
7> io:read(S, '').
eof
8> file:close(S).
ok
@spec file:open(File, read) -> {ok, IoDevice} | {error, Why}
@spec io:read(IoDevice, Prompt) -> {ok, Term} | {error, Why} | eof
只有在 io:read 讀取 stdin 的時候,才會用到 Prompt(提示符號)
@spec file:close(IoDevice) -> ok | {error, Why}
使用這些函數,就可以實做出 file:consult
consult(File) ->
case file:open(File, read) of
{ok, S} ->
Val = consult1(S),
file:close(S),
{ok, Val};
{error, Why} ->
{error, Why}
end.
consult1(S) ->
case io:read(S, '') of
{ok, Term} -> [Term|consult1(S)];
eof -> [];
Error -> Error
end.
我們可以用 code:which 找到任何「已載入模組」的目的碼
9> code:which(file).
"c:/PROGRA~1/ERL510~1.4/lib/kernel-2.16.4/ebin/file.beam"
而 file.beam 的原始程式就在 C:\Program Files\erl5.10.4\lib\kernel-2.16.4\src 目錄裡面。
io:get_line
以 io:get_line 一次讀取一行資料
1> {ok, S} = file:open("data1.dat", read).
{ok,<0.33.0>}
2> io:get_line(S, '').
"{person, \"john\", \"chen\",\n"
3> io:get_line(S, '').
"\t[{occupation, programmer}, {favorite, coding}]}.\n"
4> io:get_line(S, '').
"{cat, {name, \"money\"}, {owner, \"john\"}}."
5> io:get_line(S, '').
eof
6> io:get_line(S, '').
eof
7> file:close(S).
ok
file:read_file(File)
讀取整個檔案,作為一個 binary 資料
8> {ok, Bin} = file:read_file("data1.dat").
{ok,<<"{person, \"john\", \"chen\",\r\n\t[{occupation, programmer}, {favorite, coding}]}.\r\n{cat, {name, \"money\"}, {owner, "...>>}
file:pread
以 raw 模式開啟檔案,然後用 file:pread 讀取任意位置的檔案資料,檔案內第一個位元組,位置為 1
1> {ok, S} = file:open("data1.dat", [read, binary, raw]).
{ok,{file_descriptor,prim_file,{#Port<0.505>,564}}}
2> file:pread(S, 22, 46).
{ok,<<"\",\r\n\t[{occupation, programmer}, {favorite, cod">>}
3> file:pread(S, 1, 10).
{ok,<<"person, \"j">>}
4> file:close(S).
ok
@spec file:pread(IoDevice, Start, Len) -> {ok, Bin} | {error, Why}
尋找檔案
這是用 file:list_dir 與 file:read_file_info 搭配 re:run 做出來的。
%% -*- coding: utf-8 -*-
-module(lib_find).
-export([files/3, files/5]).
-import(lists, [reverse/1]).
-include_lib("kernel/include/file.hrl").
files(Dir, Re, Flag) ->
%% 這裡原本是用來簡化 regular expression 的輸入,例如可輸入 *.mp3
%% 但實際上測試結果,卻發生問題,因此就直接把這個轉換拿掉
%% Re1 = xmerl_regexp:sh_to_awk(Re),
reverse(files(Dir, Re, Flag, fun(File, Acc) ->[File|Acc] end, [])).
files(Dir, Reg, Recursive, Fun, Acc) ->
case file:list_dir(Dir) of
%% 以 file:list_dir 列出所有檔案 list,然後放入 find_files
{ok, Files} -> find_files(Files, Dir, Reg, Recursive, Fun, Acc);
{error, _} -> Acc
end.
%% 以 File|T 一次處理一個檔案
%% 由該檔案的類型,加上 Recursive 參數決定要不要搜尋目錄下的檔案
%% 所以這是 depth-first-search 的方法
find_files([File|T], Dir, Reg, Recursive, Fun, Acc0) ->
FullName = filename:join([Dir,File]),
%% io:format("FullName: ~p ~p~n", [FullName, Reg]),
case file_type(FullName) of
regular ->
%% re:run 的參數裡面,必須加上 unicode
%% 且設定為 {caprure, none} 時,回傳值只有 match 或 nomatch
case re:run(FullName, Reg, [{capture, none}, unicode]) of
%{match, _, _} ->
match ->
Acc = Fun(FullName, Acc0),
%% io:format("Acc = ~p~n", [Acc]),
find_files(T, Dir, Reg, Recursive, Fun, Acc);
_ ->
%% io:format("notmatch = ~n"),
find_files(T, Dir, Reg, Recursive, Fun, Acc0)
end;
directory ->
%% 遇到目錄時,由 Resursive 參數決定要不要繼續尋找目錄下的檔案
case Recursive of
true ->
Acc1 = files(FullName, Reg, Recursive, Fun, Acc0),
find_files(T, Dir, Reg, Recursive, Fun, Acc1);
false ->
find_files(T, Dir, Reg, Recursive, Fun, Acc0)
end;
error ->
find_files(T, Dir, Reg, Recursive, Fun, Acc0)
end;
find_files([], _, _, _, _, A) ->
A.
file_type(File) ->
case file:read_file_info(File) of
{ok, Facts} ->
case Facts#file_info.type of
regular -> regular;
directory -> directory;
_ -> error
end;
_ ->
error
end.
remove xmerl_regexp:sh_to_awk
在尋找檔案的程式中,有使用到 xmerl_regexp:sh_to_awk 這個轉換 regular expression 的 module,實際上測試時,卻發覺使用了反而讓程式出現問題,所以就直接把這個部份移除。
re:run
另外就是 re:run 的使用方式,第三個參數可加上 {capture, none} ,但在測試時,最好把這個參數移除,或是改為 {capture, [1]},這樣才能確定是不是有 match 到正確的位置。像以下範例的狀況,實際上 pattern 符合的位置,不是預期的副檔名的位置,而是在前面目錄的位置。
這時候就把 pattern 由 .mp3 改成 .mp3$,這樣就確定位置沒錯了。
3> re:run("d:/mp3/misc/測試test.mp3", ".mp3", [{capture, none}, unicode]).
match
4> re:run("d:/mp3/misc/測試test.mp3", ".mp3", [unicode]).
{match,[{2,4}]}
5> re:run("d:/mp3/misc/測試test.mp3", ".mp3$", [unicode]).
{match,[{24,4}]}
file:list_dir
在測試時,我們發現目錄裡面有中文字的時候,就會發生找不到檔案的問題。這個問題是 erl 預設在處理 standard_io 使用的 encoding 為 latin1,因此就造成了 file:list_dir 發生問題。
10> lib_find:files("d:/mp3/disc/測試", ".mp3$", true).
[]
11> lib_find:files("d:/mp3/misc/hot1", ".mp3$", true).
[[100,58,47,109,112,51,47,109,105,115,99,47,104,111,116,49,
[100,58,47,109,112,51,47|...]]
根據文件 unicode usage 的說明,windows 跟 macos 預設的 filename encoding 為 utf8,而 linux 是 latin1。
在 windows 的結果
1> file:native_name_encoding().
utf8
在 linux 的結果
1> file:native_name_encoding().
latin1
使用 file:list_dir 在 erl 與 werl 測試的結果不同,那是因為 werl 能支援 Unicode input and output,如果把測試程式碼寫到 module file 裡面,則都能運作。
在 windows 的 erl 測試,
1> file:list_dir("d:/mp3/disc/測試").
{error,enoent}
2> file:list_dir("d:/mp3/misc/hot1").
{ok,["00playall.m3u",
[48,49,46,32,22283,22659,20043,21335,40,33539,36920,33251,
41,46,109,112,51],
[...]|...]}
3> lists:keyfind(encoding, 1, io:getopts()).
{encoding,latin1}
然而在 windows 的 werl 測試
3> file:list_dir("d:/mp3/disc/冰與火之歌").
{ok,["00playall.m3u","01playall.m3u","02playall.m3u",
"03playall.m3u","readme.txt","S1","S2","S3"]}
4> file:list_dir("d:/mp3/misc/hot1").
{ok,["00playall.m3u",
[48,49,46,32,22283,22659,20043,21335,40,33539,36920,33251,
41,46,109,112,51],
[...]|...]}
5> lists:keyfind(encoding, 1, io:getopts()).
{encoding,unicode}
在 Erlang Unicode 兩三事 有一些測試,我想在處理檔名時,除了作業系統,還要注意 erl, werl 跟 module 的行為,可能都不一樣。
unicode in erlang
erlang 在 R17 才會正式完整支援 unicode,尤其是 erl source file 的部份,預設也是 UTF-8。
根據 Using Unicode in Erlang 文件的說明,因為 R16B 預設coding 為 bytewise (or latin1) encoding,如果在 module 的程式碼裡面要寫上中文字,而檔案要存成 utf-8 時,erl module source file 的第一行就必須要寫上
%% -*- coding: utf-8 -*-
以目前的狀況,當程式裡面遇到 re:run 與 file:list_dir ,跟 file io 有關的程式時,都必須要注意
- 以 英文目錄名 "d:/mp3/misc" 測試
- 以 中文目錄名 "d:/mp3/misc/測試" 測試
- 以 英文檔名 "d:/mp3/misc/test.mp3" 測試
- 以 中文檔名 "d:/mp3/misc/測試test.mp3" 測試
- 在 windows 與 linux 環境測試
事實上在 Java 處理中文檔名,也常常遇到很多問題,最後通常我們都會把實際的檔名紀錄在 DB 中,而 disk 裡面都只會存英文或數字的檔案或目錄名稱,避免程式在 windows/linux 上運作,會發生不同結果的意外狀況。
讀取 mp3 的 ID3 tag
mp3 的檔案資訊 metadata 儲存在 ID3 標籤區塊中,在這裡指處理兩種最簡單的 ID3 格式:ID3v1 與 ID3v1.1
ID3v1是在檔案最後 128 byts 包含固定長度的標籤
長度 | 內容 |
---|---|
3 | TAG 三個字元 |
30 | Title |
30 | Artist |
30 | Album |
4 | Year |
30 | Comment |
1 | Genre |
ID3v1.1 加入了歌曲編號,就是把30 bytes 的 comment 改成下面的格式
長度 | 內容 |
---|---|
28 | Comment |
1 | 0 |
1 | 歌曲編號 |
以下為掃描目錄內所有的 mp3,並將 ID3 資料寫入檔案中的程式。
%% -*- coding: utf-8 -*-
%% ---
%% Excerpted from "Programming Erlang",
%% published by The Pragmatic Bookshelf.
%% Copyrights apply to this code. It may not be used to create training material,
%% courses, books, articles, and the like. Contact us if you are in doubt.
%% We make no guarantees that this code is fit for any purpose.
%% Visit http://www.pragmaticprogrammer.com/titles/jaerlang for more book information.
%%---
-module(id3_v1).
-import(lists, [filter/2, map/2, reverse/1]).
-export([test/0, test2/0, dir/1, read_id3_tag/1]).
test() -> dir("d:/mp3/disc/測試").
test2() -> dir("d:/mp3/misc/hot1").
dir(Dir) ->
%% 取得目錄裡面所有副檔名為 mp3 的檔案
%%Files = lib_find:files(Dir, "\x{2e}\x{6d}\x{70}\x{33}", true),
Files = lib_find:files(Dir, ".mp3$", true),
%% 對每一個檔案以 read_id3_tag 檢查是否有 ID3
L1 = map(fun(I) ->
{I, (catch read_id3_tag(I))}
end, Files),
%% L1 = [{File, Parse}] where Parse = error | [{Tag,Val}]
%% we now have to remove all the entries from L where
%% Parse = error. We can do this with a filter operation
%% 以 filter 去掉結果為 error 的元素
L2 = filter(fun({_,error}) -> false;
(_) -> true
end, L1),
%% 將 L2 dump 到檔案 mp3data 裡面
lib_misc:dump("mp3data", L2).
read_id3_tag(File) ->
case file:open(File, [read,binary,raw]) of
{ok, S} ->
Size = filelib:file_size(File),
%% 讀取檔案最後 128 bytes
{ok, B2} = file:pread(S, Size-128, 128),
%% 判斷是否為 ID3 v1 or v1.1
Result = parse_v1_tag(B2),
file:close(S),
Result;
_Error ->
error
end.
%% 判斷是否為 ID3 v1.1
parse_v1_tag(<<$T,$A,$G,
Title:30/binary, Artist:30/binary,
Album:30/binary, _Year:4/binary,
_Comment:28/binary, 0:8,Track:8,_Genre:8>>) ->
{"ID3v1.1",
[{track,Track}, {title,trim(Title)},
{artist,trim(Artist)}, {album, trim(Album)}]};
%% 判斷是否為 ID3 v1
parse_v1_tag(<<$T,$A,$G,
Title:30/binary, Artist:30/binary,
Album:30/binary, _Year:4/binary,
_Comment:30/binary,_Genre:8>>) ->
{"ID3v1",
[{title,trim(Title)},
{artist,trim(Artist)}, {album, trim(Album)}]};
%% 其他狀況則傳回 error
parse_v1_tag(_) ->
error.
%% 去掉 binary 資料裡面的尾端 0 與 空白字元
%% 先將 Bin 轉成 list,然後 trim_blanks,再轉回 binary
trim(Bin) ->
list_to_binary(trim_blanks(binary_to_list(Bin))).
%% 將 list X 反轉後,去掉尾端 0 與 空白,然後再反轉回來
trim_blanks(X) -> reverse(skip_blanks_and_zero(reverse(X))).
skip_blanks_and_zero([$\s|T]) -> skip_blanks_and_zero(T);
skip_blanks_and_zero([0|T]) -> skip_blanks_and_zero(T);
skip_blanks_and_zero(X) -> X.
測試時,一樣會遇到 erl 無法處理中文目錄名稱的問題,可以把測試程式寫到 module 裡面,或是用 werl 測試。
寫入檔案
寫入 list 到檔案中
file:consult 可讀取檔案內容,我們可寫一個 unconsult 寫入資料
unconsult(File, L) ->
{ok, S} = file:open(File, write),
lists:foreach(fun(X) -> io:format(S, "~p.~n",[X]) end, L),
file:close(S).
測試
2> lib_misc:unconsult("test1.dat", [{cats, run}, {weather, raining}]).
ok
會得到一個檔案 test1.dat,內容為
{cats,run}.
{weather,raining}.
如果用 file:consult 讀取檔案,會得到兩個 erlang terms
3> file:consult("test1.dat").
{ok,[{cats,run},{weather,raining}]}
io:format
@spec io:format(IoDevice, Format, Args) -> ok
- IoDevice 必須要以寫入模式開啟
對 Args 來說,要對應 Format 格式
~n 是換行,這是跨平台的,在widows 會有 CR LF,在 linux 則是 LF
~p pretty-print 此參數
~s 字串
~w 以標準語法寫入資料,用來輸出 erlang term~s 還有一些其他的寫法,~10s是以10個字元的位置輸出字串,~-10S是靠左,~10.3.+s是在右邊留3個字元列印資料,其他的放 +,如果是 ~10.2.+s ,則結果就會變成 |++++++++ab|
test() ->
io:format("|~10s|~n", ["abc"]),
io:format("|~-10s|~n", ["abc"]),
io:format("|~10.3.+s|~n", ["abc"]),
io:format("|~10.10.+s|~n", ["abc"]),
io:format("|~10.7.+s|~n", ["abc"]),
io:format("~s~n", ["Hello Readers"]),
io:format("~w~n", [123]),
io:format("~s~n", ["that's it"]).
測試
20> lib_misc:test().
| abc|
|abc |
|+++++++abc|
|+++++++abc|
|+++abc++++|
Hello Readers
123
that's it
ok
file:write_file(File, IO)
IO list 是一個 list,其元素是 IO list、binary、0-255 整數,當 IO list 輸出到 File 時,會自動 flattened,清單的 { } 會被移除
scavenge_urls.erl 可以parsing html 網頁內容的資料,將所有
<a href ... </a>
資料取出來變成 list,然後再用這個 list 以 file:write_file(File, IO) 輸出到檔案。
% scavenge_urls.erl
-module(scavenge_urls).
-export([urls2htmlFile/2, bin2urls/1]).
-import(lists, [reverse/1, reverse/2, map/2]).
% 以 Urls list 加上 html ul href tag,然後存放到 File 裡面
urls2htmlFile(Urls, File) ->
file:write_file(File, urls2html(Urls)).
% 將 html binary 資料裡面的 URLs,轉換成 list 回傳
bin2urls(Bin) -> gather_urls(binary_to_list(Bin), []).
% 以 list 兩個元素製作 html 內容
urls2html(Urls) -> [h1("Urls"),make_list(Urls)].
% 第一行是 h1 Title
h1(Title) -> ["<h1>", Title, "</h1>\n"].
% 後面是 ul li 清單
make_list(L) ->
["<ul>\n",
map(fun(I) -> ["<li>",I,"</li>\n"] end, L),
"</ul>\n"].
% Bin 會先比對是不是 <a href 開頭
% 如果不是 就會 match [_|T] ,然後就去掉第一個字元,再運算一次 gather_urls
% 如果吻合 "<a href" ++ T ,接下來就利用 collect_url_body 取得 </a> 前面的內容
gather_urls("<a href" ++ T, L) ->
% T1 是 collect_url_body 處理後,剩下來的後面的資料
{Url, T1} = collect_url_body(T, reverse("<a href")),
gather_urls(T1, [Url|L]);
gather_urls([_|T], L) ->
gather_urls(T, L);
gather_urls([], L) ->
L.
% 跟 gather_urls 一樣的方法,L 一開始就是 reverse("<a href")
% 先比對 前面 是不是 </a> 開頭
% 如果不是 就會 match [H|T] ,然後就將第一個字元存到後面的 L,再運算一次 collect_url_body
% 如果吻合 "</a>" ++ T ,因為 L 的字元是相反的,就先 reverse,然後再加上 </a>,並將剩下的 T 回傳回去
collect_url_body("</a>" ++ T, L) -> {reverse(L, "</a>"), T};
collect_url_body([H|T], L) -> collect_url_body(T, [H|L]);
collect_url_body([], _) -> {[],[]}.
測試
1> S = socket_examples:nano_get_url("www.erlang.org").
<<"HTTP/1.0 200 OK\r\nServer: inets/5.7.1\r\nDate: Mon, 10 Feb 2014 06:51:11 GMT\r\nSet-Cookie: eptic_cookie=erlangorg@hades-"...>>
2> L = scavenge_urls:bin2urls(S).
["<a href=\"https://github.com/esl/erlang-web\">Erlang Web</a>", ...]
3> scavenge_urls:urls2htmlFile(L, "gathered.html").
ok
gathered.html 的內容就會是
<h1>Urls</h1>
<ul>
<li><a href="https://github.com/esl/erlang-web">Erlang Web</a></li>
...
</ul>
寫入一個隨機存取檔案
將檔案以隨機存取的方式寫入資料,作法跟讀取類似,先以 write mode 打開檔案,然後使用 file:pwrite(Position, Bin) 寫入資料。
1> {ok, S} = file:open("test3.dat", [raw, write, binary]).
{ok,{file_descriptor,prim_file,{#Port<0.500>,568}}}
2> file:pwrite(S, 10, <<"new">>).
ok
3> file:close(S).
ok
test3.dat 為以下的內容,但前面 10 個字元都是 16#00
new
目錄與檔案操作
file:list_dir(Dir) 可取得目錄 Dir 裡面的檔案清單(包含子目錄)
file:makr_dir(Dir) 會建立一個新的目錄
file:del_dir(Dir) 會刪除一個目錄
file:copy(Source, Destination) 將 Source 複製到 Destination
file:delete(File) 刪除檔案
取得檔案的資訊 file:read_file_info(F)
如果檔案 F 是有效的檔案或目錄名稱,就會傳回 {ok, Info} ,Info 是型別為 #file_info 的 record,定義如下
-record( file_info,
{ size, % size of file in bytes
type, % atom: device, directory, regular, symlink, other
access, % atom: read, write, read_write, none
atime, % {{Year,Mon,Day},{Hour,Min,Sec}} 檔案上次被讀取的時間
mtime, % {{Year,Mon,Day},{Hour,Min,Sec}} 檔案上次被寫入的時間
ctime, % 在 linux 是檔案上次被修改的時間,在 windows 是檔案被建立的時間
mode, % Integer: File Permissions, windows 裡面 owner permission會複製到group與user
links, % number of links to the file,如果不支援 link 就會永遠是 1
major_device, % Integer: Identifies the file system(linux) 或是 device number in windows ( A:=0, B:=1 )
minor_device,
inode,
uid,
gid
})
可以取得檔案的大小及type,並擴充 file:list_dir 增加檔案的資訊
-include_lib("kernel/include/file.hrl").
file_size_and_type(File) ->
case file:read_file_info(File) of
{ok, Facts} ->
{Facts#file_info.type, Facts#file_info.size};
_ ->
error
end.
ls(Dir) ->
{ok, L} = file:list_dir(Dir),
map(fun(I) -> {I, file_size_and_type(I)} end, sort(L)).
測試
1> lib_misc:ls(".").
[{"code",{directory,0}},
{"gathered.html",{regular,1768}},
{"lib_misc.beam",{regular,1616}},
{"lib_misc.erl",{regular,882}},
{"scavenge_urls.beam",{regular,1540}},
{"scavenge_urls.erl",{regular,1746}},
{"socket_examples.beam",{regular,2784}},
{"socket_examples.erl",{regular,2651}},
{"test.md",{regular,6059}},
{"test1.dat",{regular,31}},
{"test3.dat",{regular,13}}]
filelib 模組有一些小的函數,使用上比 #file_info 方便,例如 file_size(File) 與 is_dir(X)
其他功能
file:open 提供的模式有很多種,例如 compressed 是用來讀寫 gzip 壓縮檔
{error, enoent} 錯誤碼,是代表檔案不存在的意思
filename 模組有很多拆解檔名的函數,可找出副檔名,或組合檔名
filelib 模組有少量有用的函數,例如 filelib:ensure_dir(Name) 可確保父目錄存在,如果不存在就會建立目錄
character transcoding
任何其他的字元字串,經過 java 讀取後,就會變成 utf16 Big Endian encoding(參閱 Encoding Gossip: Java 的字串),如果需要特殊編碼的字串,就要加上編碼的參數。在Java中遇到亂碼問題的時候,第一步就是要識別字串的編碼是什麼,假設是 Big5,我們就要以 getBytes 告訴 JVM 要以 Big5 的方式取得字串的 byte array,然後再由byte array轉換為 utf8 的編碼。
byte[] binary = "測試".getBytes("Big5");
String b= new String(binary, "UTF-8");
erlang 一直到 R13 之後,才開始慢慢重視 unicode 的問題,mbcs 就是用來解決編碼轉換的問題。
要使用 mbcs,就先將 project checkout 下來,然後放到 C:\Program Files\erl5.10.4\lib\mbcs 路徑下,編譯後的 beam 放到 C:\Program Files\erl5.10.4\lib\mbcs\ebin 裡面,然後就可以用 werl 測試。
不能使用 erl 測試的原因是因為 erl 預設的編碼為 latin1,要使用 werl 才能正確處理 unicode 資料輸入。
Eshell V5.10.4 (abort with ^G)
1> l(mbcs).
{module,mbcs}
2> mbcs:start().
ok
3> mbcs:encode("世界,你好", gbk).
<<"ÊÀ½ç£¬ÄãºÃ">>
4> mbcs:encode("世界,你好", big5).
<<"¥@¬É¡A§A¦n">>
5>
參考
Erlang and OTP in Action
Programming Erlang: Software for a Concurrent World
沒有留言:
張貼留言