tft每日頭條

 > 生活

 > go實現數據處理

go實現數據處理

生活 更新时间:2024-08-03 06:06:29

go實現數據處理?原生類語言,相對來說,我個人最熟悉的是Delphi,而且當下一些三方的庫,提供Delphi的SDK的也很少,為了使用Go的豐富的生态庫,然後使用Delphi優秀的界面開發方案,寫本篇章的主要目的也就是一個抛磚引玉吧,希望引出各大Delphier擴展三方框架的玉石,同時也算是提供給廣大Delphier能靈活使用Go來對接Delphi的提供一個簡要的方案吧,我來為大家科普一下關于go實現數據處理?以下内容希望對你有幫助!

go實現數據處理(FFI實戰之對接GOCGO)1

go實現數據處理

Delphi中的一些數據類型到Go的數據類型的轉換

原生類語言,相對來說,我個人最熟悉的是Delphi,而且當下一些三方的庫,提供Delphi的SDK的也很少,為了使用Go的豐富的生态庫,然後使用Delphi優秀的界面開發方案,寫本篇章的主要目的也就是一個抛磚引玉吧,希望引出各大Delphier擴展三方框架的玉石,同時也算是提供給廣大Delphier能靈活使用Go來對接Delphi的提供一個簡要的方案吧。

實際上無論是什麼語言什麼類型的語言,要跨語言通用 首要的就是搞清楚各自的數據類型的内存結構,以及對齊方式,隻要将這些都搞清楚了,那其他的都是不變應萬變,水到渠成的事情。

delphi字符串到Go的字符串的轉換的幾種方法
  • 自己構造符合Go語言結構的結構

這個方法,前面咱們講過并且也用過了,也就是之前定義的gostring,如下:

typedef struct _goString{ char* UTF8Data; int datalen; }goString,*pgoString;

這樣做有他的好處,也有他的不好之處,好處是其他語言隻要定義一個這種格式的數據類型就能和Go的字符串進行交互,本方法任何語言都實用用,比較通用,不好之處,就是不能使用各自語言的原始數據類型,必須要聲明一個對應的數據類型來做轉化處理。

那麼有沒有法子來讓各自的原生語言使用原生語言的數據類型呢,肯定是有的,請繼續看。

  • 使用标準C的char*或者wchar*

通常情況下,咱們需要跨語言,使用動态庫,基本上也都是使用标準C的char*和wchar*作為參數來傳遞字符串相關信息的。這樣大家都認識,并且各個語言也都有這種原始數據指針的類型,比如Delphi中就有對應的PAnsichar,和Pchar等就支持。所以這種方式也是比較通用的。

由于就是指針,所以咱們在Go中接收數據,直接使用uintptr類型來接收就好,然後就是要将這個數據轉換到Go的string類型,如何轉換呢,這就是需要分析的點了,首先在标準C中的char*, wchar*實際上就是一個以\0結尾的連續字符數組,如此一來,咱們的突破點就找到了,那就是通過查找\0就能知道這個串的長度,那麼就容易了,當然這些都是需要指針操作的說,現在給出相關的轉換代碼如下:

func PcharLen(dstr uintptr) int { if dstr == 0 { return 0 } ptr := unsafe.Pointer(dstr) for i := 0; ; i { if 0 == *(*uint16)(ptr) { return int(i) } ptr = unsafe.Pointer(uintptr(ptr) 2) } return 0 } func FastPchar2String(pcharStr uintptr) String { if pcharStr == 0 { return "" } s := new(reflect.SliceHeader) s.Data = pcharStr s.Len = PcharLen(pcharStr) s.Cap = s.Len return string(utf16.Decode(*(*[]uint16)(unsafe.Pointer(s)))) } func Pchar2String(pcharstr uintptr) string { if pcharstr == 0 { return "" } ptr := unsafe.Pointer(pcharstr) gbt := make([]uint16, 0, 255) for i := 0; ; i { if 0 == *(*uint16)(ptr) { break } gbt = append(gbt, *(*uint16)(ptr)) ptr = unsafe.Pointer(uintptr(ptr) 2) } return string(utf16.Decode(gbt)) }

這裡的代碼轉換主要使用的特性就是查找内存中以\0結尾的來區分字符串完畢,完了之後,直接獲取數據塊數據,然後使用相應的解碼函數解碼,我這裡主要都是針對高版本的Delphi,所以給定的參數Pchar實際上都是String的類型,然後直接Pcharde,其編碼格式就是UTF16的格式,所以最終使用utf16去解碼,如果是使用的是AnsiString,或者低版本的,就需要使用相應的格式去做解碼的,如果是D2007之下,應該使用GBK去解碼了,還有可能是Utf8那麼就需要使用Utf8去解碼了。

除了使用這種方式,直接使用Delphi自己的更原始的公民string類型行不行呢。咱們繼續往下分析。

  • 使用Delphi的一等原始公民字符串類型直接作為參數傳遞

同理咱們先來分析一下Delphi的字符串的數據類型,到底是個什麼東西,這一塊,實際上相應的手冊以及Delphi的自帶源碼中的一些字符串處理函數中都有一些蛛絲馬迹,這裡就不多說了,實際上他就是一個指針,咱們使用sizeof(string)也可以發現這個長度就是指針的大小。

Delphi的string在内存中的實際格式如下:

codePage偏移

字符元素長度 偏移

引用計數 偏移

字符串長度 偏移

數據區偏移

-12

-10

-8

-4

0

這裡的偏移,指的是地址偏移,一個Delphi字符串的指針地址就是數據區偏移,所以咱們可以直接使用Pchar将一個字符串轉成一個char*的

