2012年12月20日 星期四

[Android]Market 進不去

送修的 HTC Desire 回來了,要開始安裝 App,於是點了 Market 按鈕,居然發生 404

訊息顯示:

The requested URL /intl/%locale%/mobile/android/market-tos.html was not found on this server. That’s all we know.

嗯。你不知道,我也不知道,然後呢?

花了一、兩個小時爬 google。找到以下連結。

http://forum.xda-developers.com/showthread.php?t=767992

做法很簡單,(1)先把語系切到英文。(2)按一下 Market。讓它跑過去。(3)再切回原來的語系。

好了。要怪誰呢?

2012年12月10日 星期一

[bigdata]小象幫幫忙,Hadoop 能幫什麼忙?

Hadoop 文獻探討

微軟巨量資料策略轉向,全面支援Hadoop

http://www.ithome.com.tw/itadm/article.php?c=77576

微軟表示,全面支援 Apache Hadoop,這消息說明了:

Hadoop 夠熱門,讓微軟無法抵擋。俗話說:「打不動,就加入」,現在微軟就加入了。
這不是第一個微軟採納的開放軟體。jQuery,在2008年加入微軟的 visual studio。
並且 ASP .NET MVC, ASP .NET AJAX 都將採用 jQuery 的技術。(舊消息)

最近熱門關鍵字,從雲端,換成 Big Data,有翻譯成「巨量資料」、有翻譯成「海量資料」,
到底這是什麼東西?為什麼幾乎每個介紹文章都會講到 Hadoop ?為什麼講雲端的時候有的也會提到 Hadoop?說來話長。

何謂Big Data?這問題目前沒辦法正面回答,因為個人認為現在這東西還在成長變化中,
也不是一個標準,所以無法用文字嚴格定義。網路上多數的文章會這麼說:

「因為雲端時代的來臨,資料量增加的速度非常快,所以需要一些新方法來應付這些資料。
巨量資料的特性有 3V,Volume(資料量大),Volecity(產生速度快),Variety(資料多樣性高)
在合理時間及資源下,巨量資料的技術才能夠應付這些需求。」

其實,這還是很模糊的描述。所以,只能知道,巨量資料並不是突然跑出來的東西。但「巨量資料」這個名詞是為了方便討論某些情況下,使用某些技術而產生的新名詞。巨量資料並不是每一個人或每一個企業都會面臨到的,有能力產生上面描述的 3V 的資料,才會有需要使用到巨量資料的技術。

因此,接下來,我們反過來,從被微軟納入解決方案核心的 Hadoop 來看。看 Hadoop 能夠解決什麼問題,至少可以了解,巨量資料大概是什麼東西。

Hadoop 提供的核心功能有二,名字叫 HDFS 以及 MapReduce。HDFS 是 Hadoop Distributed File System 的縮寫。MapReduce 則是 Hadoop 採用分散式計算的技術。也就是,它基本上要解決的問題有兩個,一個是儲存,一個是計算。

在 HDFS 的設計概念是這樣的。在一群電腦裡面,找一台電腦當老大,叫做 Master Node。它必須負責管理所有的電腦(包含自己)的資料的存放狀態、控制讀寫動作。所以裝了一個程式叫 Name Node 來做管理這件事。也裝了一個 Data Node 的程式來放資料。其他的電腦,叫做 Worker Node,裝了一個 Data Node 的程式來放資料。放資料的規則是,先把一個檔案,切成數個小塊(block),每塊為 64 MB,然後, Name Node 會讓檔案塊,分散到各個不同的 Data Node 去,而且,還會讓整個電腦叢集裡,每一個檔案塊,都有三份。若是 Name Node 發現檔案塊遺失或損壞,會開始尋找其他的 Data Node 上的副本來複製,使得整個電腦叢集維持每個檔案塊的數量都有三份。

從以上的設計慨念裡,可以知道:(1)它不怕單一資料存放區的硬體壞掉,壞掉照常工作,並自動回復、(2)它不怕單一檔案的大小超過一個磁碟區的大小、(3)資料存放區的大小,可以動態增加。(註1)

註1:http://www.classcloud.org/cloud/wiki/Hadoop_Lab8

要講 MapReduce 的設計概念,先講解一下,MapReduce 是什麼。在 functional language 裡面可常見到這個字。它其實是兩個英文字連起來的。也就是 Map 及 Reduce。
Map 是一種具有某種行為特色的函數,三言兩語說不清,我舉一個例子來說明。假設我有一個數列A,1~10,我想要得到另一個數列B,它裡面的數字是數列A的每個數字的 2 倍,相當於B = f(A),f(x): y=2x。撇開數學式不看。寫程式會這樣寫(C#):

int[] A = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] B = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; //初值化的數列
static int f(int x) { return 2 * x; }

for (int i = 0; i < A.Length; i++)
{
    B[i] = f(A[i]);
}

這程式裡面看到,數列A、數列B,還有函數 f。剩下來的是那個迴圈,它的動作是把每個數列A的數值,經過函數 f 得到的值,放到數列B 對應的位置。只要是可以完成這一類事情的函數,就叫做 Map 函數。這種函數的特色就是可以平行運算。以上述的例子,把 map 函數寫出來,程式再改寫一下會成為:

int[] A = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] B = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; //初值化的數列
static int f(int x) { return 2 * x; }

static int[] map(Func<int, int> _func, int[] _a)
{
    int[] _b = new int[_a.Length];
    for (int i = 0; i < _a.Length; i++)
    {
        _b[i] = _func(_a[i]);
    }
    return _b;
}

B = map( f, A)

Reduce 也是一種具備某種行為特色的函數。也是要舉一個例子來說明。假設一個數列A,1~10,要計算從 1 加到 10,程式會寫成:

int f2(int x, int y){ return x + y; }
int[] A = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int C = 0;
for (int i = 0; i < A.Length; i++)
{
    C = f2(C, A[i]);
}

其中,把迴圈部份依然可以改寫成函數,整個程式變成:

int f2(int x, int y){ return x + y; }
static int reduce(Func<int, int, int> _func, int[] _a)
{
    int _c = 0;
    for (int i = 0; i < _a.Length; i++)
    {
        _c = _func(_c, _a[i]);
    }
    return _c;
}

int[] A = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int C = 0;
C = reduce(f2, A);

凡是可以完成這一類工作的函數,都叫做 Reduce 函數。

所以,MapReduce 的技術,是已經在 Hadoop 中準備好可供分散式計算呼叫的 Map,以及 Reduce 的函數。而使用的人,要把計算的函數,寫成可被 Map 或 Reduce 呼叫。如此就可以利用分散式運算來縮短運算時間。例如,最簡單的分散式運算版的 Map,就不用迴圈,改成分給 10 個 Node 各自運算,等全部得到值,放入數列,再回傳數列,依照 Map 的概念來寫程式,就不怕平行化運算時會出問題了。

了解了 MapReduce 的意思之後,就可以看懂介紹 MapReduce 的示意圖了。要用 MapReduce 技術來計算,首先要把程式,寫成可被 Map,Reduce 使用的樣子。在叢集中,Master Node 還是當老大,負責管理所有的 Job,所以安裝一個叫 Job Tracker 的程式,而 Task Tracker 程式,則是裝在所有可以當計算的電腦裡。當運算開始,Master Node 分配 Map 函數要做的 Task 給 Worker Node,分配時會盡量交給資料在附近的 Node,如此會減少資料傳輸時間。Map 函數計算完的結果會存下來。等到的 Map 程式的結果出來,Reduce 程式可緊接著執行,即可將最終結果算出來。

總和來看,Hadoop 核心所提供的兩個功能,解決了檔案存放的問題、解決了系統擴張的問題、解決了系統備援的問題、解決了分散式運算的問題。依此來推測, Hadoop 可以被大家接受成為巨量資料的主流技術,巨量資料的主要需求及適用範圍即是這些東西。但是巨量資料的所有難題,用 Hadoop 就可以解決了嗎?其實並沒有。HDFS 與 MapReduce 只解決了基本的問題,我們可以從Apache 基金會 Hadoop 相關的其他專案的功能來了解 Hadoop 本身做不到的事。

(1) HBase,是專門用在 Hadoop 檔案系統上的資料庫系統。以此推知,Hadoop 沒有資料庫系統。

(2) Hive,是建置在 HDFS 上的分散式資料倉儲系統,可讓使用者以慣用的 SQL 語法,存取檔案中的大型資料集。以此推知,Hadoop 沒有 SQL 語法查詢功能。

(3) Pig,是 Script 語言,可用來撰寫 MapReduce 程式,不懂 Java 也可以使用這個語言開發 MapReduce 程式。以此推知,MapReduce 程式在 Hadoop 系統是用 Java 撰寫,要效能好,就要用 Java 來寫。

(4) ZooKeeper,是監控和協調 Hadoop 分散式運作的集中式服務,提供各個伺服器的配置和運作狀態資訊,用於提供不同 Hadoop 系統角色之間的工作協調。

