日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

lua工具库penlight--06数据(一)

發布時間:2024/4/13 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 lua工具库penlight--06数据(一) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這篇太長了,分了兩部分。(這個是機器翻譯之后我又校對了一下,以后的都這樣,人工翻譯太累了。)

讀數據文件

首先考慮清楚,你的確需要一個自定義的文件讀入器嗎?如果是,你能確定有能力寫好嗎?

正確,穩健,快速,當如先得把第一項處理好。

Unix世界里常見的數據文件夾是配置文件。在Java世界里也被叫做屬性文件。

?#?Read?timeout?in?seconds

read.timeout=10

?

#?Write?timeout?in?seconds

write.timeout=10

?

下面是簡單的Lua的實現:

--?property?file?parsing?with?Lua?string?patterns

props?=?[]

for?line?in?io.lines()?do

?????if?line:find('#',1,true)?~=?1?and?not?line:find('^%s*$')?then

?????????local?var,value?=?line:match('([^=]+)=(.*)')

?????????props[var]?=?value

?????end

end

?

非常簡潔,不過用了字符串匹配技巧,對于讀者來說不易讀懂。

下面是利用Penlight的實現:

require?'pl'

stringx.import()

props?=?[]

for?line?in?io.lines()?do

?????if?not?line:startswith('#')?and?not?line:isspace()?then

?????????local?var,value?=?line:splitv('=')

?????????props[var]?=?value

?????end

end

?

顯然上面的代碼可以自為文檔,而不需要到處寫注釋。它的速度稍微有點慢

,但是事實上腳本的速度有I/O決定,因此一下優化是必須的。

?

讀無結夠的文本數據

文本經常是無結構的。pl.input提供了許多簡化操作的函數。例如統計文本

中的單詞數量:

?--?countwords.lua

require?'pl'

local?k?=?1

for?w?in?input.words(io.stdin)?do

?????k?=?k?+?1

end

print('count',k)

?

或者計算平均數:

?--?average.lua

require?'pl'

local?k?=?1

local?sum?=?0

for?n?in?input.numbers(io.stdin)?do

?????sum?=?sum?+?n

?????k?=?k?+?1

end

print('average',sum/k)

這些腳本可以通過進一步排除循環提高效率。下面這個例子,使用seq.sum函數

計算數字序列的和。

--?average2.lua

require?'pl'

local?total,n?=?seq.sum(input.numbers())

print('average',total/n)

?

一個更簡單的例子是,從輸入中提取參數,如下:

--?countwords2.lua

require?'pl'

print('count',seq.count(input.words()))

?

一個有用的序列生成器要能直接讀取字符串。下面這個例子計算文件中每行的數字和:

--?sums.lua

for?line?in?io.lines()?do

