2017年2月20日 星期一

[python]用python重寫暗黑執行緒的 IpToCountry

因為臉書上有人分享這篇:

http://blog.darkthread.net/post-2017-02-13-in-memory-ip-to-country-mapping.aspx
用 100 行 C# 打造 IP 所屬國家快速查詢功能

然後有人說,是不是用 python 可以更少行,所以我就來試試會變成怎樣。

總之重寫文章裡的最後成果而已,儘量把文章中提到要防呆的也做進去,不然會不公平。

效能測試我因為不知道怎麼快速的弄出 1024 個隨機 ipv4 的列表,https://www.randomlists.com/ip-addresses 這裡給的看來是 ipv6,所以就偷懶不做。若是有人教我或是給我檔案,我可以按照文章中標準來測效能。我程式裡就隨便測 10 個 ip 的總時間,不然會因為時間太少得到 0.0。

import csv
import bisect

class IPCountryFinder:
    def __init__(self, filename):
        self.ranges = []
        self.IPRanges = []
        unknownCode = "--"
        self.CountryNames = {unknownCode:"Unknown"}
        self.IP2CN = {}
        self.count = 0
        with open(filename,'rb') as f:
            s = csv.reader(f, delimiter=',', quotechar='"')
            lastRangeEnd = 0
            for i in s:
                st = int(i[0])
                ed = int(i[1])
                cn = i[4]
                if lastRangeEnd > 0 and st > lastRangeEnd:
                    self.IP2CN[lastRangeEnd] = unknownCode
                    self.IP2CN[st-1] = unknownCode
                    self.count += 2
                self.IP2CN[st] = cn
                self.IP2CN[ed] = cn
                lastRangeEnd = ed + 1
                self.CountryNames[cn] = i[6]
            self.IPRanges = sorted(self.IP2CN.keys())


    def GetIPAddrDec(self, ipAddr):
        b = map(int,ipAddr.split('.'))
        return reduce(lambda s, x: s*256 + x, b)

    def GetCountryCode(self, ipAddr):
        a = self.GetIPAddrDec(ipAddr)
        idx = bisect.bisect_left(self.IPRanges, a) - 1
        if idx < 0:
            idx = -1
        return self.IP2CN[self.IPRanges[idx]]

    def ConvertCountryCodeToName(self, cnCode):
        if cnCode in self.CountryNames:
            return self.CountryNames[cnCode]
        return cnCode

    def GetCountryName(self, ipAddr):
        return self.ConvertCountryCodeToName(self.GetCountryCode(ipAddr))

def test():
    import time
    a = IPCountryFinder('IpToC.csv')
    m = time.time()
    p=a.GetCountryName('1.2.3.4')
    p=a.GetCountryName('1.2.3.8')
    p=a.GetCountryName('1.2.4.8')
    p=a.GetCountryName('1.4.3.8')
    p=a.GetCountryName('6.2.3.8')
    p=a.GetCountryName('1.2.3.34')
    p=a.GetCountryName('1.2.34.8')
    p=a.GetCountryName('1.34.3.8')
    p=a.GetCountryName('34.2.3.8')
    p=a.GetCountryName('18.2.3.8')
    print time.time() - m


test()

如果很想比有幾行的話,全部程式是 66 行,不含 test 的部份是 47 行。這樣比一定不是很公平,c# 光右邊大括號就很多行了。但是少很多型態宣告是比較不殺腦筋的。

2017年2月9日 星期四

electron 要用 async await 所需要的事

我現在用的是 “electron”: “^1.4.14”。沒辦法用 babel-preset-es2017。因為我用了 import {x} from “./y”,這句在 babel preset es2017 會直出,而 electron 不吃。

我會想用 preset 2017 是因為想要 asysnc await 句法,而 preset es2015 沒辦法翻。所以妥協的結果是,用 preset es2015 再加上 plugin

最後試成功的是這樣子的:

    "babel": "^6.5.2",
    "babel-core": "^6.21.0",
    "babel-loader": "^6.2.10",
    "babel-plugin-transform-runtime": "^6.22.0",
    "babel-preset-es2015": "^6.18.0",
    "babel-preset-es2017": "^6.22.0",
    "babel-preset-react": "^6.16.0",
    "babel-runtime": "^6.22.0",
    ```
雖然安裝了 `babel-preset-es2017`,但是在 webpack 設定還是用 `babel-preset-es2015`,在 loader 設定為:
    loaders: [
        {
            test: /\.js|\.jsx?$/,
            loader: 'babel-loader',
            include: [SRC_PATH],
            query: {
                presets: ['es2015', 'react'],
                plugins: ["syntax-async-functions","transform-regenerator","transform-runtime"]
            }
        },
        ...
    ]

“`

2017年2月8日 星期三

[nodejs] electron + drivelist + webpack 的小問題

為了要在 windows 系統列出磁碟機代號,用了 drivelist 這個套件,它是解譯系統指令的輸出來得到磁碟代號。

在沒有與 webpack 整合是沒問題,但是合在一起就出問題。

環境:electron, webpack, drivelist

第一個是解譯 json 的問題

當程式一引入 drivelist 就產生以下錯誤:

ERROR in ./~/drivelist/package.json
Module parse failed: C:\electron_project\electron\node_modules\drivel
ist\package.json Unexpected token (2:9)
You may need an appropriate loader to handle this file type.

因為它的 scripts.js 用到一句:

const debug = require('debug')(require('../package.json').name);

這個還好解決,首先是安裝 json-loader 使用指令 npm install --save-dev json-loader 然後在 webpack.config.jsloaders 裡加一個 { test: /\.json$/, loader: 'json-loader' } 這樣就解決了。不用改到程式。

第二個是執行路徑的問題

雖然解決了引入的問題,接下來要查詢的時候出現以下錯誤:

Uncaught Error: spawn \scripts\win32.bat ENOENT

原因是的 scripts.js 設定執行路徑是這樣做的:

const SCRIPTS_PATH = path.join(__dirname, '..', 'scripts');

在經過 webpack 打包之後, __dirname 在這裡已經變成根目錄 “/”,所以底下執行就找不到執行命令。另外,scripts 也沒有複製到目標目錄去。所以要改 webpack.config.jsscripts.js

webpack.config.jsplugins 底下的 CopyWebpackPlugin 裡面加一個 { from: path.resolve(node_modules_path, 'drivelist/scripts'), to: 'scripts' }

webpack.config.jsplugins 裡面加一個

new webpack.DefinePlugin({$dirname: '__dirname'})

這個 $dirname 就會是 main.js 執行的路徑,也就是原先 __dirname 的值。然後就要改 scripts.js 的程式碼,把 SCRIPTS_PATH 改成為:

const SCRIPTS_PATH = path.join($dirname, 'scripts')

改完之後,就可以由 electron 執行了。

註:這樣改法,node_modules 裡面的 drivelist 因為路徑關係,就沒辦法用命令列來執行了。