golang時間格式化,go語言時間格式轉換

本文目錄一覽:

golang logger輸出格式怎麼修改

1.Logger結構

首先來看下類型Logger的定義:

type Logger struct {

mu sync.Mutex // ensures atomic writes; protects the following fields

prefix string // prefix to write at beginning of each line

flag int // properties

out io.Writer // destination for output

buf []byte // for accumulating text to write

}

主要有5個成員,其中3個我們比較熟悉,分別是表示Log前綴的 “prefix”,表示Log頭標籤的 “flag” ,以及Log的輸出目的地out。 buf是一個字節數組,主要用來存放即將刷入out的內容,相當於一個臨時緩存,在對輸出內容進行序列化時作為存儲目的地。 mu是一個mutex主要用來作線程安全的實習,當有多個goroutine同時往一個目的刷內容的時候,通過mutex保證每次寫入是一條完整的信息。

2.std及整體結構

在前一篇文章中我們提到了log模塊提供了一套包級別的簡單接口,使用該接口可以直接將日誌內容打印到標準錯誤。那麼該過程是怎麼實現的呢?其實就是通過一個內置的Logger類型的變量 “std” 來實現的。該變量使用:

var std = New(os.Stderr, “”, LstdFlags)

進行初始化,默認輸出到系統的標準輸出 “os.Stderr” ,前綴為空,使用日期加時間作為Log抬頭。

當我們調用 log.Print的時候是怎麼執行的呢?我們看其代碼:

func Print(v …interface{}) {

std.Output(2, fmt.Sprint(v…))

}

這裡實際就是調用了Logger對象的 Output方法,將日誌內容按照fmt包中約定的格式轉義後傳給Output。Output定義如下 :

func (l *Logger) Output(calldepth int, s string) error

其中s為日誌沒有加前綴和Log抬頭的具體內容,xxxxx 。該函數執行具體的將日誌刷入到對應的位置。

3.核心函數的實現

Logger.Output是執行具體的將日誌刷入到對應位置的方法。

該方法首先根據需要獲得當前時間和調用該方法的文件及行號信息。然後調用formatHeader方法將Log的前綴和Log抬頭先格式化好 放入Logger.buf中,然後再將Log的內容存入到Logger.buf中,最後調用Logger.out.Write方法將完整的日誌寫入到輸出目的地中。

由於寫入文件以及拼接buf的過程是線程非安全的,因此使用mutex保證每次寫入的原子性。

l.mu.Lock()

defer l.mu.Unlock()

將buf的拼接和文件的寫入放入這個後面,使得在多個goroutine使用同一個Logger對象是,不會弄亂buf,也不會雜糅的寫入。

該方法的第一個參數最終會傳遞給runtime.Caller的skip,指的是跳過的棧的深度。這裡我記住給2就可以了。這樣就會得到我們調用log 是所處的位置。

在golang的注釋中說鎖住 runtime.Caller的過程比較重,這點我還是不很了解,只是從代碼中看到其在這裡把鎖打開了。

if l.flag(Lshortfile|Llongfile) != 0 {

// release lock while getting caller info – it‘s expensive.

l.mu.Unlock()

var ok bool

_, file, line, ok = runtime.Caller(calldepth)

if !ok {

file = “???”

line = 0

}

l.mu.Lock()

}

在formatHeader裡面首先將前綴直接複製到Logger.buf中,然後根據flag選擇Log抬頭的內容,這裡用到了一個log模塊實現的 itoa的方法,作用類似c的itoa,將一個整數轉換成一個字符串。只是其轉換後將結果直接追加到了buf的尾部。

縱觀整個實現,最值得學習的就是線程安全的部分。在什麼位置合適做怎樣的同步操作。

4.對外接口的實現

在了解了核心格式化和輸出結構後,在看其封裝就非常簡單了,幾乎都是首先用Output進行日誌的記錄,然後在必要的時候 做os.exit或者panic的操作,這裡看下Fatal的實現。

func (l *Logger) Fatal(v …interface{}) {

l.Output(2, fmt.Sprint(v…))

os.Exit(1)

}

// Fatalf is equivalent to l.Printf() followed by a call to os.Exit(1).

func (l *Logger) Fatalf(format string, v …interface{}) {

l.Output(2, fmt.Sprintf(format, v…))

os.Exit(1)

}

// Fatalln is equivalent to l.Println() followed by a call to os.Exit(1).

func (l *Logger) Fatalln(v …interface{}) {

l.Output(2, fmt.Sprintln(v…))

os.Exit(1)

}

這裡也驗證了我們之前做的Panic的結果,先做輸出日誌操作。再進行panic。

基礎知識 – Golang 中的格式化輸入輸出

【格式化輸出】

// 格式化輸出:將 arg 列表中的 arg 轉換為字符串輸出

// 使用動詞 v 格式化 arg 列表,非字符串元素之間添加空格

Print(arg列表)

// 使用動詞 v 格式化 arg 列表,所有元素之間添加空格,結尾添加換行符

Println(arg列表)

// 使用格式字符串格式化 arg 列表

Printf(格式字符串, arg列表)

// Print 類函數會返回已處理的 arg 數量和遇到的錯誤信息。

【格式字符串】

格式字符串由普通字符和佔位符組成,例如:

“abc%+ #8.3[3]vdef”

其中 abc 和 def 是普通字符,其它部分是佔位符,佔位符以 % 開頭(註:%% 將被轉義為一個普通的 % 符號,這個不算開頭),以動詞結尾,格式如下:

%[旗標][寬度][.精度][arg索引]動詞

方括號中的內容可以省略。

【旗標】

旗標有以下幾種:

空格:對於數值類型的正數,保留一個空白的符號位(其它用法在動詞部分說明)。

0 :用 0 進行寬度填充而不用空格,對於數值類型,符號將被移到所有 0 的前面。

其中 “0” 和 “-” 不能同時使用,優先使用 “-” 而忽略 “0”。

【寬度和精度】

“寬度”和“精度”都可以寫成以下三種形式:

數值 | * | arg索引*

其中“數值”表示使用指定的數值作為寬度值或精度值,“ ”表示使用當前正在處理的 arg 的值作為寬度值或精度值,如果這樣的話,要格式化的 arg 將自動跳轉到下一個。“arg索引 ”表示使用指定 arg 的值作為寬度值或精度值,如果這樣的話,要格式化的 arg 將自動跳轉到指定 arg 的下一個。

寬度值:用於設置最小寬度。

精度值:對於浮點型,用於控制小數位數,對於字符串或字節數組,用於控制字符數量(不是字節數量)。

對於浮點型而言,動詞 g/G 的精度值比較特殊,在適當的情況下,g/G 會設置總有效數字,而不是小數位數。

【arg 索引】

“arg索引”由中括號和 arg 序號組成(就像上面示例中的 [3]),用於指定當前要處理的 arg 的序號,序號從 1 開始:

‘[‘ + arg序號 + ‘]’

【動詞】

“動詞”不能省略,不同的數據類型支持的動詞不一樣。

[通用動詞]

v:默認格式,不同類型的默認格式如下:

布爾型:t

整 型:d

浮點型:g

複數型:g

字符串:s

通 道:p

指 針:p

無符號整型:x

T:輸出 arg 的類型而不是值(使用 Go 語法格式)。

[布爾型]

t:輸出 true 或 false 字符串。

[整型]

b/o/d:輸出 2/8/10 進制格式

x/X :輸出 16 進制格式(小寫/大寫)

c :輸出數值所表示的 Unicode 字符

q :輸出數值所表示的 Unicode 字符(帶單引號)。對於無法顯示的字符,將輸出其轉義字符。

U :輸出 Unicode 碼點(例如 U+1234,等同於字符串 “U+%04X” 的顯示結果)

對於 o/x/X:

如果使用 “#” 旗標,則會添加前導 0 或 0x。

對於 U:

如果使用 “#” 旗標,則會在 Unicode 碼點後面添加相應的 ‘字符’(前提是該字符必須可顯示)

[浮點型和複數型]

b :科學計數法(以 2 為底)

e/E:科學計數法(以 10 為底,小寫 e/大寫 E)

f/F:普通小數格式(兩者無區別)

g/G:大指數(指數 = 6)使用 %e/%E,其它情況使用 %f/%F

[字符串或字節切片]

s :普通字符串

q :雙引號引起來的 Go 語法字符串

x/X:十六進制編碼(小寫/大寫,以字節為元素進行編碼,而不是字符)

對於 q:

如果使用了 “+” 旗標,則將所有非 ASCII 字符都進行轉義處理。

如果使用了 “#” 旗標,則輸出反引號引起來的字符串(前提是

字符串中不包含任何製表符以外的控制字符,否則忽略 # 旗標)

