Julia 提供了一個豐富的接口處理終端、管道、tcp套接字等等I/O流對象。
接口在系統(tǒng)層的實現(xiàn)是異步的,開發(fā)者以同步的方式調用該接口、一般無需關注底層異步實現(xiàn)。 接口實現(xiàn)主要基于Julia支持的協(xié)程(coroutine)功能。
所有 Julia 流都至少提供一個 read
和一個 write
方法,且第一個參數(shù)都是流對象,例如:
julia> write(STDOUT,"Hello World")
Hello World
julia> read(STDIN,Char)
'\n'
注意我又輸入了一次回車,這樣 Julia 會讀入換行符。現(xiàn)在,由例子可見,write
方法的第二個參數(shù)是將要寫入的數(shù)據,read
方法的第二個參數(shù)是即將讀入的數(shù)據類型。例如,要讀入一個簡單的字節(jié)數(shù)組,我們可以:
julia> x = zeros(Uint8,4)
4-element Uint8 Array:
0x00
0x00
0x00
0x00
julia> read(STDIN,x)
abcd
4-element Uint8 Array:
0x61
0x62
0x63
0x64
不過像上面這么寫有點麻煩,還提供了一些簡化的方法。例如,我們可以將上例重寫成:
julia> readbytes(STDIN,4)
abcd
4-element Uint8 Array:
0x61
0x62
0x63
0x64
或者直接讀入整行數(shù)據:
julia> readline(STDIN)
abcd
"abcd\n"
注意這取決于你的終端配置,你的 TTY 可能是行緩沖、需要多輸入一個回車才會把數(shù)據傳給 julia。
如果你想要通過 STDIN 去讀每一行,你可以使用 eachline 方法:
for line in eachline(STDIN)
print("Found $line")
end
或者如果你想要以字符為單位去讀,則如下:
while !eof(STDIN)
x = read(STDIN, Char)
println("Found: $x")
end
注意上面提到的寫方法是對二進制流進行操作的。特別是,值不會被轉換成任何規(guī)范的文本表示,而是會被寫成如下:
julia> write(STDOUT,0x61)
a
對于文本 I/O,可以使用 print 或 show 方法,這取決你的需求(可以查看標準庫中對于兩者不同之處的細節(jié)描述):
julia> print(STDOUT,0x61)
97
像其他的環(huán)境一樣,Julia 有一個開放的函數(shù),它以一個文件名作為參數(shù)并且返回一個 IO 流對象,通過這個 IO 流,你可以從文件中讀或者寫其中的內容。舉個例子來說,如果我們有一個文件,hello.txt,它的內容為 “Hello, World!”:
julia> f = open("hello.txt")
IOStream(<file hello.txt>)
julia> readlines(f)
1-element Array{Union(ASCIIString,UTF8String),1}:
"Hello, World!\n"
如果你想要寫入某些內容到文件當中,你可以用寫標志 (“w”) 打開它:
julia> f = open("hello.txt","w")
IOStream(<file hello.txt>)
julia> write(f,"Hello again.")
12
如果你用這種方式檢查 hello.txt 的內容,你將會注意到它是空的;其實沒有任何東西被寫入磁盤。這是因為 IO 流在數(shù)據真正寫到磁盤之前必須被關掉:
julia> close(f)
再次檢查 hello.txt 將會顯示它的內容已經被改變了。
打開一個文件,對它的內容做一些改變,然后關閉它是一個常見的模式。為了讓這個過程更簡單,這里存在另一個 open 的調用,用一個方法作為其第一個參數(shù),用文件名作為他的第二個參數(shù),打開文件,調用該文件的方法作為一個參數(shù),然后再次關閉它。舉一個例子,給出一個方法:
function read_and_capitalize(f::IOStream)
return uppercase(readall(f))
end
你可以調用:
julia> open(read_and_capitalize, "hello.txt")
"HELLO AGAIN."
為了打開 hello.txt,調用它的 read_and_capitalize 方法,關閉 hello.txt 然后返回大寫的內容。
為了避免去定義一個已經命名的函數(shù),你可以使用 do 語法,動態(tài)的去創(chuàng)建一個匿名函數(shù):
julia> open("hello.txt") do f
uppercase(readall(f))
end
"HELLO AGAIN."
讓我們直接用一個簡單的 Tcp Sockets 的示例來說明。我們首先需要創(chuàng)建一個簡單地服務器:
julia> @async begin
server = listen(2000)
while true
sock = accept(server)
println("Hello World\n")
end
end
Task
julia>
那些熟悉 Unix socket API 的人,會覺得方法名和 Unix socket 很相似,盡管他們的用法比原生 Unix socket API 要簡單。第一次調用 listen 將會創(chuàng)建一個服務器來等待即將到來的連接,在這個案例中監(jiān)聽的端口為 2000。相同的方法可能會用來去創(chuàng)建不同的其他種類的服務器:
julia> listen(2000) # Listens on localhost:2000 (IPv4)
TcpServer(active)
julia> listen(ip"127.0.0.1",2000) # Equivalent to the first
TcpServer(active)
julia> listen(ip"::1",2000) # Listens on localhost:2000 (IPv6)
TcpServer(active)
julia> listen(IPv4(0),2001) # Listens on port 2001 on all IPv4 interfaces
TcpServer(active)
julia> listen(IPv6(0),2001) # Listens on port 2001 on all IPv6 interfaces
TcpServer(active)
julia> listen("testsocket") # Listens on a domain socket/named pipe
PipeServer(active)
注意最后一次調用的返回值類型是不同的。這是因為這個服務器沒有監(jiān)聽 TCP,而是在一個命名管道(Windows 術語)- 同樣也稱為域套接字(UNIX 的術語)。他們的不同之處非常微小,并且與他們的接收和連接方法有關系。接受方法會檢索一個到客戶端的連接,連接到我們剛剛創(chuàng)建的服務器端,而連接到服務器的函數(shù)使用的是特定的方法。連接方法和監(jiān)聽方法的參數(shù)是一樣的,所以使用的環(huán)境(比如主機,cwd 等等)能夠傳遞和監(jiān)聽方法相同的參數(shù)來建立一個連接。所以讓我們來嘗試一下(前提是已經創(chuàng)建好上面的服務器):
julia> connect(2000)
TcpSocket(open, 0 bytes waiting)
julia> Hello World
正如我們預期的那樣,我們會看到 “Hello World” 被打印出來了。所以我讓我們分析一下在后臺發(fā)生了什么。當我們調用連接函數(shù)時,我們連接到了我們剛剛創(chuàng)建的服務器。同時,接收方法返回一個服務器端的連接到最新創(chuàng)建的套接字上,然后打印 “Hello World” 來表明連接成功了。
Julia 的一個強大功能是盡管 I/O 實際上是異步發(fā)生的,但 API 仍然是同步的,我們甚至不必擔心回調或服務器是否繼續(xù)正常運行。當我們調用連接時,當前任務會等待連接建立,并且在連接建立之后,當前任務才會繼續(xù)執(zhí)行。在暫停期間,服務器任務會恢復執(zhí)行(因為現(xiàn)在一個連接請求可用),接受這個連接,打印出信息并且等待下一個客戶端。讀和寫的工作是相同的。為了更好地理解,請看以下一個簡單的 echo 服務器:
julia> @async begin
server = listen(2001)
while true
sock = accept(server)
@async while true
write(sock,readline(sock))
end
end
end
Task
julia> clientside=connect(2001)
TcpSocket(open, 0 bytes waiting)
julia> @async while true
write(STDOUT,readline(clientside))
end
julia> println(clientside,"Hello World from the Echo Server")
julia> Hello World from the Echo Server
一種不伴隨監(jiān)聽方法的 connect 函數(shù)為 connect(host::ASCIIString,port),它會嘗試去連接到主機端口參數(shù)給出的端口提供的主機參數(shù)給出的主機。它允許你如下操作:
julia> connect("google.com",80)
TcpSocket(open, 0 bytes waiting)
這個功能的基礎是 getaddrinfo 方法,將提供適當?shù)牡刂方馕?
julia> getaddrinfo("google.com")
IPv4(74.125.226.225)
更多建議: