主要因為無聊,就寫了二支小程式,當然是用AMS寫的。
一支是可以抽號碼的程式,用於上課可以抽學生→有興趣的下載
另一支是重排學生的座位用的,讓他們覺得公平→有興趣的下載
2008年10月10日 星期五
2008年7月12日 星期六
FTP工作時的二種連接模式
FTP中的兩種工作方式 :
防火牆有很多種,其中有一些會禁止那些不是從內部網路IP發出的連接請求。而ftp協議是個很老的東東,沒有考慮這個問題。可以這樣簡單描述一般ftp的工作原理:
client對server發出請求:「嗨,我的xxPort正等著你傳送資料呢,sever聽到這個請求,就會建立一個連接,指向這個Port,並且開始傳送資料。這就回到了我們的問題,如果發出請求的client在防火牆的後面,而這個防火牆禁止外部IP建立連接,那麼當然server(在外部)不能建立與client的連接。
所以,過了幾年,人們修改了ftp協議以應付這種情況。這就是passive mode,工作原理如下:client向server發出passive命令,server回答說:「資料在xxPort等你,自己來拿。」 而這回則由client建立指向server的連接,從而取得資料。這就避免了防火牆的限制。
FTP是一種文件傳輸協議,它支持兩種模式,一種方式叫做Standard (也就是 Active,主動方式),一種是 Passive (也就是PASV,被
動方式)。 Standard模式 FTP的客戶端發送 PORT 命令到FTP server。Passive模式FTP的客戶端發送 PASV命令到 FTP Server。
接著下面,介紹一個ftp,這兩種方式的工作原理:
Standard模式FTP 客戶端首先和FTP Server的TCP 21Port建立連接,通過這個通道發送命令,客戶端需要接收資料的時候在這
個通道上發送PORT命令。 PORT命令包含了客戶端用什麼Port接收資料。在傳送資料的時候,服務器端通過自己的TCP 20Port發
送資料。 FTP server必須和客戶端建立一個新的連接用來傳送資料。
Passive模式在建立控制通道的時候和Standard模式類似,當客戶端通過這個通道發送PASV 命令的時候,FTP server開啟一個
位於1024和5000之間的隨機Port並且通知客戶端在這個Port上傳送資料的請求,然後FTP server 將通過這個Port進行資料的傳送,
這個時候FTP server不再需要建立一個新的和客戶端之間的連接。
現在的FTP軟體裡面包括在IE5以上的版本裡面也已經支持這兩種模式了。一般一些FTP客戶端的軟體就比較好設置了,一
般都有一個PASV的選項,比如CuteFTP,傳輸的方式都有Standard和PASV的選項,可以自己進行選擇;另外在IE裡面如果要設
置成PASV模式的話可以選中工具-Internet選項-進階-為FTP站點啟用資料夾視圖,否則就採用Standard模式。
很多防火牆在設置的時候都是不允許接受外部發起的連接的,所以FTP的Standard模式在許多時候在內部網路的機器通過防
火牆出去的時候受到了限制,因為從服務器的TCP 20無法和內部網路的客戶端建立一個新的連接,造成無法工作。當然也可以
設置成功,首先要建立一條規則就是允許內部的IP連接外部的IP的21Port;第二條就是禁止外部IP的TCP 20Port連接內部IP的
<1024的Port,這條是為了防止外部連接內部的一般Port;第三條驗證ACK是否等於1,這個的原理就參見TCP建立連接的三次握
手吧。所以如果安全的配置的話非常困難,這個時候就想起來了PASV模式,因為不用建立新的連接,所以也就不會涉及到後
面的問題了。但是管理員可能不想使用PASV模式,因為這個時候FTP Server會開放一個隨機的高Port,儘管在IIS4和IIS5里面Port
的範圍是1024-5000,但是許多FTP Server的Port範圍達到了1024-65535,這個時候在這個主動開放的隨機Port上是有完全的訪
問權限的,如果IIS也要設置成開放的Port為1024-65535,具體方法如下:
1. regedt32
2. 找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
3. 編輯-新增-數值
Value Name: MaxUserPort Data Type: REG_DWORD Value: 65534
所以如果遇到了有防火牆的話或者怕配置麻煩的話還是採用PASV模式比較好些,但是如果真的對安全的需求很高的話建
議採用Standard模式。
防火牆有很多種,其中有一些會禁止那些不是從內部網路IP發出的連接請求。而ftp協議是個很老的東東,沒有考慮這個問題。可以這樣簡單描述一般ftp的工作原理:
client對server發出請求:「嗨,我的xxPort正等著你傳送資料呢,sever聽到這個請求,就會建立一個連接,指向這個Port,並且開始傳送資料。這就回到了我們的問題,如果發出請求的client在防火牆的後面,而這個防火牆禁止外部IP建立連接,那麼當然server(在外部)不能建立與client的連接。
所以,過了幾年,人們修改了ftp協議以應付這種情況。這就是passive mode,工作原理如下:client向server發出passive命令,server回答說:「資料在xxPort等你,自己來拿。」 而這回則由client建立指向server的連接,從而取得資料。這就避免了防火牆的限制。
FTP是一種文件傳輸協議,它支持兩種模式,一種方式叫做Standard (也就是 Active,主動方式),一種是 Passive (也就是PASV,被
動方式)。 Standard模式 FTP的客戶端發送 PORT 命令到FTP server。Passive模式FTP的客戶端發送 PASV命令到 FTP Server。
接著下面,介紹一個ftp,這兩種方式的工作原理:
Standard模式FTP 客戶端首先和FTP Server的TCP 21Port建立連接,通過這個通道發送命令,客戶端需要接收資料的時候在這
個通道上發送PORT命令。 PORT命令包含了客戶端用什麼Port接收資料。在傳送資料的時候,服務器端通過自己的TCP 20Port發
送資料。 FTP server必須和客戶端建立一個新的連接用來傳送資料。
Passive模式在建立控制通道的時候和Standard模式類似,當客戶端通過這個通道發送PASV 命令的時候,FTP server開啟一個
位於1024和5000之間的隨機Port並且通知客戶端在這個Port上傳送資料的請求,然後FTP server 將通過這個Port進行資料的傳送,
這個時候FTP server不再需要建立一個新的和客戶端之間的連接。
現在的FTP軟體裡面包括在IE5以上的版本裡面也已經支持這兩種模式了。一般一些FTP客戶端的軟體就比較好設置了,一
般都有一個PASV的選項,比如CuteFTP,傳輸的方式都有Standard和PASV的選項,可以自己進行選擇;另外在IE裡面如果要設
置成PASV模式的話可以選中工具-Internet選項-進階-為FTP站點啟用資料夾視圖,否則就採用Standard模式。
很多防火牆在設置的時候都是不允許接受外部發起的連接的,所以FTP的Standard模式在許多時候在內部網路的機器通過防
火牆出去的時候受到了限制,因為從服務器的TCP 20無法和內部網路的客戶端建立一個新的連接,造成無法工作。當然也可以
設置成功,首先要建立一條規則就是允許內部的IP連接外部的IP的21Port;第二條就是禁止外部IP的TCP 20Port連接內部IP的
<1024的Port,這條是為了防止外部連接內部的一般Port;第三條驗證ACK是否等於1,這個的原理就參見TCP建立連接的三次握
手吧。所以如果安全的配置的話非常困難,這個時候就想起來了PASV模式,因為不用建立新的連接,所以也就不會涉及到後
面的問題了。但是管理員可能不想使用PASV模式,因為這個時候FTP Server會開放一個隨機的高Port,儘管在IIS4和IIS5里面Port
的範圍是1024-5000,但是許多FTP Server的Port範圍達到了1024-65535,這個時候在這個主動開放的隨機Port上是有完全的訪
問權限的,如果IIS也要設置成開放的Port為1024-65535,具體方法如下:
1. regedt32
2. 找到HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
3. 編輯-新增-數值
Value Name: MaxUserPort Data Type: REG_DWORD Value: 65534
所以如果遇到了有防火牆的話或者怕配置麻煩的話還是採用PASV模式比較好些,但是如果真的對安全的需求很高的話建
議採用Standard模式。
轉字串為table的函數
在程式中,你可以呼叫這個函數來將字串轉為table,當然您的字串是規格化的,比如您有一個字串→str=" coffee,spring,worm,tea"(是用逗號來分隔的字串內容),那麼你可以在程式中寫:tb_back = DelimitedStringToTable(str, ",");
你可以得到一個table名為tb_back,而其內容就是四個字串。
function DelimitedStringToTable(DelimitedString, Delimiter)
tbReturn = {};
local strWorking;
local nPos = nil;
local strData;
local nTableIndex = 1;
local nDelimiterLength = String.Length(Delimiter);
if(nDelimiterLength < 1)then
tbReturn[nTableIndex] = DelimitedString;
return tbReturn;
end
strWorking = DelimitedString;
nPos = String.Find(strWorking,Delimiter);
while(nPos ~= -1)do
strData = String.Left(strWorking,nPos-1);
tbReturn[nTableIndex] = strData;
nTableIndex = nTableIndex + 1;
local nLength = String.Length(strWorking);
strWorking = String.Right(strWorking,nLength - (nPos + (nDelimiterLength-1)));
nPos = String.Find(strWorking,Delimiter);
end
if(strWorking ~= "")then
tbReturn[nTableIndex] = strWorking;
end
return tbReturn;
end
你可以得到一個table名為tb_back,而其內容就是四個字串。
function DelimitedStringToTable(DelimitedString, Delimiter)
tbReturn = {};
local strWorking;
local nPos = nil;
local strData;
local nTableIndex = 1;
local nDelimiterLength = String.Length(Delimiter);
if(nDelimiterLength < 1)then
tbReturn[nTableIndex] = DelimitedString;
return tbReturn;
end
strWorking = DelimitedString;
nPos = String.Find(strWorking,Delimiter);
while(nPos ~= -1)do
strData = String.Left(strWorking,nPos-1);
tbReturn[nTableIndex] = strData;
nTableIndex = nTableIndex + 1;
local nLength = String.Length(strWorking);
strWorking = String.Right(strWorking,nLength - (nPos + (nDelimiterLength-1)));
nPos = String.Find(strWorking,Delimiter);
end
if(strWorking ~= "")then
tbReturn[nTableIndex] = strWorking;
end
return tbReturn;
end
Script-Tip011其他內建涵數
*dofile
AMS的程式引擎完全支援LUA程式程言,您可以將程式寫在外面存成*.lua(純文字檔),在您的
AMS程式中再以dofile將外部程式檔引入及執行。
語法:
dofile(lua檔路徑);
您可以用這種方式來幫助您整理及分類龐大的程式碼,每執行一次上述的語法,路徑中的lua
檔便會重新讀入並執行一次。
*require
與dofile一樣是引入外部的lua檔並執行,不同的地方在於,程式啟動後,以require引入的lua檔
只會執行一次,並存於記憶體中,就算您在某個event中再require一次,程式引擎也不會重讀
路徑中的lua檔。
語法:
require(lua檔路徑);
※require很適合您將變數的初始化或專案會用到的涵數寫在lua然後引入,這樣可避免大量的
變數式及functions寫在global script中,程式碼多到您自己眼花。
※只要您將您要引入的外部lua檔,放入專案的Modules目錄中,您在寫路徑的時候就不必寫完
整路徑,只要寫檔名就行了,這適用於dofile及require
例如:您寫了一個外部檔test.lua並放入專案的Modules目錄中,那您引入的語法:
dofile("test.lua");
或
require("test.lua");
*type
這個內建涵數是讓您用來取得一個變數所帶的資料型別的,語法:
my_var = type(變數);
依資料的不同,它會傳回六種字串名:"nil","number","string","boolean","table","function"
例如:
a = 898;
my_var = type(a); --my_var會得到"number"這個字串值
b = "coffeeworm";
two_var = type(b); --two_var會得到"string"這個字串值
※在程式實務中,有時您必須確認一下變數的型別是不是您所想要的,以避免出錯。
範例:
code:--------------------------------------------------------------------------------
function Max(n1, n2)
if (type(n1)~= "number") or (type(n2)~= "number") then --利用這行來判斷是否二個參數都是數字
Dialog.Message("錯誤","二個都必須輸入數字");
return nil;
else
if n1 > n2 then
return n1;
else
return n2;
end
end
end
AMS的程式引擎完全支援LUA程式程言,您可以將程式寫在外面存成*.lua(純文字檔),在您的
AMS程式中再以dofile將外部程式檔引入及執行。
語法:
dofile(lua檔路徑);
您可以用這種方式來幫助您整理及分類龐大的程式碼,每執行一次上述的語法,路徑中的lua
檔便會重新讀入並執行一次。
*require
與dofile一樣是引入外部的lua檔並執行,不同的地方在於,程式啟動後,以require引入的lua檔
只會執行一次,並存於記憶體中,就算您在某個event中再require一次,程式引擎也不會重讀
路徑中的lua檔。
語法:
require(lua檔路徑);
※require很適合您將變數的初始化或專案會用到的涵數寫在lua然後引入,這樣可避免大量的
變數式及functions寫在global script中,程式碼多到您自己眼花。
※只要您將您要引入的外部lua檔,放入專案的Modules目錄中,您在寫路徑的時候就不必寫完
整路徑,只要寫檔名就行了,這適用於dofile及require
例如:您寫了一個外部檔test.lua並放入專案的Modules目錄中,那您引入的語法:
dofile("test.lua");
或
require("test.lua");
*type
這個內建涵數是讓您用來取得一個變數所帶的資料型別的,語法:
my_var = type(變數);
依資料的不同,它會傳回六種字串名:"nil","number","string","boolean","table","function"
例如:
a = 898;
my_var = type(a); --my_var會得到"number"這個字串值
b = "coffeeworm";
two_var = type(b); --two_var會得到"string"這個字串值
※在程式實務中,有時您必須確認一下變數的型別是不是您所想要的,以避免出錯。
範例:
code:--------------------------------------------------------------------------------
function Max(n1, n2)
if (type(n1)~= "number") or (type(n2)~= "number") then --利用這行來判斷是否二個參數都是數字
Dialog.Message("錯誤","二個都必須輸入數字");
return nil;
else
if n1 > n2 then
return n1;
else
return n2;
end
end
end
Script-Tip012程式的除錯
*寫程式出現錯誤是常發生的事,當您的程式越寫越多,就愈容易犯邏輯錯誤,譬如重覆定義了變數,或符號打錯了,有時為了找一個小錯誤會花上您一天的時間。
AMS提供了很完善的除錯機制,讓您在程式執行錯誤時可以很快地找到錯誤的地方。所有在AMS中的Script都使用同一個除錯技術。
不過有一點是您必須了解的,在有些function,modules或script並不必然能除錯的,尤其是一些外界開發者所寫的用於AMS的程式,可能有它們自己的除錯方式,您必須要自行去讀取這些作者的文件,以了解如何發現錯誤。
※您所寫的程式可能千變萬化(程式語言彈性本來就很大),程式引擎通常只能捕捉明顯的寫法錯誤,其他您就不要太苛求了!
在AMS中,當您執行程式時,程式引擎所能捕捉的錯誤有二類: 語法錯誤和功能性錯誤
*語法錯誤
這類型的錯誤,通常是您的程式寫法有缺損或錯誤,在您預覽或建立(build)您的程式時會被捕捉到。例如:
foo =
上列的程式碼算是很明顯的式子不完整,定義了變數卻沒有指定值給它。
語法錯誤還有一種是您使用內建涵數,但卻沒有傳入規定的參數個數,不過這種錯誤在預覽及建立程式時,不會被捕捉到,直到執行期才會出現錯誤。
例如:
Dialog.Message("大家好");
如上式,使用訊息對話盒的涵數,至少要傳入二個參數(可參閱各action說明),但只傳入一個,在預覽或建立時都不會捕捉錯誤,因為程式引擎檢查了涵數名稱沒有錯,括號也沒少,也有";"當結尾,所以就不認為有錯,不過這行程式在執行時便會出現錯誤訊息說明少了參數。
※如果您參考一下使用手冊中,Dialog.Message的涵數模型如下:
code:--------------------------------------------------------------------------------
number Dialog.Message ( string Title,
string Text,
number Type = MB_OK,
number Icon = MB_ICONINFORMATION,
number DefaultButton = MB_DEFBUTTON1 )
--------------------------------------------------------------------------------
上述的涵數模式是述說Dialog.Message這個內建涵數需要5個參數傳入,前二個要字串,後三個要數字,不過後三個有預設值,所以您不寫的話就用預設值,前二個就不能省了。
*功能性錯誤
有時候您使用內建涵數去執行某些動作,但給的參數錯了(如打錯字),就會出現這類的錯誤,在建立程式時不會捕捉到錯誤(語法沒錯),而且在執行期,AMS預設不會跳出任何錯誤訊息,例如:
filecontents = TextFile.ReadToString("geskdf''' exist.txt");
要讀入某文字檔,但檔名打錯了,在執行期雖出錯,但程式不會有任何動靜,又如下:
Folder.Delete("c:\\temp");
這行程式是要刪掉c:\temp這個目錄,如果這個目錄不存在,那就發生錯誤,但這類錯誤也不會顯現出任何動靜。
想要捕捉這類的錯誤,您可以靠自己寫程式(利用內建的Debug Action)來捕捉,再自行決定要不要出現錯誤警告,或利用程式來避免。
*Debug Action
使用Application.GetLastError這個內建涵數來了解最近的一個Action的執行結果,用以判斷某一行程式的執行是否成功。
它的寫法公式:
last_error_code = Application.GetLastError();
範例:
code:--------------------------------------------------------------------------------
File.Copy("c:\\temp\\test.dat", "c:\\temp\\text2.dat");
error_code = Application.GetLastError(); --這行可以捕捉上一行的copy的結果的傳回代碼
if (error_code ~= 0) then
Dialog.Message("錯誤", "複製檔案錯誤!");
Application.Exit(); --離開程式(內建action)
end
--------------------------------------------------------------------------------
上面的程式很直覺,AMS的程式引擎規定,任一action的執行如果成功就會傳回 0,若出錯就會傳回某特定的數字代碼(像是1182之類的),所以上面的程式去判斷error_code 這個變數如果不是0,那顯然是copy那行出錯了,便跳出個錯誤訊息給使用者看,接著就退出程式。
※如果您想了解程式引擎所捕捉到的程式錯誤說明,可如下:
code:--------------------------------------------------------------------------------
File.Copy("c:\\temp\\test.dat", "c:\\temp\\text2.dat");
error_code = Application.GetLastError(); --這行可以捕捉上一行的copy的結果的傳回代碼
if (error_code ~= 0) then
Dialog.Message("錯誤", "複製檔案錯誤!說明:" .._tblErrorMessage[error_code]);
Application.Exit(); --離開程式(內建action)
end
--------------------------------------------------------------------------------
上面的式子中_tblErrorMessage[錯誤代碼],這是內建讓您查錯誤說明的Tables,這裡所介紹的是很實用的作法,通常都是用Application.GetLastError()搭配Dialog.Message可以讓您在寫程式時,自覺比較容易出錯的地方,在設計期寫進去幫自己除錯,沒問題了再移掉(放著也行)。
※如果您連續執行了幾個action其中一個出錯,但您的Application.GetLastError()寫在一個對的action後面,那便捕捉不到錯誤了,這點應注意!
*使用Debug.ShowWindow
AMS內建了一系統以Debug開頭的Action,可以讓您在程式執行時出現一個報告執行過程的視窗,這也是一個除錯的利器!
使用法:
Debug.ShowWindow(true);
執行這一行後便會跳出一個視窗,接下來用Debug.Print輔助,便可顯示之後所有程式運行的過程,幫自己檢查運用情形。
執行下一行, Debug視窗就會關掉。
Debug.ShowWindow(false);
若您使用Debug.SetTraceMode(true);這個action的話,便會出現詳細的程式運行情形,利用Debug.GetEventContext()這個內建涵數,您可以取得出錯點是在哪個Event,否則物件一多,涵數一多,您可能會找不到。
AMS提供了很完善的除錯機制,讓您在程式執行錯誤時可以很快地找到錯誤的地方。所有在AMS中的Script都使用同一個除錯技術。
不過有一點是您必須了解的,在有些function,modules或script並不必然能除錯的,尤其是一些外界開發者所寫的用於AMS的程式,可能有它們自己的除錯方式,您必須要自行去讀取這些作者的文件,以了解如何發現錯誤。
※您所寫的程式可能千變萬化(程式語言彈性本來就很大),程式引擎通常只能捕捉明顯的寫法錯誤,其他您就不要太苛求了!
在AMS中,當您執行程式時,程式引擎所能捕捉的錯誤有二類: 語法錯誤和功能性錯誤
*語法錯誤
這類型的錯誤,通常是您的程式寫法有缺損或錯誤,在您預覽或建立(build)您的程式時會被捕捉到。例如:
foo =
上列的程式碼算是很明顯的式子不完整,定義了變數卻沒有指定值給它。
語法錯誤還有一種是您使用內建涵數,但卻沒有傳入規定的參數個數,不過這種錯誤在預覽及建立程式時,不會被捕捉到,直到執行期才會出現錯誤。
例如:
Dialog.Message("大家好");
如上式,使用訊息對話盒的涵數,至少要傳入二個參數(可參閱各action說明),但只傳入一個,在預覽或建立時都不會捕捉錯誤,因為程式引擎檢查了涵數名稱沒有錯,括號也沒少,也有";"當結尾,所以就不認為有錯,不過這行程式在執行時便會出現錯誤訊息說明少了參數。
※如果您參考一下使用手冊中,Dialog.Message的涵數模型如下:
code:--------------------------------------------------------------------------------
number Dialog.Message ( string Title,
string Text,
number Type = MB_OK,
number Icon = MB_ICONINFORMATION,
number DefaultButton = MB_DEFBUTTON1 )
--------------------------------------------------------------------------------
上述的涵數模式是述說Dialog.Message這個內建涵數需要5個參數傳入,前二個要字串,後三個要數字,不過後三個有預設值,所以您不寫的話就用預設值,前二個就不能省了。
*功能性錯誤
有時候您使用內建涵數去執行某些動作,但給的參數錯了(如打錯字),就會出現這類的錯誤,在建立程式時不會捕捉到錯誤(語法沒錯),而且在執行期,AMS預設不會跳出任何錯誤訊息,例如:
filecontents = TextFile.ReadToString("geskdf''' exist.txt");
要讀入某文字檔,但檔名打錯了,在執行期雖出錯,但程式不會有任何動靜,又如下:
Folder.Delete("c:\\temp");
這行程式是要刪掉c:\temp這個目錄,如果這個目錄不存在,那就發生錯誤,但這類錯誤也不會顯現出任何動靜。
想要捕捉這類的錯誤,您可以靠自己寫程式(利用內建的Debug Action)來捕捉,再自行決定要不要出現錯誤警告,或利用程式來避免。
*Debug Action
使用Application.GetLastError這個內建涵數來了解最近的一個Action的執行結果,用以判斷某一行程式的執行是否成功。
它的寫法公式:
last_error_code = Application.GetLastError();
範例:
code:--------------------------------------------------------------------------------
File.Copy("c:\\temp\\test.dat", "c:\\temp\\text2.dat");
error_code = Application.GetLastError(); --這行可以捕捉上一行的copy的結果的傳回代碼
if (error_code ~= 0) then
Dialog.Message("錯誤", "複製檔案錯誤!");
Application.Exit(); --離開程式(內建action)
end
--------------------------------------------------------------------------------
上面的程式很直覺,AMS的程式引擎規定,任一action的執行如果成功就會傳回 0,若出錯就會傳回某特定的數字代碼(像是1182之類的),所以上面的程式去判斷error_code 這個變數如果不是0,那顯然是copy那行出錯了,便跳出個錯誤訊息給使用者看,接著就退出程式。
※如果您想了解程式引擎所捕捉到的程式錯誤說明,可如下:
code:--------------------------------------------------------------------------------
File.Copy("c:\\temp\\test.dat", "c:\\temp\\text2.dat");
error_code = Application.GetLastError(); --這行可以捕捉上一行的copy的結果的傳回代碼
if (error_code ~= 0) then
Dialog.Message("錯誤", "複製檔案錯誤!說明:" .._tblErrorMessage[error_code]);
Application.Exit(); --離開程式(內建action)
end
--------------------------------------------------------------------------------
上面的式子中_tblErrorMessage[錯誤代碼],這是內建讓您查錯誤說明的Tables,這裡所介紹的是很實用的作法,通常都是用Application.GetLastError()搭配Dialog.Message可以讓您在寫程式時,自覺比較容易出錯的地方,在設計期寫進去幫自己除錯,沒問題了再移掉(放著也行)。
※如果您連續執行了幾個action其中一個出錯,但您的Application.GetLastError()寫在一個對的action後面,那便捕捉不到錯誤了,這點應注意!
*使用Debug.ShowWindow
AMS內建了一系統以Debug開頭的Action,可以讓您在程式執行時出現一個報告執行過程的視窗,這也是一個除錯的利器!
使用法:
Debug.ShowWindow(true);
執行這一行後便會跳出一個視窗,接下來用Debug.Print輔助,便可顯示之後所有程式運行的過程,幫自己檢查運用情形。
執行下一行, Debug視窗就會關掉。
Debug.ShowWindow(false);
若您使用Debug.SetTraceMode(true);這個action的話,便會出現詳細的程式運行情形,利用Debug.GetEventContext()這個內建涵數,您可以取得出錯點是在哪個Event,否則物件一多,涵數一多,您可能會找不到。
Script-Tip010字串的操作
*連接字串:您可以利用 .. 這個運算子來連結二個字串,或者連結字串和數字,數字會轉為
字串。
code:--------------------------------------------------------------------------------
Fullname = "Coffee".." worm";
--這時Fullname的值為"Coffee worm"
d = 365;
text = "一年有"..d.."天";
--這時text的值為"一年有365天"
--------------------------------------------------------------------------------
※為了程式閱讀方便,.. 並不一定要緊靠著字串或變數
foo = "test"..var;
寫成
foo = "test" .. var;也可以
*字串的比較
在寫程式的時候,經常會需要比對二個字串是否一樣,AMS內定使用 == 這個符號作為比對
符號,中文的話沒什麼問題,比對英文時預設是大小寫視為不同的(二個英文字串必須大小寫
都一樣,字串才會相等),比對的結果為布林值。
範例:
code:--------------------------------------------------------------------------------
str_one = "CoffeeWorm";
str_two = "CoffeeWorm";
if str_one == str_two then
Dialog.Message("比對結果", "二個字串相同");
else
Dialog.Message("比對結果", "二個字串不同");
end
--------------------------------------------------------------------------------
上例會顯示二個字串相同
※如果您希望比對時不要管字母大小寫,您可以將欲比對的字串先套用內建涵數Strng.Upper(
轉為大寫)或String.Lower(轉為小寫)再進行比對。
範例:
code:--------------------------------------------------------------------------------
str_one = "coffeeworm";
str_two = "CoffeeWorm";
if String.Lower(str_one) == String.Lower(str_two) then
Dialog.Message("比對結果", "二個字串相同");
else
Dialog.Message("比對結果", "二個字串不同");
end
--------------------------------------------------------------------------------
上例會顯示二個字串相同
*計算字串長度(含有多少個字元)
只要使用內建涵數String.Length就可以傳回字串的長度了
code:--------------------------------------------------------------------------------
str_text = "我是 Coffeeworm";
num_chars = String.Length(str_text);
Dialog.Message("字元數", num_chars.."個字");
--------------------------------------------------------------------------------
※特別注意,中文字是雙位元碼,所以一個中文字會被算二個字元,字串中的空白鍵也會照
算,所以這個例子會回應 「15個字」
*搜尋字串
有時候您會需要在一串長字串中搜尋一下是否含有指定的字串,這只要使用內建涵數
String.Find就行了
code:--------------------------------------------------------------------------------
str_source_text = "Isn't it a wonderful day outside?";
str_search = "wonder";
found_pos = String.Find(str_source_text , str_search);
--如果有找到,會傳回找到字串的最左邊字元的位置,若找不到會傳回nil
if found_pos ~= nil then
Dialog.Message("找到了", "在第"..found_pos.."個字");
else
Dialog.Message("找不到", "不包含這個字串");
end
--------------------------------------------------------------------------------
※中文字也是一樣可以找的,AMS是完全相容於雙位元碼的
*置換指定字串
想在長字串中將所有的某個字換掉可以使用內建涵數String.Replace
code:--------------------------------------------------------------------------------
str_target = "There can be only one. Only one is 合格的!";
str_search = "合";
str_place = "正";
str_new = String.Replace(str_target, str_search, str_place)
Dialog.Message("新字串", str_new);
--------------------------------------------------------------------------------
上例中,str_new便是得出的新字串,原字串中的「格」已被換成「正」了
*分解字串
有時候,我們想取得長字串的某些位置的字元,有三個內建的涵數可以用
String.Left 從字串的左側取得指定數目的字元
String.Right 從字串的右側取得指定數目的字元
String.Mid 指定從字串的第幾字元開始取,取幾個字元
範例:
code:--------------------------------------------------------------------------------
source_text = "It really is good to see you again.";
strleft = String.Left(source_text, 13);
strright = String.Right(source_text, 18);
strmid = String.Mid(source_text, 14, 4);
--------------------------------------------------------------------------------
上例中:
strleft 會是"It really is ",strright 會是" to see you again.",而strmid 會是"good"
*將字串型的數字轉成數字型
當您擁有一個字串型的數字時,您必須利用內建涵數String.ToNumber將它變成數字型態,然
後用於判斷式時才不會出錯。
例如:
code:--------------------------------------------------------------------------------
age = "18";
if age < 31 then
Dialog.Message("test", "比31歲小")
end
--------------------------------------------------------------------------------
上述的程式執行到if age < 31 then這一行時,就會出現錯誤了,因為程式引擎會認為字串和數
值是不能比大小的!
※在實務中,何時會有得到字串型數字的情形呢?譬如,您放了一個InputBox讓使用者輸入數
字,然後您去取得使用者輸入的數字,因InputBox預設取回的是「字串」,所以您便得到了一
個字串型數字。
要解決這個問題,如下:
code:--------------------------------------------------------------------------------
age = "18";
if String.ToNumber(age) < 31 then
Dialog.Message("test", "比31歲小")
end
--------------------------------------------------------------------------------
※還有一個方法:AMS內定,當字串型數字做運算時,就會自動轉換型別了,所以您可以讓
您所握有的字串進行一個數學運算
code:--------------------------------------------------------------------------------
age = "18";
age = age + 0
if age < 31 then
Dialog.Message("test", "比31歲小")
end
字串。
code:--------------------------------------------------------------------------------
Fullname = "Coffee".." worm";
--這時Fullname的值為"Coffee worm"
d = 365;
text = "一年有"..d.."天";
--這時text的值為"一年有365天"
--------------------------------------------------------------------------------
※為了程式閱讀方便,.. 並不一定要緊靠著字串或變數
foo = "test"..var;
寫成
foo = "test" .. var;也可以
*字串的比較
在寫程式的時候,經常會需要比對二個字串是否一樣,AMS內定使用 == 這個符號作為比對
符號,中文的話沒什麼問題,比對英文時預設是大小寫視為不同的(二個英文字串必須大小寫
都一樣,字串才會相等),比對的結果為布林值。
範例:
code:--------------------------------------------------------------------------------
str_one = "CoffeeWorm";
str_two = "CoffeeWorm";
if str_one == str_two then
Dialog.Message("比對結果", "二個字串相同");
else
Dialog.Message("比對結果", "二個字串不同");
end
--------------------------------------------------------------------------------
上例會顯示二個字串相同
※如果您希望比對時不要管字母大小寫,您可以將欲比對的字串先套用內建涵數Strng.Upper(
轉為大寫)或String.Lower(轉為小寫)再進行比對。
範例:
code:--------------------------------------------------------------------------------
str_one = "coffeeworm";
str_two = "CoffeeWorm";
if String.Lower(str_one) == String.Lower(str_two) then
Dialog.Message("比對結果", "二個字串相同");
else
Dialog.Message("比對結果", "二個字串不同");
end
--------------------------------------------------------------------------------
上例會顯示二個字串相同
*計算字串長度(含有多少個字元)
只要使用內建涵數String.Length就可以傳回字串的長度了
code:--------------------------------------------------------------------------------
str_text = "我是 Coffeeworm";
num_chars = String.Length(str_text);
Dialog.Message("字元數", num_chars.."個字");
--------------------------------------------------------------------------------
※特別注意,中文字是雙位元碼,所以一個中文字會被算二個字元,字串中的空白鍵也會照
算,所以這個例子會回應 「15個字」
*搜尋字串
有時候您會需要在一串長字串中搜尋一下是否含有指定的字串,這只要使用內建涵數
String.Find就行了
code:--------------------------------------------------------------------------------
str_source_text = "Isn't it a wonderful day outside?";
str_search = "wonder";
found_pos = String.Find(str_source_text , str_search);
--如果有找到,會傳回找到字串的最左邊字元的位置,若找不到會傳回nil
if found_pos ~= nil then
Dialog.Message("找到了", "在第"..found_pos.."個字");
else
Dialog.Message("找不到", "不包含這個字串");
end
--------------------------------------------------------------------------------
※中文字也是一樣可以找的,AMS是完全相容於雙位元碼的
*置換指定字串
想在長字串中將所有的某個字換掉可以使用內建涵數String.Replace
code:--------------------------------------------------------------------------------
str_target = "There can be only one. Only one is 合格的!";
str_search = "合";
str_place = "正";
str_new = String.Replace(str_target, str_search, str_place)
Dialog.Message("新字串", str_new);
--------------------------------------------------------------------------------
上例中,str_new便是得出的新字串,原字串中的「格」已被換成「正」了
*分解字串
有時候,我們想取得長字串的某些位置的字元,有三個內建的涵數可以用
String.Left 從字串的左側取得指定數目的字元
String.Right 從字串的右側取得指定數目的字元
String.Mid 指定從字串的第幾字元開始取,取幾個字元
範例:
code:--------------------------------------------------------------------------------
source_text = "It really is good to see you again.";
strleft = String.Left(source_text, 13);
strright = String.Right(source_text, 18);
strmid = String.Mid(source_text, 14, 4);
--------------------------------------------------------------------------------
上例中:
strleft 會是"It really is ",strright 會是" to see you again.",而strmid 會是"good"
*將字串型的數字轉成數字型
當您擁有一個字串型的數字時,您必須利用內建涵數String.ToNumber將它變成數字型態,然
後用於判斷式時才不會出錯。
例如:
code:--------------------------------------------------------------------------------
age = "18";
if age < 31 then
Dialog.Message("test", "比31歲小")
end
--------------------------------------------------------------------------------
上述的程式執行到if age < 31 then這一行時,就會出現錯誤了,因為程式引擎會認為字串和數
值是不能比大小的!
※在實務中,何時會有得到字串型數字的情形呢?譬如,您放了一個InputBox讓使用者輸入數
字,然後您去取得使用者輸入的數字,因InputBox預設取回的是「字串」,所以您便得到了一
個字串型數字。
要解決這個問題,如下:
code:--------------------------------------------------------------------------------
age = "18";
if String.ToNumber(age) < 31 then
Dialog.Message("test", "比31歲小")
end
--------------------------------------------------------------------------------
※還有一個方法:AMS內定,當字串型數字做運算時,就會自動轉換型別了,所以您可以讓
您所握有的字串進行一個數學運算
code:--------------------------------------------------------------------------------
age = "18";
age = age + 0
if age < 31 then
Dialog.Message("test", "比31歲小")
end
Script-Tip009關於functions的使用
涵數是AMS中很有用的功能,分為內建涵數及自訂涵數,像Dialog.Message即為內建涵數,您只要提供所需的參數給它,它便為您呈現對話盒,AMS的內建涵數非常的多,只要熟悉他們的用法,您就可以做大部份的事了,當然您也可以自行定義您所需要的涵數,稱為自訂涵數,您需要按照下列的程式模型來定義:
function 涵數名(參數)
欲執行的程式
return 值;
end
以function關鍵字為首,以end結尾,參數是可有可無的,視您的需要,之中包著您的程式碼,若使用return關鍵字,則可讓涵數的執行有「傳回值」。
下面是一個簡單的自訂涵數範例:
code:--------------------------------------------------------------------------------
function Helloworld()
Dialog.Message("Welcome" , "Hello World !!!");
end
--------------------------------------------------------------------------------
上面是一個沒有參數,沒有傳回值的涵數,名為Helloworld(),您可以在任何地方呼叫它,只要在程式上打入下列這一行:
Helloworld();
如此就會呼叫這個涵數並執行其程式碼,會出現一個對話盒(標題為Welcome,文字為Hello World !!!)
※有一件要特別注意的是:在您呼叫某個涵數之前,必須確定它已經在記憶體中被初始化(即涵數已被執行過),所以我們通常會將自訂涵數集中寫在Globals Functions中,如此當程式啟動時便可確保這些涵數都已初始化了。
*涵數的傳入值(參數)
以下範例:
code:--------------------------------------------------------------------------------
--定義一個含有一個參數的涵數
function HelloWorld(a1)
Dialog.Message("title", a1);
end
--在其他地方呼叫它
HelloWorld("大家好!");
--------------------------------------------------------------------------------
如同上面的範例,執行的結果會出現一個對話盒,標題為title,文字列為 大家好!
當我們在定義涵數時,指明了這個涵數必須含有一個參數(傳入值),所以呼叫的時候就必須寫入您要傳入的字串或數字才不會出錯。上例在呼叫涵數時傳入了「大家好!」這個字串,意思便是以「大家好!」這個字串代表a1這個變數在涵數中的程式碼使用。
當然傳入值理論上可以定義無限多個,就看您的需要了,如下例:
code:--------------------------------------------------------------------------------
--定義一個含有2個參數的涵數
function HelloWorld(a1,a2)
Dialog.Message(a1, a2);
end
--在其他地方呼叫它
HelloWorld("大家好!","世界和平!");
--------------------------------------------------------------------------------
上面的程式碼定義了涵數有二個參數,所以呼叫時便傳入二個字串為參數(以逗點隔開),參數會依次序引入,用「大家好!」代表a1,用「世界和平!」代表a2
*涵數的傳回值
有時候我們寫一些涵數來做特定的事,並希望它將執行的結果(稱為傳回值)傳回給呼叫它的程式,以便繼續運用它執行的結果,如下例:
code:--------------------------------------------------------------------------------
--定義一個涵數,有2個參數,且有一個傳回值
function BMI(x,y)
bmi = y/(x*x);
return bmi;
end
--呼叫
my_var = BMI(1.65, 60);
--------------------------------------------------------------------------------
上面的程式中,特別注意的是傳入二個參數後,涵數內部會用這二個參數做運算,並用return關鍵字傳回計算的結果給呼叫它的式子,所以my_var的值便是計算後的結果。
※多重傳回值
code:--------------------------------------------------------------------------------
--定義一個涵數,有2個參數,且有2個傳回值
function sortNumbers(number1,number2)
if number1 <= number2 then
return number1, number2;
else
return number2, number1;
end
end
--呼叫
first_num, second_num = sortNumbers(number1,number2);
--------------------------------------------------------------------------------
注意上面程式中的呼叫語法,因為有二個傳回值,所以定義二個變數來接收
※重新定義涵數內含的方法很簡單,只要您在程式的其他地方用同一個名稱來定義涵數,那原本同名的涵數就會被覆蓋。
※將涵數放入Tables中
如果您想寫一個大型的程式,用到很多自訂的涵數,您可以考慮將涵數一個個放入Tables中,以便統一管理。
範例:
code:--------------------------------------------------------------------------------
--涵數一
function sun()
Dialog.Message("title", "我是太陽!");
end
--涵數二
function moon()
Dialog.Message("title", "我是月亮!");
end
--建一個空Tables
Hello = {};
--將涵數放入Tables中
Hello.s = sun;
Hello.m = moon;
--呼叫的方法
Hello.s();
Hello.m();
function 涵數名(參數)
欲執行的程式
return 值;
end
以function關鍵字為首,以end結尾,參數是可有可無的,視您的需要,之中包著您的程式碼,若使用return關鍵字,則可讓涵數的執行有「傳回值」。
下面是一個簡單的自訂涵數範例:
code:--------------------------------------------------------------------------------
function Helloworld()
Dialog.Message("Welcome" , "Hello World !!!");
end
--------------------------------------------------------------------------------
上面是一個沒有參數,沒有傳回值的涵數,名為Helloworld(),您可以在任何地方呼叫它,只要在程式上打入下列這一行:
Helloworld();
如此就會呼叫這個涵數並執行其程式碼,會出現一個對話盒(標題為Welcome,文字為Hello World !!!)
※有一件要特別注意的是:在您呼叫某個涵數之前,必須確定它已經在記憶體中被初始化(即涵數已被執行過),所以我們通常會將自訂涵數集中寫在Globals Functions中,如此當程式啟動時便可確保這些涵數都已初始化了。
*涵數的傳入值(參數)
以下範例:
code:--------------------------------------------------------------------------------
--定義一個含有一個參數的涵數
function HelloWorld(a1)
Dialog.Message("title", a1);
end
--在其他地方呼叫它
HelloWorld("大家好!");
--------------------------------------------------------------------------------
如同上面的範例,執行的結果會出現一個對話盒,標題為title,文字列為 大家好!
當我們在定義涵數時,指明了這個涵數必須含有一個參數(傳入值),所以呼叫的時候就必須寫入您要傳入的字串或數字才不會出錯。上例在呼叫涵數時傳入了「大家好!」這個字串,意思便是以「大家好!」這個字串代表a1這個變數在涵數中的程式碼使用。
當然傳入值理論上可以定義無限多個,就看您的需要了,如下例:
code:--------------------------------------------------------------------------------
--定義一個含有2個參數的涵數
function HelloWorld(a1,a2)
Dialog.Message(a1, a2);
end
--在其他地方呼叫它
HelloWorld("大家好!","世界和平!");
--------------------------------------------------------------------------------
上面的程式碼定義了涵數有二個參數,所以呼叫時便傳入二個字串為參數(以逗點隔開),參數會依次序引入,用「大家好!」代表a1,用「世界和平!」代表a2
*涵數的傳回值
有時候我們寫一些涵數來做特定的事,並希望它將執行的結果(稱為傳回值)傳回給呼叫它的程式,以便繼續運用它執行的結果,如下例:
code:--------------------------------------------------------------------------------
--定義一個涵數,有2個參數,且有一個傳回值
function BMI(x,y)
bmi = y/(x*x);
return bmi;
end
--呼叫
my_var = BMI(1.65, 60);
--------------------------------------------------------------------------------
上面的程式中,特別注意的是傳入二個參數後,涵數內部會用這二個參數做運算,並用return關鍵字傳回計算的結果給呼叫它的式子,所以my_var的值便是計算後的結果。
※多重傳回值
code:--------------------------------------------------------------------------------
--定義一個涵數,有2個參數,且有2個傳回值
function sortNumbers(number1,number2)
if number1 <= number2 then
return number1, number2;
else
return number2, number1;
end
end
--呼叫
first_num, second_num = sortNumbers(number1,number2);
--------------------------------------------------------------------------------
注意上面程式中的呼叫語法,因為有二個傳回值,所以定義二個變數來接收
※重新定義涵數內含的方法很簡單,只要您在程式的其他地方用同一個名稱來定義涵數,那原本同名的涵數就會被覆蓋。
※將涵數放入Tables中
如果您想寫一個大型的程式,用到很多自訂的涵數,您可以考慮將涵數一個個放入Tables中,以便統一管理。
範例:
code:--------------------------------------------------------------------------------
--涵數一
function sun()
Dialog.Message("title", "我是太陽!");
end
--涵數二
function moon()
Dialog.Message("title", "我是月亮!");
end
--建一個空Tables
Hello = {};
--將涵數放入Tables中
Hello.s = sun;
Hello.m = moon;
--呼叫的方法
Hello.s();
Hello.m();
訂閱:
文章 (Atom)