對於 x/X:

如果使用了 ” ” 旗標,則在每個元素之間添加空格。

如果使用了 “#” 旗標,則在十六進制格式之前添加 0x 前綴。

[指針類型]

p :帶 0x 前綴的十六進制地址值。

[符合類型]

複合類型將使用不同的格式輸出,格式如下:

結 構 體:{字段1 字段2 …}

數組或切片:[元素0 元素1 …]

映 射:map[鍵1:值1 鍵2:值2 …]

指向符合元素的指針:{}, [], map[]

複合類型本身沒有動詞,動詞將應用到複合類型的元素上。

結構體可以使用 “+v” 同時輸出字段名。

【注意】

1、如果 arg 是一個反射值,則該 arg 將被它所持有的具體值所取代。

2、如果 arg 實現了 Formatter 接口,將調用它的 Format 方法完成格式化。

3、如果 v 動詞使用了 # 旗標(%#v),並且 arg 實現了 GoStringer 接口,將調用它的 GoString 方法完成格式化。

如果格式化操作指定了字符串相關的動詞(比如 %s、%q、%v、%x、%X),接下來的兩條規則將適用:

4。如果 arg 實現了 error 接口,將調用它的 Error 方法完成格式化。

5。如果 arg 實現了 string 接口,將調用它的 String 方法完成格式化。

在實現格式化相關接口的時候,要避免無限遞歸的情況,比如:

type X string

func (x X) String() string {

return Sprintf(“%s”, x)

}

在格式化之前,要先轉換數據類型,這樣就可以避免無限遞歸:

func (x X) String() string {

return Sprintf(“%s”, string(x))

}

無限遞歸也可能發生在自引用數據類型上面,比如一個切片的元素引用了切片自身。這種情況比較罕見,比如:

a := make([]interface{}, 1)

a[0] = a

fmt.Println(a)

【格式化輸入】

// 格式化輸入:從輸入端讀取字符串(以空白分隔的值的序列),

// 並解析為具體的值存入相應的 arg 中,arg 必須是變量地址。

// 字符串中的連續空白視為單個空白,換行符根據不同情況處理。

// \r\n 被當做 \n 處理。

// 以動詞 v 解析字符串,換行視為空白

Scan(arg列表)

// 以動詞 v 解析字符串,換行結束解析

Scanln(arg列表)

// 根據格式字符串中指定的格式解析字符串

// 格式字符串中的換行符必須和輸入端的換行符相匹配。

Scanf(格式字符串, arg列表)

// Scan 類函數會返回已處理的 arg 數量和遇到的錯誤信息。

【格式字符串】

格式字符串類似於 Printf 中的格式字符串,但下面的動詞和旗標例外:

p :無效

T :無效

e/E/f/F/g/G:功能相同,都是掃描浮點數或複數

s/v :對字符串而言,掃描一個被空白分隔的子串

對於整型 arg 而言,v 動詞可以掃描帶有前導 0 或 0x 的八進制或十六進制數值。

寬度被用來指定最大掃描寬度(不會跨越空格),精度不被支持。

如果 arg 實現了 Scanner 接口,將調用它的 Scan 方法掃描相應數據。只有基礎類型和實現了 Scanner 接口的類型可以使用 Scan 類方法進行掃描。

【注意】

連續調用 FScan 可能會丟失數據,因為 FScan 中使用了 UnreadRune 對讀取的數據進行撤銷,而參數 io.Reader 只有 Read 方法,不支持撤銷。比如:

golang在將struct轉成json字符串的時候如何將日期類型的數據格式化?

如果你想輸出的時間是YYYY-MM-DD的話

要在使用json數據化之前自己處理時間

type Article struct {

    Id             int

    Title          string

    CreateTimeStr  string

}

然後要將之前的時間轉過來

Article.CreateTimeStr = Createdatetime.Format(“2006-01-02”)

最後序列化JSON就是YYYY-MM-DD

這是最簡單的方法

golang 獲取時間精確能到納秒嗎

這樣。不過只是個精確到納秒的計時器,不是精確到納秒的當前時間。windows好像只能拿到ms精度的當前時間吧,不是很清楚。

package main

import (

“syscall”

“time”

“unsafe”

)

