小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

云風(fēng)的 BLOG: 策劃們離不開的 Excel

 icecity1306 2014-11-27

策劃們離不開的 Excel

我相信至少在國內(nèi)的游戲策劃圈, Excel 是每天必不可少的存在。倒不是因?yàn)橐盟谱鲾?shù)值表格,一切文檔最終都一定是用 Excel 寫的。但作為一個程序員,我相當(dāng)?shù)耐春?Excel 文件,就好像我當(dāng)初痛恨 word 一樣。只有幾個字就不要保存成 doc 文件啦,可現(xiàn)在已經(jīng)沒有人用 word 了,大家全轉(zhuǎn)去 Excel 了。如果有可能,策劃一定愿意在單元格里寫腳本的,這樣可以將重點(diǎn)標(biāo)紅。

提取 Excel 中的文字信息并不復(fù)雜,但真正的麻煩在于 Excel 文件對版本管理工具是極不友好的。甚至你打開一次 Excel 文件再保存關(guān)閉,也會生成一個完全不同的新版本。這是因?yàn)?,文件中記錄了最后修改的時間(是的,Excel 不信任文件系統(tǒng)里的時間);還有激活的單元格是哪一個。在這種環(huán)境下,多人協(xié)作的版本控制工具用起來絕對是一個悲劇。

我大概花了一周時間來試圖解決一系列問題。結(jié)果不算成功,也不算失敗。這里記錄一下上周踩過的坑。

問題源于我們的項(xiàng)目中,策劃把一切他們能生產(chǎn)的東西都記在了諸多的 excel 表格里。當(dāng)然,和上世紀(jì)的程序員一樣,大家都盡量自己維護(hù)自己的那塊文件,所以即使在版本管理工具下,也基本沒有沖突。但是總有那么 1% 的機(jī)會,幾個人會修改同一張表格的,尤其在項(xiàng)目壓力大時,往往實(shí)現(xiàn)功能的程序也會打開表格對里面的數(shù)據(jù)做一些修改。在版本控制工具下,沖突就在所難免了。尤其是我們剛剛讓策劃從 svn 遷移到 git 下,git 的工作流的復(fù)雜性很容易讓策劃的腦子不夠用了(實(shí)際上受 Excel 文件格式限制,他們也只需要一個版本備份工具,其它本來就是多余的)。我開始動念頭來解決問題。

首先,xlsx 文件其實(shí)是一個標(biāo)準(zhǔn) zip 壓縮包,里面打包了一系列 xml 文件。如果僅僅是需要一個文本格式,那么只需要把包解開,用一種非壓縮的形式重新打包即可。

對于一些嵌入的圖片,只需要用 base64 編碼。由于嵌入表格的圖片多半不會修改,所以并不會造成版本間的差異。

一開始,我以為這項(xiàng)工作兩小時就能搞定,事后發(fā)現(xiàn),太天真了。

我寫了一個 lua 的小程序,可以讀出 zip 包里的文件,對文件名排序,然后按文件名/內(nèi)容的次序依次把文件連在一起形成一個大文本文件(其中的2進(jìn)制內(nèi)容使用 base64 編碼)。這樣處理后,xlsx 文件基本就是一個文本文件了。為了對版本管理工具友好,我對 xml 里的標(biāo)簽后增加了適當(dāng)?shù)姆中?。這樣處理以后,版本管理工具基本能識別出表格數(shù)據(jù)每個版本的差異。

第2步,可以動手消除一些對版本有影響卻對我們沒有意義的數(shù)據(jù)段。比如文件的最后修改時間、激活的單元格等。這樣、如果打開一個 excel 文件,保存后就不會產(chǎn)生差異。

那么,這是一個新的文件格式。怎么讓 Excel (或 wps 等兼容產(chǎn)品)打開它編輯呢?

雖然第一反應(yīng)是給 excel 寫一個插件。但我知道拿不是一兩個小時可以搞定的。所以我選擇了一條彎路。寫了一個腳本,可以生成一個臨時目錄/文件,在用戶想打開一個自定義格式文件時,先轉(zhuǎn)換為標(biāo)準(zhǔn)的 xlsx 臨時文件,讓關(guān)聯(lián)的軟件(excel 或 wps 等)編輯它。我們可以監(jiān)控這個文件的變更時間,來即使把臨時文件轉(zhuǎn)換回去。當(dāng)這個臨時文件可寫時,就表示已經(jīng)停止編輯這個文件了(excel 對打開的文件有文件鎖定)。這時,可以刪除臨時文件。

讓自定義文件格式關(guān)聯(lián)到這個腳本(我用 lua 編寫的十多行程序),策劃就可以直接雙擊自定義格式文件編輯了。


我天真的以為這可以解決大部分問題。但一試用就發(fā)現(xiàn)了問題。

策劃很容易在表格中嵌入 ole 對象,比如他們最喜歡的 visio 繪圖。對于 Ole 對象,是以2進(jìn)制對象形式存在文件里的。但是、visio 這種 COM 對象序列化后有同樣的問題:COM 對象其實(shí)也是一個包,而包里同樣有時間戳。這同樣會導(dǎo)致沒有修改過的對象每次持久化的結(jié)果是不同的。