?????print(seq.sum(input.numbers(line))

end

?

讀分隔文件

讀取分隔文件是常有的事,它們或以空格、或者逗號分隔,或許還有初始的列頭。

如下例:

?EventID????Magnitude????LocationX????LocationY????LocationZ

?981124001????2.0????18988.4????10047.1????4149.7

?981125001????0.8????19104.0????9970.4????5088.7

?981127003????0.5????19012.5????9946.9????3831.2

...

?

input.fields可以提取列,并且可以設置分隔符(默認空格)。

下面是計算所有事件里x的平均位置:(即上表中的LocationX?

?--?avg-x.lua

require?'pl'

io.read()?--?skip?the?header?line

local?sum,count?=?seq.sum(input.fields?{3})

print(sum/count)

?

input.fields可以提取列,并且可以設置分隔符(默認空格)。

下面是計算所有事件里x的平均位置:(即上表中的LocationX?

?--?avg-x.lua

require?'pl'

io.read()?--?skip?the?header?line

local?sum,count?=?seq.sum(input.fields?{3})

print(sum/count)

?

input.fields可以使用字段數或列數,可以從1開始也可以從其它列開始。如果你傳遞一個字段計數,你會得到該計數的每個字段:

?for?id,mag,locX,locY,locZ?in?input.fields?(5)?do

?....

?end

?

input.fields?默認會嘗試將每個字段轉換為數字。它將跳過不匹配模式的字段,如果任何字段都不能轉換為數字時會中止腳本。input.fields第二個參數是分隔符,默認是空格。如果傳入’?‘,會匹配?'任意數目的空格',即?'%s+',你可以使用?任意的Lua?字符串匹配模式。

第三個參數是數據源,默認為標準輸入?(由input.create_getter定義)。它假定數據源有一個read的方法,可以產生下一行,即它是一個?'類文件'?對象。作為一種特殊情況,一個字符串,將分成多行:

?>?for?x,y?in?input.fields(2,'?','10?20\n30?40\n')?do?print(x,y)?end

?10??????20

?30??????40

?

注意對于壞的字段,默認行為是顯示出錯的行號:

?>?for?x,y?in?input.fields(2,'?','10?20\n30?40x\n')?do?print(x,y)?end

?10??????20

?line?2:?cannot?convert?'40x'?to?number

?

Input.fields的這種行為是適當的,第四個可選參數是一個選項表:?{no_fail=true}意味著嘗試轉換后失敗后,僅返回字符串,而不是向?AWK(譯注:一個*nix下的文本匹配工具)繼續運行。你有責任檢查返回的字段的類型。{no_convert=true}是否將所有字段都作為字符串返回。

有時將整個數據放到內存中,會很有用,如提取的列的操作。Penlight專門為閱讀此類型的數據,提供了一個靈活的data解析器。例如看起來像這樣的文件:

?x,y

?10,20

?2,5

?40,50

?

data.read將創建如下的表,每一行創建一個子表:

?>?t?=?data.read?'test.txt'

?>?pretty.dump(t)

?{{10,20},{2,5},{40,50},fieldnames={'x','y'},delim=','}

?

現在,您可以使用提供的方法來分析返回的表。例如,方法column_by_name返回的列的所有表。

?--?testdata.lua

?require?'pl'

?d?=?data.read('fev.txt')

?for?_,name?in?ipairs(d.fieldnames)?do

?????local?col?=?d:column_by_name(name)

?????if?type(col[1])?==?'number'?then

?????????local?total,n?=?seq.sum(col)

?????????utils.printf("Average?for?%s?is?%f\n",name,total/n)

?????end

?end

?

data.read有點小聰明,默認情況下它期望第一行是列名稱,除非其中都是何數字。它會嘗試推導第一行的列分隔符。有時它會猜錯,當然可以顯式指定分隔符。第二個可選參數是一個選項表:?可以重寫delim?字符串匹配模式),fieldnames(列表或逗號分隔的字符串),是否轉換no_convert?(默認是要轉換),numfields?(列表中的列的索引)?和thousands_dot?千位在?Excel?CSV?中的分隔符是?‘.?')

一個非常強大的功能是在這種數據上執行類似于?SQL?的查詢:

?--?queries?on?tabular?data

?require?'pl'

?local?d?=?data.read('xyz.txt')

?local?q?=?d:select('x,y,z?where?x?>?3?and?z?<?2?sort?by?y')

?for?x,y,z?in?q?do

?????print(x,y,z)

?end

?

請注意查詢的格式限于以下語法:

?FIELDLIST?[?'where'?CONDITION?]?[?'sort?by'?FIELD?[asc|desc]]

?

任何有效的?Lua?代碼都可以出現在CONDITION中;請記住它并不是SQL,您必須使用==?(此警告來自于經驗)

若想這樣,字段名稱必須是?Lua?的標識符。所以read會改變字段名,這樣,所有非字母數字字符替換為下劃線。然而,?original_fieldnames字段總是包含原始字段名。

read可以很好的處理標準?CSV?文件,但是它不會嘗試成為一個全面的?CSV?分析器。加上csv=true選項,可以處理雙引號字段,這些字段可以包含逗號,尾隨逗號也是有意義的。

電子表格程并不總是處理這種數據最好的工具,這看起來可能對某些人很奇怪。下面是一個玩具?CSV?文件?;要認識這個問題,想象類似下面的數千行和數十列:

?Department?Name,Employee?ID,Project,Hours?Booked

?sales,1231,overhead,4

?sales,1255,overhead,3

?engineering,1501,development,5

?engineering,1501,maintenance,3

?engineering,1433,maintenance,10

?

任務是減少相關的行和列的數據、?或許做一些處理行的數據,并將結果寫到一個新的?CSV?文件。write_row方法使用分隔符將行寫入一個文件?;Data.select_row類似Data.select,除了它會循環訪問行,而不是字段;如果我們處理很多列,這是必要的?!

?names?=?{[1501]='don',[1433]='dilbert'}

?keepcols?=?{'Employee_ID','Hours_Booked'}

?t:write_row?(outf,{'Employee','Hours_Booked'})

?q?=?t:select_row?{

?????fields=keepcols,

?????where=function(row)?return?row[1]=='engineering'?end

?}

?for?row?in?q?do

?????row[1]?=?names[row[1]]

?????t:write_row(outf,row)

?end

?

Data.select_rowData.select可以傳遞一個表,并指定查詢?;字段表,定義條件和可選參數的sort_by函數。它不是必須的,但如果我們有更復雜的行條件?(如屬于指定的一組)。

如果不能存到hackery(不知如何翻譯)如全局變量,將不能作為一般條件查詢字符串。

在?1.0.3,您可以指定所選列的顯式轉換函數。例如,這是?Unix?日期戳一個日志文件:

?Time?Message

?1266840760?+#?EE7C0600006F0D00C00F06010302054000000308010A00002B00407B00

?1266840760?closure?data?0.000000?1972?1972?0

?1266840760?++?1266840760?EE?1

?1266840760?+#?EE7C0600006F0D00C00F06010302054000000408020A00002B00407B00

?1266840764?closure?data?0.000000?1972?1972?0

?

我們想把第一列作為實際日期對象,這樣convert可以從1顯式轉換字段。(請注意第一次我們必須顯式轉換字符串為數字)。

?Date?=?require?'pl.Date'

?

?function?date_convert?(ds)

?????return?Date(tonumber(ds))

?end

?

?d?=?data.read(f,{convert={[1]=date_convert},last_field_collect=true})

?

這給了我們一個兩列數據集,其中的第一列包含日期對象,第二列包含該行的其余部分。查詢可以很容易找出事件發生的一天:

?q?=?d:select?"Time,Message?where?Time:weekday_name()=='Sun'"

?

數據沒有來從文件,也不一定來自于實驗室或會計部。在?Linux?上,?ps?aux為您提供在您的機器上運行的所有進程的完整列表。直接把ps?aux的結果送入data.read,并對它執行有用的查詢。請注意非標識符字符,如?%會轉化為下劃線:

?require?'pl'

?f?=?io.popen?'ps?aux'

?s?=?data.read?(f,{last_field_collect=true})

?f:close()

?print(s.fieldnames)

?print(s:column_by_name?'USER')

?qs?=?'COMMAND,_MEM?where?_MEM?>?5?and?USER=="steve"'

?for?name,mem?in?s:select(qs)?do

?????print(mem,name)

?end

?

我一直崇拜?AWK?編程語言?;使用filter中,您可以讓lua作為簡單的AWK

?--?printxy.lua

?require?'pl'

?data.filter?'x,y?where?x?>?3'

?

數據文件沒有標題,沒有字段名稱,是常見現象。如果所有字段都是數字,data.read把此類文件為一個特殊的例外。由于在查詢表達式中無法使用的列名稱,您可以使用?類AWK的?索引,例如?'?$1、?$2$1?>?3'。我有一個小的可執行腳本,在我的系統稱為lf這看起來像這樣:

?#!/usr/bin/env?lua

?require?'pl.data'.filter(arg[1])

?

它可用于一般的從數據中提取列的篩選器命令。(列規格可能是表達式或甚至常量)

?$?lf?'$1,$5/10'?<?test.dat

?

(與?AWK類似,請注意在此命令使用單引號,這樣可以防止shell解釋列索引。如果您在?Windows?上,你必須用在雙引號引起傳遞給您的批處理文件參數)

作為教程的資源,看看test-data.lua的使用,以及其他例子的評論。

?

從readData.copy_select查詢返回的數據,基本上是只是數組的行:?{{1,2},{3,4}}。所以您可以使用read處理任何類似數組的數據集,或者任何其它類似的函數處理。尤其是,在array2d函數中數據工作正常。事實上,這些函數可以作為方法?;例如array2d.flatten?,可以直接給我們一維列表:

?v?=?data.read('dat.txt'):flatten()

?

LuaMatrix期望數據像矩陣一樣正確的形狀:

?>?matrix?=?require?'matrix'

?>?m?=?matrix(data.read?'mat.txt')

?>?=?m

?1???????0.2?????0.3

?0.2?????1???????0.1

?0.1?????0.2?????1

?>?=?m^2??--?same?as?m*m

?1.07????0.46????0.62

?0.41????1.06????0.26

?0.24????0.42????1.05

?

write將寫入矩陣文件。

最后,可以使用全局變量_DEBUG打印出查詢生成并動態編譯的實際迭代器函數。通過使用代碼生成,我們可以任優化查詢的性能。

?>?lua?-lpl?-e?"_DEBUG=true"?-e?"data.filter?'x,y?where?x?>?4?sort?by?x'"?<?test.txt

?return?function?(t)

?????????local?i?=?0

?????????local?v

?????????local?ls?=?{}

?????????for?i,v?in?ipairs(t)?do

?????????????if?v[1]?>?4??then

?????????????????????ls[#ls+1]?=?v

?????????????end

?????????end

?????????table.sort(ls,function(v1,v2)

?????????????return?v1[1]?<?v2[1]

?????????end)

?????????local?n?=?#ls

?????????return?function()

?????????????i?=?i?+?1

?????????????v?=?ls[i]

?????????????if?i?>?n?then?return?end

?????????????return?v[1],v[2]

?????????end

?end

?

?10,20

?40,50

?

?

讀取配置文件

配置模塊提供了把幾種類型的配置文件轉換為一個?Lua?表的簡單方法。考慮的簡單示例:

?#?test.config

?#?Read?timeout?in?seconds

?read.timeout=10

?

?#?Write?timeout?in?seconds

?write.timeout=5

?

?#acceptable?ports

?ports?=?1002,1003,1004

?

可以使用config.read讀,使用pretty.write顯示結果?:

?--?readconfig.lua

?local?config?=?require?'pl.config'

?local?pretty=?require?'pl.pretty'

?

?local?t?=?config.read(arg[1])

?print(pretty.write(t))

?

lua?readconfig.lua?test.config的輸出是:

?{

???ports?=?{

?????1002,

?????1003,

?????1004

???},

???write_timeout?=?5,

???read_timeout?=?10

?}

?

config.read將產生所有鍵/值對,忽略?#?注釋,并確保鍵名稱是正確的?Lua?標識符,通過非標識符字符替換為?_。如果這些值是數字,他們將被轉換。(所以t.write_timeout的值是數字?5)。此外,由逗號分隔的任何值將同樣轉換為數組。

可以一個反斜杠續行。考慮下面這行:

?names=one,two,three,?\

?four,five,six,seven,?\

?eight,nine,ten

?

此外支持?Windows?風格的?INI?文件。INI?文件的部分結構自然轉換為嵌套表在?Lua?中:

?;?test.ini

?[timeouts]

?read=10?;?Read?timeout?in?seconds

?write=5?;?Write?timeout?in?seconds

?[portinfo]

?ports?=?1002,1003,1004

?

輸出為:

?{

???portinfo?=?{

?????ports?=?{

???????1002,

???????1003,

???????1004

?????}

???},

???timeouts?=?{

?????write?=?5,

?????read?=?10

???}

?}

?

你現在可以這樣用t.timeouts.write引用write?timeout.

最后一個例子顯示config.read?讀取逗號分隔的文件的靈活性。

?one,two,three

?10,20,30

?40,50,60

?1,2,3

?

它將生成下表:

?{

???{?"one",?"two",?"three"?},

???{?10,?20,?30?},

???{?40,?50,?60??},

???{?1,?2,?3?}

?}

?

config.read不是設計為讀取所有的?CSV?文件,但打算支持沒有鍵-值對的結構,如?'/?etc/passwd'?等一些?Unix?配置文件???。

這個函數想成為讀取配置的瑞士軍刀,它無需做出假設,你也可能不喜歡他們(假設)。所以有一個可選的額外參數,來進行一些控制,可能有以下字段:

?{

????variablilize?=?true,

????convert_numbers?=?tonumber,

????trim_space?=?true,

????list_delim?=?',',

????trim_quotes?=?true,

????ignore_assign?=?false,

????keysep?=?'=',

????smart?=?false,

?}

?

variablilize選項即第一個示例中將write.timeout轉化write_timeout?。如果convert_numbers為?true,嘗試轉換開始像數的任何字符串。您可以指定您自己的函數?(如像?'5224?kb'?的字符串轉換為數字)。

trim_space可確保有沒有開始或結尾的空白值,list_delim分割字符?(如可能?Lua?字符串模式?'%s+'.)

例如,在?Unix?中的密碼文件是冒號分隔:

?t?=?config.read('/etc/passwd',{list_delim=':'})

?

這將產生以下輸出在我的系統?(只有最后兩線所示):

?{

???...

???{

?????"user",

?????"x",

?????"1000",

?????"1000",

?????"user,,,",

?????"/home/user",

?????"/bin/bash"

???},

???{

?????"sdonovan",

?????"x",

?????"1001",

?????"1001",

?????"steve?donovan,28,,",

?????"/home/sdonovan",

?????"/bin/bash"

???}

?}

?

你可以進入這一個更明智的格式,加上判斷哪些用戶名是索引(?tablex.pairmap函數必須返回valuekey!)

?t?=?tablex.pairmap(function(k,v)?return?v,v[1]?end,t)

?

得到:

?{?...

???sdonovan?=?{

?????"sdonovan",

?????"x",

?????"1001",

?????"1001",

?????"steve?donovan,28,,",

?????"/home/sdonovan",

?????"/bin/bash"

???}

?...

?}

?

許多常見的?Unix?配置文件可以通過調整這些參數讀取。/etc/fstab,選項為{list_delim=‘%s+’,ignore_assign=true}將正確分隔列。在文件里查找’KEY?VALUE’是常見的,如/etc/ssh/ssh_config;?選項{keysep=‘?’}使config.read返回一個表,其中每個key具有一個值value。

在?Linux?中的文件procfs通常使用?':’作為分隔符

?>?t?=?config.read('/proc/meminfo',{keysep=':'})

?>?=?t.MemFree

?220140?kB

?

這一結果是一個字符串,因為tonumber不喜歡它,但把convert_numbers定義為function(s)?return?tonumber((s:gsub('?kB$','')))?end,可以返回實際數字。(額外的括號是必要因此tonumber僅從gsub中獲取的第一個結果)。

tests/test-config.lua':

?testconfig([[

?MemTotal:????????1024748?kB

?MemFree:??????????220292?kB

?]],

?{?MemTotal?=?1024748,?MemFree?=?220292?},

?{

??keysep?=?':',

??convert_numbers?=?function(s)

?????s?=?s:gsub('?kB$','')

?????return?tonumber(s)

???end

??}

?)

?

smart選項可以讓config.read替你一個合理的猜測,例子為tests/test-config.lua。基本上可以直接在智能模式下處理這些常見的文件格式?(那些遵循同一模式的文件):?'?/etc/fstab?''/?proc/XXXX/status''ssh_config'?和?'pdatedb.conf'

請注意,?config.read可以傳入類文件對象的參數;如果它不是一個字符串,并支持read方法,才可以使用。例如,若要從字符串中讀取一個配置,請使用stringio.open?.

總結

以上是生活随笔為你收集整理的lua工具库penlight--06数据(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。