(5) Mahout,立即可用的 MapReduce 函式庫,常見的數值分析方法,業集分類和篩選方法,都有對應的函數可以呼叫。

這些專案的功能,可以來解決巨量資料的其他需求,而這些需求,不是每個人都需要,所以不在核心功能之中。也可以想見巨量資料的需求,還是因人而異,而且差異很大。

希望這篇有把 Hadoop 能做什麼說清楚,也希望有把巨量資料的其中一個面向說清楚。

2012年11月22日 星期四

[aspx]HttpUtility.UrlDecode 與 Server.UrlDecode 真的不一樣

我以為我經過上次的錯誤,中文亂碼可以不會再出現在我的生活中。

直到我的膝蓋中了一箭。 (這裡)

找了一陣子,原來有人,以前的程式碼用了Server.UrlDecode

HttpUtility.UrlDecode 與 Server.UrlDecode 真的不一樣。

我寫了以下的程式來證明:

Function WriteOutput(ByVal str As String) As String
    Response.AppendHeader("Content-Type", "text/html;charset=UTF-8")
    Dim bs As Byte() = System.Text.Encoding.UTF8.GetBytes(str)
    Response.ContentEncoding = System.Text.Encoding.UTF8
    Response.OutputStream.Write(bs, 0, bs.Length)
    Return "OK"
End Function
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
    WriteOutput("System.Text.Encoding.Default.EncodingName:" + System.Text.Encoding.Default.EncodingName + "<br/>")
    WriteOutput("System.Text.Encoding.Default.WebName:" + System.Text.Encoding.Default.WebName + "<br/>")
    WriteOutput("HttpUtility.UrlDecode:" + HttpUtility.UrlDecode("%e4%b8%ad") + "<br/>")
    WriteOutput("Server.UrlDecode:" + Server.UrlDecode("%e4%b8%ad") + "<br/>")
End Sub

結果這就是不一樣:

image

附帶說明一下,這個現象不一定會在你的伺服器出現。在我的開發環境,如下。

image

我現在還找不到確切指出設定不同的地方。

我只看到兩個 EncodingName 所出現的字串是不同的。

據網路消息,說 Server.UrlDecode 是用系統編碼。可是我還沒辦法找到如何顯示現在系統編碼。

2012年11月20日 星期二

[aspx]不會用設定,不要怪微軟!(原題:好心的微軟幫了倒忙。中文亂碼解決實錄)

更新:要看最後解法,請直接跳最下面

有史以來解決中文亂碼最長的一次。從我2000年開始,踩進可怕的中文混沌中,
從沒有這麼令人生氣的一次了。

原因就是微軟幫倒忙!微軟好心幫忙但是我還是得告訴它我的 charset。

「絕聖棄智,民利百倍。絕仁棄義,民復孝慈。絕巧棄利,盜賊無有。」--老子

故事是這樣開始的。要求是:
「我需要用 post 方法,傳送一筆記錄的資料,而網頁伺服器接到之後,幫忙下 SQL 插入資料。」

你看多簡單的一句話!結果發生亂碼,沒關係,那我就寫個測試的程式,看是傳過去的字就亂了,還是 SQL 執行完才錯。

好,簡化過的發送端程式如下:

Function SendPutRequest(ByVal keyname As String, ByVal keyvalue As String)
    Dim uristr As String = "http://localhost:1490/WebSite1/poid_fail.aspx"
    Dim req As System.Net.HttpWebRequest = System.Net.HttpWebRequest.Create(uristr)
    req.Method = "POST"

    Dim param As String = "a=put"
    param += "&" & "key" & "=" & keyname
    param += "&" & "value" & "=" & keyvalue
    Dim bs As Byte() = Encoding.UTF8.GetBytes(param)
    req.ContentType = "application/x-www-form-urlencoded"
    req.ContentLength = bs.Length

    Using reqStream As System.IO.Stream = req.GetRequestStream()
        reqStream.Write(bs, 0, bs.Length)
    End Using
    Using wr As System.Net.WebResponse = req.GetResponse()
        Dim rs(wr.ContentLength) As Byte
        Dim expectcount As Integer = wr.ContentLength
        Dim readcount As Integer = 0
        Response.ContentEncoding = System.Text.Encoding.UTF8
        readcount += wr.GetResponseStream().Read(rs, 0, wr.ContentLength)
        Response.Write(Encoding.UTF8.GetChars(rs))
    End Using
    Return "OK"
End Function

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
    SendPutRequest("DataName", "中文測試")
End Sub

發送端,就只是一個假裝用 form 打出資料的 request。其中也很簡單的一個 keyname 跟 keyvalue,傳送至伺服端

