最新消息

[公告2014/05/30] 如有需要將部落格中,任何一篇文章的程式碼使用在商業用途,請與我聯繫。

[公告2015/04/26] Line版的 iInfo程式與投資應用 群組已上線想加入的朋友們,請先查看 "入群須知" 再與我聯繫 Line : aminwhite5168,加入請告知身分與回答 "入群須知" 的問題。

[公告2018/04/22] 台北 Python + Excel VBA 金融資訊爬蟲課程,課程如網頁內容 金融資訊爬蟲班:台北班 Python 金融資訊爬蟲、EXCEL VBA 金融資訊爬蟲

[公告2019/01/08] 請注意:我再次重申,部落格文章的程式碼,是要提供各位參考與學習,一旦網頁改版請自行修改,別要求東要求西要我主動修改,你們用我寫東西賺錢了、交差了,請問有分我一杯羹嗎?既然賺錢沒分我,請問有什麼理由要求我修改,如果沒能力改,就花錢來找我上課。

[公告2019/12/01] 若各位有 Excel VBA 案子開發需求,歡迎與我聯繫,可接案處理。

2012年9月29日 星期六

C/C++ 取得檔案大小

<方法一> 利用 ftell
unsigned long GetFileLength1 (FILE * fileName)
{
     unsigned long pos = ftell(fileName);
     unsigned long len = 0;
   
     fseek ( fileName, 0L, SEEK_END );
     len = ftell ( fileName );
     fseek ( fileName, pos, SEEK_SET );
     return len;
}

<方法二>利用stat
unsigned long GetFileLength2 (char * fileName)
{
    struct stat buf;
    int i = stat ( fileName, &buf );

    if (i !=0)
       MessageBox(NULL,"ERROR for STAT","ERROR",0);

    return buf.st_size;
}

<方法三>利用filelength 必須include< fcntl.h>
原型: long filelength(int fd)
long GetFileLength3(char *fileName)
{
    int fd = open(fileName,O_RDONLY | O_BINARY);
    if(fd == -1 ) return -1;
    long lsize = filelength(fd);
    close(fd);
    return lsize;
}

<方法四> 利用Windows API GetFileSize & GetFileSizeEx
Large File Size 請使用 GetFileSizeEx函式。

<方法五>利用Windows API  FindFirstFile()
/// For Windows:
#include <windows.h>
double dblFileSize(const char* fname)
{
  if (!fname && !*fname)
     return 0.0;
  HANDLE h;
  WIN32_FIND_DATA info;

  if ((h=FindFirstFile(fname,&info)) 
      != INVALID_HANDLE_VALUE)
  {
     FindClose(h);
     if ((info.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) == 0) // Is it a file?
     {
        union
        {
          struct { DWORD low, high; } lh;
          __int64 size; // MS large int extension
        } file;
        file.lh.low = info.nFileSizeLow;
        file.lh.high= info.nFileSizeHigh;
        return file.size; // will be casted to double
     }
     // It's a directory, not a file
  }
  return 0.0; // No such name.
}
方法 1~3都無法處理大檔案,要注意~
資料來源參考:這裡

識別檔案編碼UTF8與ANSI

開啟一個CSV的文字檔

以二進制的方式開起該文字檔

從上圖可以看出二進制檔的檔頭內容是EF BB BF,此即為UTF8編碼格式
若是在二進制檔的檔頭看不到EF BB BF,而是直接看到檔案的資料,則該檔即為ANSI編碼
其他的編碼原則如下表:

編碼
表示 (十六進位)
表示 (十進位)
UTF-8
EF BB BF
239 187 191
UTF-16(大端序)
FE FF
254 255
UTF-16(小端序)
FF FE
255 254
UTF-32(大端序)
00 00 FE FF
0 0 254 255
UTF-32(小端序)
FF FE 00 00
255 254 0 0
UTF-7
2B 2F 76和以下的一個位元組:[ 38 | 39 | 2B | 2F ]
43 47 118和以下的一個位元組:[ 56 | 57 | 43 | 47 ]
en:UTF-1
F7 64 4C
247 100 76
en:UTF-EBCDIC
DD 73 66 73
221 115 102 115
en:Standard Compression Scheme for Unicode
0E FE FF
14 254 255
en:BOCU-1
FB EE 28 及可能跟隨著FF
251 238 40 及可能跟隨著255

表格參考資料:維基

Batch教學--For使用

今天介紹使用Batch中的For指令

FOR /R – 列舉目前目錄下的全部子目錄名所有檔案
FOR /R [[drive:]path] %variable IN (set) DO command [command-parameters]
FOR /R %i IN (*) DO echo %i
FOR /D – 列舉目前目錄下的子目錄名
FOR /D %variable IN (set) DO command [command-parameters]
FOR /D %i IN (*) DO echo %i
FOR /L – 以增量形式從開始到結束一個數字序列
FOR /L %variable IN (start,step,end) DO command [command-parameters]
set sum=0
FOR /L %i IN (1, 1, 100) DO set /a sum=sum+%i
echo %sum%
>> 5050
FOR /F – 做在一個檔案裡的指令
FOR /F ["options"] %variable in (file-set) do command [command-parameters]
FOR /F ["options"] %variable in ("string") do command [command-parameters]
FOR /F ["options"] %variable in ('command') do command [command-parameters]
file-set為一個或多個檔案名稱。/f 分析每個檔案的每一行,跳過空白行。"options" 關鍵字:
  1. eol=c 用來決定斷行符號,預設為 \n,但可換成其他字元(其中 c 只能是一個字元)
  2. skip=n 用來決定要先跳過幾層迴圈(也就是跳過前面幾行的意思)
  3. delims=xxx 用來決定欄位的分隔字元,預設為空白與TAB符號,並可自訂多個字元,多個字元時需用逗號(,)區隔開。
  4. tokens=x,y,m-n用來決定一次要取出幾個欄位,第一個欄位會存放在第一個自動變數,第二個欄位會存放在第二個自動變數裡,依此類推,說明如下。
  • 第一個變數為%i並設並tokens=1-3,則後面將得到的變數為%%i、%%j、%%k
Set a=aaa_bbb_111-222-333
For /F "tokens=1-3 delims=_" %%i in ("%a%") do echo %%i  %%j  %%k
>> aaa  bbb  111-222-333
  • 第一個變數為%i並設並tokens=2,3,則後面將得到的變數為%%i、%%j
Set a=aaa_bbb_111-222-333
For /F "tokens=1-3 delims=_" %%i in ("%a%") do echo %%i  %%j
>> bbb  111-222-333


2012年9月24日 星期一

Batch教學--變數使用

環境變數的使用,可以做到跟VC++中CString的用法類似,各位可以參考如下。
set A=1234567890
set B=%A%

REM 從第3 字元後開始取2個字元
set B=%A:~3,2%
echo %B%
>>45

REM 從前面開始取5個字元
set B=%A:~,5%
echo %B%
>>12345

REM 從第3字元後開始取到後面。
set B=%A:~3%
echo %B%
>>4567890

REM 從後面開始取前5個字元。
set B=%A:~-5%
echo %B%
>>67890

REM 從開頭取到最後2個字元不取。
set B=%A:~0,-2%
echo %B%
>>12345678

REM 字元中有5的取代成sss。
set B=%A:5=sss%
echo %B%
>>1234sss67890
其他常用系統變數
系統變數
描述
%n(%0%1 ~ %9)
外部變數輸入所使用的變數代稱,如C/C++Argv
%CMDEXTVERSION%
展開為目前的命令處理擴充功能的版本號碼
%CMDCMDLINE%
處理目前命令提示字元視窗命令的cmd.exe的完整路徑
%CD%
目前的工作資料夾
%DATE%
目前的系統日期
%ERRORLEVEL%
最近執行過的命令的錯誤碼;非零的值表示發生過的錯誤碼
%ProgramFiles%
應用程式目錄,預設是C:\Program Files
%ProgramFiles(x86)%
應用程式目錄,預設是C:\Program Files(x86)
%Path%
執行檔的搜尋路徑
%RANDOM%
顯示032767之間的十進位整數亂數
%SystemRoot%
系統根目錄,預設是C:\WINNTC:\WINDOWS
%SystemDirectory%
系統目錄,預設是C:\WINNT\System32C:\WINDOWS\System32
%TIME%
目前的系統時間
%Temp%%Tmp%
暫存檔目錄
%USERNAME%
使用者帳號名稱
%WINDIR%
Windows目錄,預設是C:\WINNTC:\WINDOWS

Python 圖表工具


如果要顯示結點關係圖,可選擇歷史最悠久的是Graphviz 透過pyDot生成DOT檔。
如果是需要生成統計圖,則pyChart可能更加適合。
如果要做三維的圖形,可考慮Dislin
如果要一個歷史最悠久的,最受歡迎的,應該選擇Gnuplot可透過Gnuplot.py來訪問。
如果需要一個最前衛的,操作類似matlab的,則應該選擇matplotlib
如果你想從最底層來,而且喜歡SVG格式,則可看看SVGDraw吧。
生成各種Python圖表chardir
模組的高品質圖形輸出Python Matplotlib。
PyX - Python graphics package

Eexcel VBA資料類型

一、資料類型
在Access中可用的資料類型分為3種:標準型、自定義型、物件型。
1.標準型,共有7種。
  (1) 整數:尾碼符為"%"。
  (2) 長整數:尾碼符為"&"。
  (3) 單精確度:尾碼符為"!"。
  (4) 雙精度:尾碼符為"#"。
  (5) 貨幣:尾碼符為"@"。
  (6) 字串:尾碼符為"$"。
  (7) 變體:可用於任何資料類型。

二、變數定義
自定義型:它使用關鍵字Type來定義VBA產生新的資料類型,類似C/C++的結構,定義如下:
Type Point
   X as Integer
   Y as Integer
End Type

即定義了一個點(point )的資料結構,宣告與使用變數方式如下:
Dim pCurPoint as Point
PCurPoint.X=10
PCurPoint.Y=10

三、變數的作用域

2012年9月20日 星期四

Batch教學--IF指令

條件判斷
IF [NOT] EXIST filename command
IF [NOT] EXIST filename (command) ELSE (command)
IF [/I] [NOT] item1==item2 command
IF item1 Compare-op item2 command
IF item1 Compare-op item2 (command) ELSE (command)
IF DEFINED variable command

Compare-op:
EQU – 等於
NEQ – 不等於
LSS – 小於
LEQ – 小於或等於
GTR – 大於
GEQ – 大於或等於

大小相同
if "ABC"=="ABC" echo 大小寫相同

區分大小寫
if not "ABC"=="abc" echo 大小寫不同

不區分大小寫
if /I "ABC"=="abc" echo 兩者相同相等

判斷檔案是否存在
if NOT EXIST C:\text.txt echo c:\text.txt檔案不存在

如果環境變數是空,可在變數外加上特殊符號,以防止錯誤發生
IF "2" == "15" echo "bigger" 

確認環境變數是否存在
IF DEFINED a echo 123
set a=5
IF DEFINED a echo 123
set a=
IF DEFINED a echo 123

2012年9月18日 星期二

Batch教學--Set指令

顯示、計算環境變數
Set /a [expression]
Set /p variable=string
Set /p variable=<file

列出系統的所有環境變數
Set

列出系統以'P'開頭環境變數
Set P

將Amin加入USER環境變數
Set USER=Amin 

清除環境變數
Set USER=

將C:\test加入PATH環境變數
Set PATH=C:\test;%PATH%

環境變數的計算
Set /a a+=1 
運算子 (依照運算優先順序由高到低排列)
()                  - 組成一群
! ~ -               - 一元運算符
* / %               - 數字運算元
+ -                 - 數字運算元
<< >>               - 邏輯位移
&                   - 位元運算 and
^                   - 位元運算 exclusive or
|                   - 位元運算 or
= *= /= %= += –=    - 指定
&= ^= |= <<= >>=
,                   - 運算式分隔字元
各進制計算
SET /A Result=020          REM 八進制進算 
SET /A Result=16           REM 十進制進算
SET /A Result=0x10         REM 十六進制進算
SET /A Result=010+0x20-24  REM 混合進算

等待使用者輸入數值或字串再設定至環境變數a
Set /p a=

將檔案內容導入環境變數a
Set /p a=<file.txt

2012年9月17日 星期一

使用RW讀寫Smbus device

在網路上介紹透過RW或是RU來操作SMBus的資料很少,好不容易才找到可以參考的且實用的網頁,不過說明的又很有限,所以在此完整的說明操作流程。

1.下載安裝RWEverything,開啟RW中PCI Device table列表,找Smbus controller,如Intel PCH SMbus controoler: D31:F3,將預設以1 Byte顯示方式改為 4 Byte。
2.找到SMB_BASE的位置 , 如: offset 20h~23h。


3.右鍵點選”Open IO Space”, 開啟對應的IO Space table。


4.以下即開始介紹藉由IO操作SMBus讀寫Device的方法
(1)我們先來看幾個常用的暫存器。

(2)下面分別介紹Byte read、Word read、Block read讀取資料的步驟。

Byte read:
Step1:
清除暫存器狀態:SMB_BASE + 0x00 → Host Status。
填入0xFE清除暫存器狀態,數值變化的狀態0x40 → 0xFE → 0x00 → 0x40。

Step2:
填入裝置在SMBus的位址:SMB_BASE + 0x04 → Slave Address。
如Memory Slave Address 為0xA0~0xA6,Bit0=1代表read,所以填入0xA0 + 1 = 0xA1。

Step3:
指定欲讀取裝置內資訊擺放的起點:SMB_BASE + 0x03 → HOST Command。
讀取裝置內資訊的起始位置,從頭開始讀取,這裡先填0x00。

Step4:
選擇執行的方式與啟動:SMB_BASE + 0x02 → Host Control。
填入0x48,Bit6=1代表開始執行、Bit2~4代表選擇要使用的執行方式。



Step5:
讀取資料:SMB_BASE + 0x05 → Host Data0。
返回裝置內指定位置的資訊。



Word Read:
Step1:
清除暫存器狀態:SMB_BASE + 0x00 → Host Status。
填入0xFE清除暫存器狀態,數值變化的狀態0x40 → 0xFE → 0x00 → 0x40。

Step2:
填入裝置在SMBus的位址:SMB_BASE + 0x04 → Slave Address。
如Memory Slave Address 為0xA0~0xA6,Bit0=1代表read,所以填入0xA0 + 1 = 0xA1。

Step3:
指定欲讀取裝置內資訊擺放的起點:SMB_BASE + 0x03 → HOST Command。
讀取裝置內資訊的起始位置,從頭開始讀取,這裡先填0x00。

Step4:
選擇執行的方式與啟動:SMB_BASE + 0x02 → Host Control。
填入0x4C,Bit6=1代表開始執行、Bit2~4代表選擇要使用的執行方式。
SMB_BASE + 0x00 數值變化的狀態:0x40 → 0x42。
SMB_BASE + 0x02 數值變化的狀態:0x4C → 0x0C。



Step5:
讀取資料:SMB_BASE + 0x05 → Host Data0。
返回裝置內指定第一位置的資訊。

Step6:
讀取資料:SMB_BASE + 0x06 → Host Data1。
返回裝置內指定第二位置的資訊。


Block read:
Step1:
清除暫存器狀態:SMB_BASE + 0x00 → Host Status。
填入0xFE清除暫存器狀態,數值變化的狀態0x40 → 0xFE → 0x00 → 0x40。

Step2:
填入裝置在SMBus的位址:SMB_BASE + 0x04 → Slave Address。
如Memory Slave Address 為0xA0~0xA6,Bit0=1代表read,所以填入0xA0 + 1 = 0xA1。

Step3:
指定欲讀取裝置內資訊擺放的起點:SMB_BASE + 0x03 → HOST Command。
讀取裝置內數值的起始位置,從0x01頭開始讀取。

Step4:
選擇執行的方式與啟動:SMB_BASE + 0x02 → Host Control。
填入0x54,Bit6=1代表開始執行、Bit2~4代表選擇要使用的執行方式。
SMB_BASE + 0x00 數值變化的狀態:0x40 → 0xC1。
SMB_BASE + 0x02 數值變化的狀態:0x54 → 0x14。


Step5:
取得資料的長度:SMB_BASE + 0x05 → Host Data0。
回傳數值位元組數,該值也是愈讀取裝置啟始點的數值資料。


Step6:
SMB_BASE + 0x07 → HOST_BLOCK_DB。
返回block讀取的第一個位元組,該值為Step3指定起點 + 1數值。


Step7:
取得Block read全部數值:SMB_BASE + 0x00 → Host Status。
寫0xFF清除狀態位元,表示該筆資料已取走,通知SMBus傳回下一筆資料至offset 0x07的位置。
數值變化的狀態:0xC1 → 0xFE → 0x81 → 0xC1。
最後一筆資料顯示狀態:0xC1 → 0xFE → 0x82 → 0xC2。

以此類推,重複做Step7動作直到這個block讀完。

資料來源:怎樣在RU下讀寫Smbus device

2012年9月15日 星期六

使用Internet Explorer object下載每日上櫃交易明細


抓取上櫃交易明細需要使用POST來傳遞參數,在VBA中能使用的方法就是使用XmlHttp。
不過我們想嘗試使用其他Excel VBA抓取網頁數據的方法來抓取,於是選用了Internet Explorer物件來試著抓取上櫃交易明細。

如下使用Internet Explorer物件抓取上櫃交易明細
Option Explicit
Sub ie_test_Navigate_PostData()
    Dim objIE As Object
    Dim bPostData() As Byte
    Dim xlURL As String
    Dim UrlHeaders As String

    xlURL = "http://www.gretai.org.tw/ch/stock/aftertrading/broker_trading/download_ALLCSV.php"
    UrlHeaders = "Content-Type: application/x-www-form-urlencoded" & vbCrLf
    bPostData = StrConv("stk_date=1010914&curstk=5349", vbFromUnicode)
    
    Set objIE = CreateObject("InternetExplorer.Application")
    With objIE
        .Visible = True
        .Navigate xlURL, , , bPostData, UrlHeaders
        While .ReadyState <> 4 Or .Busy = True
            DoEvents
        Wend
    End With
End Sub
雖然可以透過InternetExplorer物件來抓取上櫃交易明細,但當順利取得檔案時卻因需要點選下載檔案對話框,因此這裡可能就無法使用InternetExplorer物件抓取。


對於Navigate進階的使用可參考這裡

VC++, JavaScript的日期函數使用

因工作要使用VC++時間處理函數,加上近期在研究Highstock也需要使用到JavaScript時間處理函數,所以將兩者處理時間的方法一起列出

以下是一個使用VC++時間處理類別CTime的範例
int main(int argc, TCHAR* argv[])
{
 CTime cTime = CTime::GetCurrentTime(); 
 printf("Year : %d\n", cTime.GetYear());
 printf("Month : %d\n", cTime.GetMonth());
 printf("Day : %d\n", cTime.GetDay());
 printf("Hour : %d\n", cTime.GetHour());
 printf("Minute : %d\n", cTime.GetMinute());
 printf("Second : %d\n", cTime.GetSecond());
 printf("Time : %d\n", cTime.GetTime());
 return 1;
}
執行結果

以下是一個使用JavaScript時間處理函數的範例
<html>
  <head>
  <title>JavaScript Test Date Function</title>
  <script type="text/javascript">
    function run() {
      var CurrentTime = new Date();
      document.write("Year : " + (CurrentTime.getYear() + 1900) + "<br>")
      document.write("Month : " + (CurrentTime.getMonth() + 1) + "<br>")
      document.write("Date : " + CurrentTime.getDate() + "<br>")
      document.write("Hours : " + CurrentTime.getHours() + "<br>")
      document.write("Minutes : " + CurrentTime.getMinutes() + "<br>") 
      document.write("Seconds : " + CurrentTime.getSeconds() + "<br>")
      document.write("Time : " + CurrentTime.getTime() + "<br>")
    }
  </script>
  </head>
  <body onload = run();>
  </body>
</html>
執行結果