解決這個問題并不復(fù)雜。我注意到 7zip 是可以打開 COM 對象文件的。在網(wǎng)上下載 7zip 的源代碼,很容易就寫出一個 COM 對象文件的解碼程序(大約幾十行 C 代碼)。我們要做的僅僅是把包里的文件時間抹掉即可。


如果進(jìn)行到這里就停住,我就不會認(rèn)為我在浪費(fèi)時間。起碼我解決了一個問題:沒有編輯的 excel 文件無論保存多少次都是無差異的。文件格式變成了文本,大致上可(被程序員)讀了。

可我貪心的想把問題解決的徹底點(diǎn),設(shè)計(jì)一個自己的格式會更好一些。就是一個比 csv 格式強(qiáng)一些的,保留了有限的版面格式信息(包括單元格的寬高、顏色、邊框等)的易于用文本編輯器編輯修改的文件。

我們可以把重要的信息都集中放在一起,不重要的格式信息放在另一部分。如果版本沖突,可以自由拋棄一個版本的格式信息而采用另一個版本的,這樣不至于損壞數(shù)據(jù)本身。

csv 格式設(shè)計(jì)的很糟糕(比如那個雙引號)。也不利于做版本比較合并(易于人編輯且容易合并的格式應(yīng)該是一個單元格一行,而不是按行保存。且 csv 會丟失重要的公式文本。

我設(shè)計(jì)的格式大致是這樣的:

[!sheet]
name:名字
[data]
A1:123
B1:234
C1:=SUM(A1,B1)
A2:Hello World
[value]
B1:357
[style]
...

在數(shù)據(jù)段保留有公式信息,把公式計(jì)算值放在 value 段。然后把版面信息全部放去 style 段。這樣,即使 value 和 style 段全破壞,也可以直接用 excel 打開修復(fù)整張表格。有了這種格式,程序員會更愿意用 vi 打開文件直接編輯,他們絕對不想再安裝 excel 的(尤其是對 linux 程序員)。找到想修改的單元格比 csv 文件更容易,也可以用最自然的 \ 轉(zhuǎn)義。和 excel 原始格式不同,我們不把 string 保存在表格數(shù)據(jù)之外的地方(excel 文件會生成一個叫 sharedstrings 的 xml 文件保存所有的字符串,在表格數(shù)據(jù)里只做 index 引用)。且把更容易引起沖突的公式計(jì)算值隔離開(這樣,不會因?yàn)橛腥朔謩e修改了 A1 和 B1 而導(dǎo)致 C1 的計(jì)算值在兩個版本中都不正確)。


當(dāng)我大致實(shí)現(xiàn)完后,我發(fā)現(xiàn)我嚴(yán)重低估了 excel 的板式描述的復(fù)雜性。要完全復(fù)原 excel 里看到的所有版式信息,需要記錄和分析很多的數(shù)據(jù)才能保持一致。全部實(shí)現(xiàn)完這樣所需要的時間遠(yuǎn)遠(yuǎn)超過我的預(yù)期,所以我不想在這上面浪費(fèi)時間了。

當(dāng)然,做這件事情讓我好好了解了一下 excel 文檔的結(jié)構(gòu)(大致瀏覽了 ISO-IEC-29500-1)。這里,我不想把半成品的工具開源。但可以提供一個副產(chǎn)品:我寫了一個小程序,可以把 excel 轉(zhuǎn)換為文本。這個工具可以幫助我們在命令行 grep excel 里的數(shù)據(jù)。需要這個工具的同學(xué)可以去我的 github 頁面看 xlsx2txt 這個項(xiàng)目 。


另一些值得記一下的東西:

上周我考察了許多 xlsx 的讀寫庫,有 go 版本的,python 版本的,lua 版本的。

lua 版本我找到一個叫 xlsxwriter 的東西。簡單的閱讀發(fā)現(xiàn),性能上有許多提高的空間。學(xué)會一門語言,不在于學(xué)會它的語法,而在于熟悉這種語言下解決問題的各種慣用法。比如 lua 的字符串處理簡單而強(qiáng)大,你需要熟悉它。

function Xmlwriter._escape_attributes(attribute)    
    attribute = string.gsub(attribute, '&', '&') 
    attribute = string.gsub(attribute, '"', '"')    
    attribute = string.gsub(attribute, '<', '<')  
    attribute = string.gsub(attribute, '>', '>')  
    return attribute        
end

這個函數(shù)有問題嗎?

這樣寫會更好一些:

local escape_attrib_tbl = {
    ['&'] = '&',
    ['"'] = '"',
    ['<'] = '<',
    ['>'] = '>',
}

function Xmlwriter._escape_attributes(attribute)    
    attribute = string.gsub(attribute, '[&"<>]', escape_attrib_tbl)
    return attribute
end

如果你想知道能好多少,可以跑一下測試 。

云風(fēng) 提交于 November 25, 2014 03:58 PM | 固定鏈接

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多