而,接收端程式(簡化版)很快就寫好了:

<%@ Page Language="VB" %>

<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.OleDb" %>

<script runat="server">
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
        Dim act As String
        Dim insertSql As String = ""
        Try
            act = Request.Form("a")
            If act = "put" Then
                Dim keyname As String
                keyname = Request.Form("key")
                Dim keyvalue As String
                keyvalue = Request.Form("value")
                insertSql = "insert into [poidata] ($columns$) values ($values$)"
                insertSql = insertSql.Replace("$columns$", keyname).Replace("$values$", keyvalue)
                ExecuteSql(insertSql)
            End If
        Catch ex As Exception
            insertSql = ex.Message
        End Try
        Dim contentbody As String = insertSql
        Response.AppendHeader("Content-Type", "text/json")
        Response.ContentEncoding = System.Text.Encoding.UTF8
        Response.OutputStream.Write(Encoding.UTF8.GetBytes(contentbody), 0, System.Text.Encoding.UTF8.GetByteCount(contentbody))
    End Sub
</script>

在開發環境很正常。發送端與接收端都是在同一個電腦上,沒有問題。

image 

把接收端程式丟到真的伺服器去,就變亂碼!:

image

好。很正常的,因為值有中文,沒有 encode 所以變亂碼,我可以接受。
而我還特別把接收端的程式的 response,以 Encoding.UTF8.GetBytes 轉成 byte,然後用 write byte 方式,這是最保險,照字串byte回傳回來的保險方式。

那我 encode 吧。

把傳送端 encode。

param += "&" & "key" & "=" & HttpUtility.UrlEncode(keyname)

param += "&" & "value" & "=" & HttpUtility.UrlEncode(keyvalue)

結果長一模一樣。奇怪了。

image

那我接收端 decode 看看,結果一樣。

那來檢查兩邊的 default encoding 好了。
把接收端的 default encoding 回傳回來看看。

Dim contentbody As String = insertSql + "<br />" + System.Text.Encoding.Default.EncodingName

在開發環境,看起來是這樣

image

把程式丟到真的伺服器上,嗯,都是 big5 啊!

image

啊,等等,不對勁耶,為什麼真的伺服器不是寫「繁體中文 (Big5)」傳回來,而是用「Chinese Traditional (Big5)」這是搞什麼鬼?

後來,同事看我搞很久,決定傳授我妙方。
原本,在 IIS 的設定,我是使用虛擬目錄的方式,指到網站程式所放的目錄。
而他的經驗是,在 IIS 上,指定用「網路應用程式」
他也幫我弄好了。只是,網路位址會移動,而我,只是改別人的程式,不太想去改位址這種事。
如果,只想研究到這裡的人,就去改成「網路應用程式」試試吧。

因為沒辦法、因為不死心,我決定來試試強迫接到 encode 過的字串為目標,
想確定傳過去的 byte 沒有變動。
所以,接收端先不 decode,就接到什麼回寫什麼看看。
再來,因為傳送端我 encode 一次不夠,我再多 encode 一次看看。

param += "&" & "value" & "=" & HttpUtility.UrlEncode(HttpUtility.UrlEncode(keyvalue))

很賭氣的作法。UrlEncode 這種 encode 法,作兩次 encode,要得回原來的字,就要 decode 兩次。只要我拿到不是亂碼的字串,算清楚encode次數,就知道是不是 byte 被動到了。

image

很好,這是好的開始,這看來像是 encode 一次的字串。
那我接收端,就 decode 一次試試。

keyvalue = HttpUtility.UrlDecode(Request.Form("value"))

image

耶,看來好了。

而實際把組好的 SQL 字串執行下去,資料庫裡,也就正常的中文了。

回頭討論,我的傳送端 encode 兩次,我的接收端 decode 一次。
這代表,有某個地方幫我 decode 一次。而且在我的程式執行之前。
按照我的推斷,那一次的 decode 的時候,參考不知道哪裡的編碼,在那裡字串亂掉了。
明明就看不懂中文,還硬要轉,當然就搞亂了。
這從 default encoding name 可以看得出來,雖然都是 Big5,但是一個用中文表示,一個用英文表示,明白的指出,兩個的執行環境是不同的,而這個執行環境的不同,就會導致亂碼。
而我,用兩次 encode,讓我的字串被自動 decode (也就是第一次,不是我的程式做的) 之後,成為 encode 一次的狀態,這時是 ascii 的字串表示式,用這個字串,讓我的程式來 decode 回 .net 裡面的字串。由於資訊保持完整,所以可以完美地解回 .net 字串。問題才得以解決。
確切幫忙 decode 一次的程式我沒有找到,不過應該不脫離 iis,asp .net 這個範圍。
所以這次幫倒忙就算在微軟的頭上!