兩者的差異

VC++
JavaScript
Time
Second
Millisecond
Year
不加1900
需加1900
Month
1為起點,範圍1~12
0為起點,範圍0~11

2012年9月8日 星期六

使用Stored Procedure取得SQLyog的版本

透過SQLyog寫一個Stored Procedure(預存程序),來取得SQLyog版本資訊
DELIMITER //
CREATE
    PROCEDURE `stock`.`GetDBVersion`(OUT VERSION VARCHAR(60),OUT DAY VARCHAR(60))
    LANGUAGE SQL NOT DETERMINISTIC
    CONTAINS SQL SQL SECURITY DEFINER
    BEGIN
        SELECT VERSION() INTO VERSION;
        SELECT CURDATE() INTO DAY;
    END//
DELIMITER ;

取得SQLyog版本資訊前先初始化GetDBVersion預存程序
CALL GetDBVersion(@version,@day);

再下SQL指令取得版本
SELECT @version,@day;

執行結果

2012年9月7日 星期五

使用Stored Procedure列出交易日

透過SQLyog寫一個Stored Procedure(預存程序),來產生一個連續日期的序列
DELIMITER $$
CREATE
    PROCEDURE `stock`.`gen_time_sequence`(startstamp TIMESTAMP, endstamp TIMESTAMP, intval INTEGER, unitval VARCHAR(10))
    BEGIN
 DECLARE thisStamp TIMESTAMP;
 DECLARE nextStamp TIMESTAMP;
 SET thisStamp = startstamp;

 -- Drop and Create the temp table

 DROP TEMPORARY TABLE IF EXISTS timestamp_sequence;

 CREATE TEMPORARY TABLE IF NOT EXISTS timestamp_sequence(
 id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,    
 TIME TIMESTAMP
 );    
  
 -- 迴圈, 每次增加間隔
 REPEAT 
  SELECT CASE unitval
   WHEN 'MICROSECOND' THEN TIMESTAMPADD(MICROSECOND, intval, thisStamp)
   WHEN 'SECOND'      THEN TIMESTAMPADD(SECOND, intval, thisStamp)
   WHEN 'MINUTE'      THEN TIMESTAMPADD(MINUTE, intval, thisStamp)
   WHEN 'HOUR'        THEN TIMESTAMPADD(HOUR, intval, thisStamp)
   WHEN 'DAY'         THEN TIMESTAMPADD(DAY, intval, thisStamp)
   WHEN 'WEEK'        THEN TIMESTAMPADD(WEEK, intval, thisStamp)
   WHEN 'MONTH'       THEN TIMESTAMPADD(MONTH, intval, thisStamp)
   WHEN 'QUARTER'     THEN TIMESTAMPADD(QUARTER, intval, thisStamp)
   WHEN 'YEAR'        THEN TIMESTAMPADD(YEAR, intval, thisStamp)
   END INTO nextStamp;
  -- 過濾週六,週日與特殊日子
  IF DAYOFWEEK(thisStamp) <> 1 AND DAYOFWEEK(thisStamp) <> 7 OR DATE(thisStamp) = STR_TO_DATE('2012-08-02', '%Y-%m-%d') THEN
   INSERT INTO timestamp_sequence(Dateid, Weekdayid) SELECT DATE(thisStamp), DAYOFWEEK(thisStamp);
  ELSEIF DATE(thisStamp) = STR_TO_DATE('2012-03-03', '%Y-%m-%d') THEN
   INSERT INTO timestamp_sequence(Dateid, Weekdayid) SELECT DATE(thisStamp), DAYOFWEEK(thisStamp);
  END IF;
  SET thisStamp = nextStamp;
 UNTIL thisStamp >= endstamp
 END REPEAT;
    END$$
DELIMITER ;

要產生時間序列前先初始化gen_time_sequence預存程序
CALL gen_time_sequence('2012-01-01','2012-12-31', 1, 'DAY');  

取得時間序列產生的天數,執行以下SQL指令
SELECT COUNT(1) FROM timestamp_sequence;

執行結果

取得時間序列中的前10筆資料
select * from timestamp_sequence limit 10;

執行結果