func NewStopWatch() func() time.Duration {

var QPCTimer func() func() time.Duration

QPCTimer = func() func() time.Duration {

lib, _ := syscall.LoadLibrary(“kernel32.dll”)

qpc, _ := syscall.GetProcAddress(lib, “QueryPerformanceCounter”)

qpf, _ := syscall.GetProcAddress(lib, “QueryPerformanceFrequency”)

if qpc == 0 || qpf == 0 {

return nil

}

var freq, start uint64

syscall.Syscall(qpf, 1, uintptr(unsafe.Pointer(freq)), 0, 0)

syscall.Syscall(qpc, 1, uintptr(unsafe.Pointer(start)), 0, 0)

if freq = 0 {

return nil

}

freqns := float64(freq) / 1e9

return func() time.Duration {

var now uint64

syscall.Syscall(qpc, 1, uintptr(unsafe.Pointer(now)), 0, 0)

return time.Duration(float64(now-start) / freqns)

}

}

var StopWatch func() time.Duration

if StopWatch = QPCTimer(); StopWatch == nil {

// Fallback implementation

start := time.Now()

StopWatch = func() time.Duration { return time.Since(start) }

}

return StopWatch

}

func main() {

// Call a new stop watch to create one from this moment on.

watch := NewStopWatch()

// Do some stuff that takes time.

time.Sleep(1)

// Call the stop watch itself and it will return a time.Duration

dur := watch()

}

這和語言沒關係,操作系統要提供這樣的原語。linux和windows都是可以的。

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/270648.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-12-16 13:38
下一篇 2024-12-16 13:38

相關推薦

  • AES加密解密算法的C語言實現

    AES(Advanced Encryption Standard)是一種對稱加密算法,可用於對數據進行加密和解密。在本篇文章中,我們將介紹C語言中如何實現AES算法,並對實現過程進…

    編程 2025-04-29
  • 學習Python對學習C語言有幫助嗎?

    Python和C語言是兩種非常受歡迎的編程語言,在程序開發中都扮演着非常重要的角色。那麼,學習Python對學習C語言有幫助嗎?答案是肯定的。在本文中,我們將從多個角度探討Pyth…

    編程 2025-04-29
  • 使用Golang調用Python

    在現代軟件開發中,多種編程語言的協作是相當普遍的。其中一種使用場景是Golang調用Python,這使得在使用Python庫的同時,可以利用Golang的高性能和強大並發能力。這篇…

    編程 2025-04-29
  • Python被稱為膠水語言

    Python作為一種跨平台的解釋性高級語言,最大的特點是被稱為”膠水語言”。 一、簡單易學 Python的語法簡單易學,更加人性化,這使得它成為了初學者的入…

    編程 2025-04-29
  • OpenJudge答案1.6的C語言實現

    本文將從多個方面詳細闡述OpenJudge答案1.6在C語言中的實現方法,幫助初學者更好地學習和理解。 一、需求概述 OpenJudge答案1.6的要求是,輸入兩個整數a和b,輸出…

    編程 2025-04-29
  • 解決docker-compose 容器時間和服務器時間不同步問題

    docker-compose是一種工具,能夠讓您使用YAML文件來定義和運行多個容器。然而,有時候容器的時間與服務器時間不同步,導致一些不必要的錯誤和麻煩。以下是解決方法的詳細介紹…

    編程 2025-04-29
  • Python按位運算符和C語言

    本文將從多個方面詳細闡述Python按位運算符和C語言的相關內容,並給出相應的代碼示例。 一、概述 Python是一種動態的、面向對象的編程語言,其按位運算符是用於按位操作的運算符…

    編程 2025-04-29
  • 使用Golang創建黑色背景圖片的方法

    本文將從多個方面介紹使用Golang創建黑色背景圖片的方法。 一、安裝必要的代碼庫和工具 在開始創建黑色背景圖片之前,我們需要先安裝必要的代碼庫和工具: go get -u git…

    編程 2025-04-29
  • Python語言由荷蘭人為中心的全能編程開發工程師

    Python語言是一種高級語言,很多編程開發工程師都喜歡使用Python語言進行開發。Python語言的創始人是荷蘭人Guido van Rossum,他在1989年聖誕節期間開始…

    編程 2025-04-28
  • Python語言設計基礎第2版PDF

    Python語言設計基礎第2版PDF是一本介紹Python編程語言的經典教材。本篇文章將從多個方面對該教材進行詳細的闡述和介紹。 一、基礎知識 本教材中介紹了Python編程語言的…

    編程 2025-04-28

發表回復

登錄後才能評論