我想也許會有高手更了解 IIS 或 asp .net 的可以用更漂亮的方式解決這問題吧?
我的確是不了解所以硬幹啊。

更新:

因為無可避免地會被 decode 一次,所以指定正確 charset 一定是正解。指定的方式即在 request 的 Content-Type 裡。如下:

req.ContentType = "application/x-www-form-urlencoded;charset=UTF-8"

接收端程式,不用需 decode,微軟會按指定編碼幫你搞定。

它就是要幫你解,你只好一定要告訴他怎麼解。

2012年10月29日 星期一

[asp]使用VS2008,怎麼對 asp debug

如果使用VS2008,若直接對 asp 按下 debug,會出現

image

經過搜尋,解決方法是,

(1)設定iis,使得該頁可以直接使用。

image

(2)打開VS2008,開啟網站(檔案->開啟->網站),選擇網站所在的目錄

image

(3)附加至處理序(偵錯->附加至處理序),選擇 w3wp.exe (適用於 iis 6)

image

按下附加之後,VS2008,就會進入偵錯模式。

(4)瀏覽器跑一下 asp 網頁,方案總管會出現指令碼文件。

image

(5)雙擊 asp 文件,打開文件後,即可設中斷點,開始正式 debug。

image

 

結尾:

美中不足,不能邊改邊debug,你可以看到這文件是唯讀的。

[asp] 最近又用到 asp,幾個筆記需要記

判斷 access 資料庫某個欄位值為 null

我以為用 if cursor.fields(i).value = null then…可以判斷。我錯了。

要用 if isnull(cursor.fields(i).value) then … 才行。

 

到底權限怎麼開才能寫入?

首先,我的 os 是 windows 7,iis是 7 (應該吧…)

如果是 asp .net 的程式,IIS_USRS 這個使用者要開啟權限到寫入。

如果是 asp 的程式,IUSR 這個使用者要開啟權限到寫入。

 

如何存取utf-8 編碼的文字檔內容?

asp 直接 openTextFile 是不對的。它會按執行環境的預設值讀寫檔案。

我有改過CODEPAGE="65001",無效。

目前使用 ADODB.Stream 可。

有人提供小函數使用。

Sub Save2File (sText, sFile)
    Dim oStream
    Set oStream = CreateObject("ADODB.Stream")
    With oStream
        .Open
        .CharSet = "utf-8"
        .WriteText sText
        .SaveToFile sFile, 2
    End With
    Set oStream = Nothing
End Sub

Function ReadUtf8File(sFile)

    Set objStream = CreateObject("ADODB.Stream")
    With objStream
        .Charset = "utf-8"
        .Type=2
        .mode=3
        .Open
        .loadfromfile sFile
        ReadUtf8File=.readtext
        .Close
    End With
    Set objStream = Nothing

End Function

使用 javascript 的 escape 的文字,asp 怎麼解?

我基本上放棄了。試了幾次都不成功。這個問題很大,需要前後程式配合(html, javascript, asp)。因此會另外再寫詳細的文章。

簡單來說,我後來轉用 htmlencode。那 javascript 怎麼做 htmlencode 或 htmldecode?

以下有兩個小函數可以使用

function htmldecode(strHtmlEncode){
    var div =  document.createElement('div');
    div.innerHTML = strHtmlEncode;
    return div.innerText
}

function htmlencode(strHTML)
{
    var div = document.createElement('div');
    div.innerText = strHTML;
    return div.innerHTML;
}

asp 的程式,直接取值就好,不用解。

2012年10月23日 星期二

[html]用 javascript 查詢在網頁裡有多少定義過的 style?

原來,在 document.styleSheets 裡,存放了我們定義過的 style (class)。
也就是在 <style type="text/css"> 或 <link rel="stylesheet" type="text/css" href="src.css"> 這兩類地方寫的 style。

使用 javascript 可以取出內容。

var count = document.styleSheets.length;
    for (var y=0; y < count; y++)
    {
        var classes = document.styleSheets[y].rules || document.styleSheets[y].cssRules
        for(var x=0;x<classes.length;x++) {
            console.log("sheet"+y.toString()+" rule"+x.toString()+":"+classes[x].selectorText+":"+((classes[x].cssText) ? (classes[x].cssText) : (classes[x].style.cssText)));
        }
    }