另外就是這是支持Unicode的Delphi,應該是從2010開始,基本上都是這個存儲結構,如果是低版本的,則隻有引用計數偏移以及字符串長度偏移

知道了這個結構,那麼咱們就能來實現這個原始字符串類型的代碼實現了,首先是接收類型,已經說了string就是一個指針,所以,接收類型咱們依然使用uintptr作為接收參數,然後要做的就是根據這個内存結構來将數據還原到Go的版本

package main func DelphiStringLen(delphiString uintptr) (result int32) { //Delphi字符串的地址的-4地址位置為長度 if delphiString == 0 { return 0 } result = *(*int32)(unsafe.Pointer(delphiString - 4)) return } func FastDelphiString2String(delphiString uintptr, unicodeDelphi bool) string { if delphiString == 0 { return "" } dataLen := *(*int32)(unsafe.Pointer(delphiString - 4)) if dataLen == 0 { return "" } var codePage uint16 if unicodeDelphi { codePage = *(*uint16)(unsafe.Pointer(delphiString - 12)) } else { codePage = 936 } switch codePage { case 1200: //UTF16編碼頁 s := new(reflect.SliceHeader) s.Data = delphiString s.Len = int(dataLen * 2) s.Cap = s.Len return string(utf16.Decode(*(*[]uint16)(unsafe.Pointer(s)))) case 65001: //是UTF8的,那麼直接開整 s := make([]byte, dataLen) CopyMemory(unsafe.Pointer(&s[0]), unsafe.Pointer(delphiString), uintptr(dataLen)) return *(*string)(unsafe.Pointer(&s)) case 936, 20936: //gbk的 s := new(reflect.SliceHeader) s.Data = delphiString s.Len = int(dataLen) s.Cap = s.Len if resultUtf8, err := GBK2Utf8(*(*[]byte)(unsafe.Pointer(s))); err == nil { return *(*string)(unsafe.Pointer(&resultUtf8)) } case 950: //繁體中文 } return "" }

上面兩個函數,就可以直接接收Delphi的原生string類型,并且,咱們還針對codepage來針對不同的編碼頁,做了不同的轉換,比如如果此時Go寫了一個動态庫,導出函數

//export delphiString func delphiString(delphiString uintptr, unicodeDelphi bool) { codePage := *(*uint16)(unsafe.Pointer(delphiString - 12)) notify(fmt.Sprintf("字符串長度:%d,codePage:%d", DelphiStringLen(delphiString), codePage)) notify(FastDelphiString2String(delphiString, unicodeDelphi)) }

然後咱們可以在Delphi中直接聲明一個函數類型

var delphiString: procedure(m: string;unicodeDelphi: Boolean);cdecl; //調用 var msg: string; begin str := '不得閑測試234123服大'; delphiString(str,True); end; //或者 var delphiString: procedure(m: UTF8String;unicodeDelphi: Boolean);cdecl; //調用 var msg: UTF8String; begin str := '不得閑測試234123服大'; delphiString(str,True); end;

到這裡,基本上,我就将我所知道的将Delphi的字符串轉到Go的字符串的方式都講了,有興趣的可以自己試試。

Delphi的時間日期類型到Go的日期時間類型

同理咱們先看一下兩個日期時間類型的區别,Delphi中的TDateTime實際上就是一個Double類型,而Go中的Time類型是一個結構體,并且兩者針對時間存儲的規則也不相同,所以想用指針啥的來直接轉換,肯定就不行了。

思考一下Double類型就是Go語言中的float64,咱們在Go中可以使用float64類型來接收Delphi的日期類型,而主要要做的,就是需要将Delphi的傳遞過來的double轉換成一個有效的Go的時間類型,咱們可以先看一下規則,從Delphi自己的時間單元相關的代碼可以發現,Delphi的TDateTime實際上表示的是 其表示的時間到1899-12-30号的天數 (表示的時間的毫秒數/一天的總共毫秒數集合),通過這個規則,咱們做好轉換就OK了

package DelphiTime type ( TDateTime float64 ) const ( MinsPerHour = 60 MinsPerDay = 24 * MinsPerHour SecsPerDay = MinsPerDay * 60 MSecsPerDay = SecsPerDay * 1000 ) var delphiFirstTime time.Time func init() { delphiFirstTime = time.Date(1899, 12, 30, 0, 0, 0, 0, time.Local) } func (date TDateTime) ToTime() time.Time { mDay := time.Duration(date) ms := (date - TDateTime(mDay)) * TDateTime(MSecsPerDay) return delphiFirstTime.Add(mDay*time.Hour*24 time.Duration(ms)*time.Millisecond) } func Time2DelphiTime(t time.Time) TDateTime { if t.IsZero() { return 0 } days := t.Sub(delphiFirstTime) / (time.Hour * 24) y, m, d := t.Date() nowdate := time.Date(y, m, d, 0, 0, 0, 0, time.Local) times := float64(t.Sub(nowdate)) / float64(time.Hour*24) return TDateTime(float64(days) times) }

小結

通過上面的分解說明,咱們可以看到,要讓雙方互通,主要就是内存結構保持一緻,其他的按照相關類型的規則去做相應的去轉化,去處理就好,也就是知道其所以然,搞明白構造之後,就可以随便玩了,完全可以根據自己的想法去行走,去應用,而不需要死扣着CGO文檔中的一些東西去生搬硬套。

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关生活资讯推荐

热门生活资讯推荐

网友关注

Copyright 2023-2024 - www.tftnews.com All Rights Reserved