Documentation ¶
Index ¶
- Variables
- func ClearTerminal()
- func GetMapReturnSlice() []string
- func GoodByePrint()
- func HelpMessage()
- func PrintWordByWord(words []string)
- func SplitIntoWords(text string) []string
- type AllAnswers
- type AllMapUsages
- type AllQuestions
- type AnswersSampleStruct
- type DataBase
- type Features
- type FunctionsDefinitions
- type QuestionsSampleStruct
- type SingleDefinitionExamples
- type SingleDefinitions
- type WordsInGolangNumbered
Constants ¶
This section is empty.
Variables ¶
View Source
var Answers = AllAnswers{ SingleAnws: map[string]string{ "operator": "Hints and Tips Calculate the number of working cars produced per hour The percentage (passed as an argument) is a number between 0-100. To make this percentage a bit easier to work with, start by dividing it by 100. To compute the number of cars produced successfully, multiply the percentage (divided by 100) by the number of cars produced. When multiplying two numbers together, they both need to be of the same type. Use type conversions if needed.", "operator 1": "Hints and Tips Calculate the number of working cars produced per hour The percentage (passed as an argument) is a number between 0-100. To make this percentage a bit easier to work with, start by dividing it by 100. To compute the number of cars produced successfully, multiply the percentage (divided by 100) by the number of cars produced. When multiplying two numbers together, they both need to be of the same type. Use type conversions if needed.", }, }
View Source
var AnswersSample = AnswersSampleStruct{ MapAnswersSample: map[string]string{ "operator": `// CalculateWorkingCarsPerHour calculates how many working cars are // produced by the assembly line every hour. func CalculateWorkingCarsPerHour(productionRate int, successRate float64) float64 { last := float64(productionRate) * successRate / 100 return last }`, "operator 1": `// CalculateWorkingCarsPerHour calculates how many working cars are // produced by the assembly line every hour. func CalculateWorkingCarsPerHour(productionRate int, successRate float64) float64 { last := float64(productionRate) * successRate / 100 return last }`, }, }
View Source
var LOGO string = fmt.Sprintln(` ┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
• GOCRON-VERSION 💠 v1.0.38 💠 •
• Go learning with CLI tool •
• ██████████████████████████████████████████████████████████████████████████████████████████████████████████ •
• ██ ███ ███ ██ █████ ███ ██████ █ •
• ██ ██ ██ ██ ████ ██ ████ █ •
• ██ ████████████ █████ ██ ███████ ██ ███████ ████ █████ ██ █ ███ █ •
• ██ ████████████ ███████ ██ ████████████ ███████ ████ ███████ ██ ██ ██ █ •
• ██ █████ ██ ███████ ██ ████████████ █████ ████ ███████ ██ ███ █ █ •
• ██ █████ ██ ███████ ██ ████████████ ██ ███████ ███████ ██ ████ █ •
• ██ ███████ ██ ███████ ██ ████████████ ████ █████ ███████ ██ █████ █ •
• ██ ███████ ██ █████ ██ ███████ ██ ██████ ████ █████ ██ ██████ █ •
• ██ ██ ██ ██ ███████ ███ ██ ███████ █ •
• ██ ███ ███ ██ ███████ ███ ███ ████████ █ •
• ██████████████████████████████████████████████████████████████████████████████████████████████████████████ •
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
─────────────────────────────────────────────────────────────────────────────────────────────────────────────`)
View Source
var MapUsage = AllMapUsages{ SingleDef: map[string]string{ "godoc": `Usage: godoc [flag] The flags are: -v verbose mode -timestamps=true show timestamps with directory listings -index enable identifier and full text search index (no search box is shown if -index is not set) -index_files="" glob pattern specifying index files; if not empty, the index is read from these files in sorted order -index_throttle=0.75 index throttle value; a value of 0 means no time is allocated to the indexer (the indexer will never finish), a value of 1.0 means that index creation is running at full throttle (other goroutines may get no time while the index is built) -index_interval=0 interval of indexing; a value of 0 sets it to 5 minutes, a negative value indexes only once at startup -play=false enable playground -links=true link identifiers to their declarations -write_index=false write index to a file; the file name must be specified with -index_files -maxresults=10000 maximum number of full text search results shown (no full text index is built if maxresults <= 0) -notes="BUG" regular expression matching note markers to show (e.g., "BUG|TODO", ".*") -goroot=$GOROOT Go root directory -http=addr HTTP service address (e.g., '127.0.0.1:6060' or just ':6060') -templates="" directory containing alternate template files; if set, the directory may provide alternative template files for the files in $GOROOT/lib/godoc -url=path print to standard output the data that would be served by an HTTP request for path -zip="" zip file providing the file system to serve; disabled if empty `, }, }
View Source
var Message string = fmt.Sprintln(`
my name is Sina LalehBakhsh, I hope this API is useful for you
after running program, write your single word about any of GO language.
if your perpuse is more than one word, for convenience searching, just write keywords.
like this:
map slice
`)
View Source
var OriginSingleDef = SingleDefinitions{ SingleDef: map[string]string{ "go": "Fans of Go (called gophers) describe Go as having the expressiveness of dynamic languages like Python or Ruby, with the performance of compiled languages like C or C++. The language is open source, and was started by engineers at Google. It's written using a C-style syntax, has statically typed variables, manages memory using garbage collection, and is compiled into stand-alone executables. Go is noted for the concurrent programming features built into the language core, the networking packages in the standard library (such as a web server), fast compilation and execution speed. Its simple, minimalistic and consistent language design make for a delightful experience, while the abundant and thoughtful tooling addresses traditional problems such as consistent formatting and documentation. The home page for Go is go.dev, and there is an excellent interactive tutorial at tour.go.dev. Go (often referred to as Golang) is a statically-typed programming language known for its simplicity, efficiency, and strong support for concurrent programming. It was designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson and was first released to the public in 2009.", "golang": "Fans of Go (called gophers) describe Go as having the expressiveness of dynamic languages like Python or Ruby, with the performance of compiled languages like C or C++. The language is open source, and was started by engineers at Google. It's written using a C-style syntax, has statically typed variables, manages memory using garbage collection, and is compiled into stand-alone executables. Go is noted for the concurrent programming features built into the language core, the networking packages in the standard library (such as a web server), fast compilation and execution speed. Its simple, minimalistic and consistent language design make for a delightful experience, while the abundant and thoughtful tooling addresses traditional problems such as consistent formatting and documentation. The home page for Go is go.dev, and there is an excellent interactive tutorial at tour.go.dev. Go (often referred to as Golang) is a statically-typed programming language known for its simplicity, efficiency, and strong support for concurrent programming. It was designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson and was first released to the public in 2009.", "gopher": "Fans of Go (called gophers) describe Go as having the expressiveness of dynamic languages like Python or Ruby, with the performance of compiled languages like C or C++. The language is open source, and was started by engineers at Google. It's written using a C-style syntax, has statically typed variables, manages memory using garbage collection, and is compiled into stand-alone executables. Go is noted for the concurrent programming features built into the language core, the networking packages in the standard library (such as a web server), fast compilation and execution speed. Its simple, minimalistic and consistent language design make for a delightful experience, while the abundant and thoughtful tooling addresses traditional problems such as consistent formatting and documentation. The home page for Go is go.dev, and there is an excellent interactive tutorial at tour.go.dev. Go (often referred to as Golang) is a statically-typed programming language known for its simplicity, efficiency, and strong support for concurrent programming. It was designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson and was first released to the public in 2009.", "gophers": "Fans of Go (called gophers) describe Go as having the expressiveness of dynamic languages like Python or Ruby, with the performance of compiled languages like C or C++. The language is open source, and was started by engineers at Google. It's written using a C-style syntax, has statically typed variables, manages memory using garbage collection, and is compiled into stand-alone executables. Go is noted for the concurrent programming features built into the language core, the networking packages in the standard library (such as a web server), fast compilation and execution speed. Its simple, minimalistic and consistent language design make for a delightful experience, while the abundant and thoughtful tooling addresses traditional problems such as consistent formatting and documentation. The home page for Go is go.dev, and there is an excellent interactive tutorial at tour.go.dev. Go (often referred to as Golang) is a statically-typed programming language known for its simplicity, efficiency, and strong support for concurrent programming. It was designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson and was first released to the public in 2009.", "what is go?": "Fans of Go (called gophers) describe Go as having the expressiveness of dynamic languages like Python or Ruby, with the performance of compiled languages like C or C++. The language is open source, and was started by engineers at Google. It's written using a C-style syntax, has statically typed variables, manages memory using garbage collection, and is compiled into stand-alone executables. Go is noted for the concurrent programming features built into the language core, the networking packages in the standard library (such as a web server), fast compilation and execution speed. Its simple, minimalistic and consistent language design make for a delightful experience, while the abundant and thoughtful tooling addresses traditional problems such as consistent formatting and documentation. The home page for Go is go.dev, and there is an excellent interactive tutorial at tour.go.dev. Go (often referred to as Golang) is a statically-typed programming language known for its simplicity, efficiency, and strong support for concurrent programming. It was designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson and was first released to the public in 2009.", "what is go ?": "Fans of Go (called gophers) describe Go as having the expressiveness of dynamic languages like Python or Ruby, with the performance of compiled languages like C or C++. The language is open source, and was started by engineers at Google. It's written using a C-style syntax, has statically typed variables, manages memory using garbage collection, and is compiled into stand-alone executables. Go is noted for the concurrent programming features built into the language core, the networking packages in the standard library (such as a web server), fast compilation and execution speed. Its simple, minimalistic and consistent language design make for a delightful experience, while the abundant and thoughtful tooling addresses traditional problems such as consistent formatting and documentation. The home page for Go is go.dev, and there is an excellent interactive tutorial at tour.go.dev. Go (often referred to as Golang) is a statically-typed programming language known for its simplicity, efficiency, and strong support for concurrent programming. It was designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson and was first released to the public in 2009.", "what is golang?": "Fans of Go (called gophers) describe Go as having the expressiveness of dynamic languages like Python or Ruby, with the performance of compiled languages like C or C++. The language is open source, and was started by engineers at Google. It's written using a C-style syntax, has statically typed variables, manages memory using garbage collection, and is compiled into stand-alone executables. Go is noted for the concurrent programming features built into the language core, the networking packages in the standard library (such as a web server), fast compilation and execution speed. Its simple, minimalistic and consistent language design make for a delightful experience, while the abundant and thoughtful tooling addresses traditional problems such as consistent formatting and documentation. The home page for Go is go.dev, and there is an excellent interactive tutorial at tour.go.dev. Go (often referred to as Golang) is a statically-typed programming language known for its simplicity, efficiency, and strong support for concurrent programming. It was designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson and was first released to the public in 2009.", "what is golang ?": "Fans of Go (called gophers) describe Go as having the expressiveness of dynamic languages like Python or Ruby, with the performance of compiled languages like C or C++. The language is open source, and was started by engineers at Google. It's written using a C-style syntax, has statically typed variables, manages memory using garbage collection, and is compiled into stand-alone executables. Go is noted for the concurrent programming features built into the language core, the networking packages in the standard library (such as a web server), fast compilation and execution speed. Its simple, minimalistic and consistent language design make for a delightful experience, while the abundant and thoughtful tooling addresses traditional problems such as consistent formatting and documentation. The home page for Go is go.dev, and there is an excellent interactive tutorial at tour.go.dev. Go (often referred to as Golang) is a statically-typed programming language known for its simplicity, efficiency, and strong support for concurrent programming. It was designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson and was first released to the public in 2009.", "godoc": "Godoc is a Go package that lets you create, manage, and use Go documentation in “the Go way”. for installing: sudo apt install golang-golang-x-tools. The Go way is a set of principles that, as a Go programmer, you should follow to improve code quality. Using Godoc, you can easily read other developers' documentation and code. You can also automate the creation of your own documentation and publish it using Godoc.Godoc is similar to Javadoc, the code documentor for Java. They both use comments and code in modules to generate documentation. And both tools structure that documentation in HTML so you can view it in a browser.", "what is godoc?": "Godoc is a Go package that lets you create, manage, and use Go documentation in “the Go way”. for installing: sudo apt install golang-golang-x-tools. The Go way is a set of principles that, as a Go programmer, you should follow to improve code quality. Using Godoc, you can easily read other developers' documentation and code. You can also automate the creation of your own documentation and publish it using Godoc.Godoc is similar to Javadoc, the code documentor for Java. They both use comments and code in modules to generate documentation. And both tools structure that documentation in HTML so you can view it in a browser.", "install godoc": "Run this in Terminal ===> sudo apt install golang-golang-x-tools", "getting started with godoc": "install the Godoc package from the golang website using this command: go get golang.org/x/tools/cmd/godoc. Conventionally, Go developers use port 6060 to host documentation. This is the command for running a Godoc server on that port: godoc -http=:6060 . The command above hosts your code documentation on localhost, or 127.0.0.1. The port doesn't have to 6060; godoc will run on any unoccupied port. However, it's always best to follow the Go documentation conventions.", "about go": "Fans of Go (called gophers) describe Go as having the expressiveness of dynamic languages like Python or Ruby, with the performance of compiled languages like C or C++. The language is open source, and was started by engineers at Google. It's written using a C-style syntax, has statically typed variables, manages memory using garbage collection, and is compiled into stand-alone executables. Go is noted for the concurrent programming features built into the language core, the networking packages in the standard library (such as a web server), fast compilation and execution speed. Its simple, minimalistic and consistent language design make for a delightful experience, while the abundant and thoughtful tooling addresses traditional problems such as consistent formatting and documentation. The home page for Go is go.dev, and there is an excellent interactive tutorial at tour.go.dev.", "create environment go": "0.Create Environment GO in Command Line Interface Go: 1- go mod init YOURNAME 2- go work init YOURWORKDIRECTORY 3- go run main.go OR go run projectName.go", "create go project": "0.Create Environment GO in Command Line Interface Go: 1- go mod init YOURNAME 2- go work init YOURWORKDIRECTORY 3- go run main.go OR go run projectName.go", "go project": "0.Create Environment GO in Command Line Interface Go: 1- go mod init YOURNAME 2- go work init YOURWORKDIRECTORY 3- go run main.go OR go run projectName.go", "environment go": "0.Create Environment GO in Command Line Interface Go: 1- go mod init YOURNAME 2- go work init YOURWORKDIRECTORY 3- go run main.go OR go run projectName.go", "go environment": "0.Create Environment GO in Command Line Interface Go: 1- go mod init YOURNAME 2- go work init YOURWORKDIRECTORY 3- go run main.go OR go run projectName.go", "create go environment": "0.Create Environment GO in Command Line Interface Go: 1- go mod init YOURNAME 2- go work init YOURWORKDIRECTORY 3- go run main.go OR go run projectName.go", "run go environment": "0.Create Environment GO in Command Line Interface Go: 1- go mod init YOURNAME 2- go work init YOURWORKDIRECTORY 3- go run main.go OR go run projectName.go", "run go environment project": "0.Create Environment GO in Command Line Interface Go: 1- go mod init YOURNAME 2- go work init YOURWORKDIRECTORY 3- go run main.go OR go run projectName.go", "build": "1.build: The go build command compiles the source code in the current directory and generates an executable file.", "clean": "2.clean: The go clean command removes the output produced by the go build command, including the executable and any temporary files that were created during the build.", "flag": "5.flag package: Command-line flags are a common way to specify options for command-line programs. For example, in wc -l the -l is a command-line flag. Go provides a flag package supporting basic command-line flag parsing. We'll use this package to implement our example command-line program.", "flag package": "5.flag package: Command-line flags are a common way to specify options for command-line programs. For example, in wc -l the -l is a command-line flag. Go provides a flag package supporting basic command-line flag parsing. We'll use this package to implement our example command-line program.", "goroutine": "138.Goroutines and Channels: What are they? Goroutines are lightweight threads created and managed by the Go runtime. Channels are pipes that carry values of a specific type. Why are they useful? Goroutines allow functions to be executed concurrently, without needing to deal with the complications of operating system threads. Channels allow goroutines to produce results asynchronously. How are they used? Goroutines are created using the go keyword. Channels are defined as data types. Are there any or limitations? pitfalls Care must be taken to manage the direction of channels. Goroutines that share data require additional features. Are there any alternatives? Goroutines and channels are the built-in Go concurrency features, but some applications can rely on a single thread of execution, which is created by default to execute the main function.", "go routine": "138.Goroutines and Channels: What are they? Goroutines are lightweight threads created and managed by the Go runtime. Channels are pipes that carry values of a specific type. Why are they useful? Goroutines allow functions to be executed concurrently, without needing to deal with the complications of operating system threads. Channels allow goroutines to produce results asynchronously. How are they used? Goroutines are created using the go keyword. Channels are defined as data types. Are there any or limitations? pitfalls Care must be taken to manage the direction of channels. Goroutines that share data require additional features. Are there any alternatives? Goroutines and channels are the built-in Go concurrency features, but some applications can rely on a single thread of execution, which is created by default to execute the main function.", "goroutines": "138.Goroutines and Channels: What are they? Goroutines are lightweight threads created and managed by the Go runtime. Channels are pipes that carry values of a specific type. Why are they useful? Goroutines allow functions to be executed concurrently, without needing to deal with the complications of operating system threads. Channels allow goroutines to produce results asynchronously. How are they used? Goroutines are created using the go keyword. Channels are defined as data types. Are there any or limitations? pitfalls Care must be taken to manage the direction of channels. Goroutines that share data require additional features. Are there any alternatives? Goroutines and channels are the built-in Go concurrency features, but some applications can rely on a single thread of execution, which is created by default to execute the main function.", "channels": "138.Goroutines and Channels: What are they? Goroutines are lightweight threads created and managed by the Go runtime. Channels are pipes that carry values of a specific type. Why are they useful? Goroutines allow functions to be executed concurrently, without needing to deal with the complications of operating system threads. Channels allow goroutines to produce results asynchronously. How are they used? Goroutines are created using the go keyword. Channels are defined as data types. Are there any or limitations? pitfalls Care must be taken to manage the direction of channels. Goroutines that share data require additional features. Are there any alternatives? Goroutines and channels are the built-in Go concurrency features, but some applications can rely on a single thread of execution, which is created by default to execute the main function.", "goroutines and channels": "138.Goroutines and Channels: What are they? Goroutines are lightweight threads created and managed by the Go runtime. Channels are pipes that carry values of a specific type. Why are they useful? Goroutines allow functions to be executed concurrently, without needing to deal with the complications of operating system threads. Channels allow goroutines to produce results asynchronously. How are they used? Goroutines are created using the go keyword. Channels are defined as data types. Are there any or limitations? pitfalls Care must be taken to manage the direction of channels. Goroutines that share data require additional features. Are there any alternatives? Goroutines and channels are the built-in Go concurrency features, but some applications can rely on a single thread of execution, which is created by default to execute the main function.", "channels and goroutines": "138.Goroutines and Channels: What are they? Goroutines are lightweight threads created and managed by the Go runtime. Channels are pipes that carry values of a specific type. Why are they useful? Goroutines allow functions to be executed concurrently, without needing to deal with the complications of operating system threads. Channels allow goroutines to produce results asynchronously. How are they used? Goroutines are created using the go keyword. Channels are defined as data types. Are there any or limitations? pitfalls Care must be taken to manage the direction of channels. Goroutines that share data require additional features. Are there any alternatives? Goroutines and channels are the built-in Go concurrency features, but some applications can rely on a single thread of execution, which is created by default to execute the main function.", "regular expressions": "189.Regular Expressions: The regular expressions used in this section perform basic matches, but the regexp package supports an extensive pattern syntax, which is described at https://pkg.go.dev/regexp/syntax@go1.17.1.", "regularexpressions": "189.Regular Expressions: The regular expressions used in this section perform basic matches, but the regexp package supports an extensive pattern syntax, which is described at https://pkg.go.dev/regexp/syntax@go1.17.1.", "regex": "189.Regular Expressions: The regular expressions used in this section perform basic matches, but the regexp package supports an extensive pattern syntax, which is described at https://pkg.go.dev/regexp/syntax@go1.17.1.", "currying": "Currying: Function currying is the practice of writing a function that takes a function (or functions) as input, and returns a new function.", "trimspace(s)": "TrimSpace(s): This function returns the string s without leading or trailing whitespace characters.", "trim(s, set)": "Trim(s, set): This function returns a string from which any leading or trailing characters contained in the string set are removed from the string s.", "trimLeft(s, set)": "This function returns the string s without any leading character contained in the string set. This function matches any of the specified characters—use the TrimPrefix function to remove a complete substring.", "trimright(s, set)": "This function returns the string s without any trailing character contained in the string set. This function matches any of the specified characters—use the TrimSuffix function to remove a complete substring.", "trimprefix(s, prefix)": "function returns the string s after removing the specified prefix string. This function removes the complete prefix string—use the TrimLeft function to remove characters from a set.", "trimsuffix(s, suffix)": "function returns the string s after removing the specified suffix string. This function removes the complete suffix string—use the TrimRight function to remove characters from a set.", "trimfunc(s, func)": "TrimFunc(s, func): This function returns the string s from which any leading or trailing character for which a custom function returns true are removed.", "trimleftFunc(s, func)": "TrimLeftFunc(s, func): function returns the string s from which any leading character for which a custom function returns true are removed.", "trimrightFunc(s, func)": "TrimRightFunc(s, func): function returns the string s from which any trailing character for which a custom function returns true are removed.", "for": "56.for: Go allows loops only inside of functions. The for keyword is used to create loops that repeatedly execute statements. The most basic for loops will repeat indefinitely unless interrupted by the break keyword Incorporating the Condition into the Loop", "package": "Go applications are organized in packages. A package is a collection of source files located in the same directory. All source files in a directory must share the same package name. When a package is imported, only entities (functions, types, variables, constants) whose names start with a capital letter can be used / accessed. The recommended style of naming in Go is that identifiers will be named using camelCase, except for those meant to be accessible across packages which should be PascalCase. EXAMPLE: || package lasagna || ", "packages": "Go applications are organized in packages. A package is a collection of source files located in the same directory. All source files in a directory must share the same package name. When a package is imported, only entities (functions, types, variables, constants) whose names start with a capital letter can be used / accessed. The recommended style of naming in Go is that identifiers will be named using camelCase, except for those meant to be accessible across packages which should be PascalCase. EXAMPLE: || package lasagna || ", "variable": "Variables Go is statically-typed, which means all variables must have a defined type at compile-time.Variables can be defined by explicitly specifying a type, EXAMPLE: var explicit int || Explicitly typed||", "variables": "Variables Go is statically-typed, which means all variables must have a defined type at compile-time.Variables can be defined by explicitly specifying a type, EXAMPLE: var explicit int || Explicitly typed||", "constant": "Constants hold a piece of data just like variables, but their value cannot change during the execution of the program.Constants are defined using the const keyword and can be numbers, characters, strings or booleans. EXAMPLE: const Age = 21", "constants": "Constants hold a piece of data just like variables, but their value cannot change during the execution of the program.Constants are defined using the const keyword and can be numbers, characters, strings or booleans. EXAMPLE: const Age = 21", "function": "Go functions accept zero or more parameters. Parameters must be explicitly typed, there is no type inference. Values are returned from functions using the return keyword. A function is invoked by specifying the function name and passing arguments for each of the function's parameters.", "functions": "Go functions accept zero or more parameters. Parameters must be explicitly typed, there is no type inference. Values are returned from functions using the return keyword. A function is invoked by specifying the function name and passing arguments for each of the function's parameters.", "comment": "Note that Go supports two types of comments. Single line comments are preceded by // and multiline comments are inserted between /* and */.", "comments": "Note that Go supports two types of comments. Single line comments are preceded by // and multiline comments are inserted between /* and */.", "bool": "Booleans in Go are represented by the predeclared boolean type bool, which values can be either true or false. It's a defined type.", "boolean": "Booleans in Go are represented by the predeclared boolean type bool, which values can be either true or false. It's a defined type.", "booleans": "Booleans in Go are represented by the predeclared boolean type bool, which values can be either true or false. It's a defined type.", "function comment": "A function comment should be written directly before the function declaration. It should be a full sentence that starts with the function name. For example, an exported comment for the function Calculate should take the form Calculate .... It should also explain what arguments the function takes, what it does with them, and what its return values mean, ending in a period):", "function comments": "A function comment should be written directly before the function declaration. It should be a full sentence that starts with the function name. For example, an exported comment for the function Calculate should take the form Calculate .... It should also explain what arguments the function takes, what it does with them, and what its return values mean, ending in a period):", "number": "Go contains basic numeric types that can represent sets of either integer or floating-point values. Numbers can be converted to other numeric types through Type Conversion.", "numbers": "Go contains basic numeric types that can represent sets of either integer or floating-point values. Numbers can be converted to other numeric types through Type Conversion.", "int": "e.g. 0, 255, 2147483647. A signed integer that is at least 32 bits in size (value range of: -2147483648 through 2147483647). But this will depend on the systems architecture. Most modern computers are 64 bit, therefore int will be 64 bits in size (value rate of: -9223372036854775808 through 9223372036854775807).", "float64": "e.g. 0.0, 3.14. Contains the set of all 64-bit floating-point numbers.", "uint": "e.g. 0, 255. An unsigned integer that is the same size as int (value range of: 0 through 4294967295 for 32 bits and 0 through 18446744073709551615 for 64 bits). Go has shorthand assignment for the operators above (e.g. a += 5 is short for a = a + 5). Go also supports the increment and decrement statements ++ and -- (e.g. a++). For integer division, the remainder is dropped (e.g. 5 / 2 == 2).", "arithmetic operators": "Go supports many standard arithmetic operators. EXAMPLE: + , - , / , % . For integer division, the remainder is dropped (e.g. 5 / 2 == 2).", "operator": "Go supports many standard arithmetic operators. EXAMPLE: + , - , / , % . For integer division, the remainder is dropped (e.g. 5 / 2 == 2).", "operators": "Go supports many standard arithmetic operators. EXAMPLE: + , - , / , % . For integer division, the remainder is dropped (e.g. 5 / 2 == 2).", "basic operator": "Go supports many standard arithmetic operators. EXAMPLE: + , - , / , % . For integer division, the remainder is dropped (e.g. 5 / 2 == 2).", "basic operators": "Go supports many standard arithmetic operators. EXAMPLE: + , - , / , % . For integer division, the remainder is dropped (e.g. 5 / 2 == 2).", "converting between types": "Converting between types is done via a function with the name of the type to convert to. ", "arithmetic operations on different types": "In many languages you can perform arithmetic operations on different types of variables, but in Go this gives an error. ", "string": "A string in Go is an immutable sequence of bytes, which don't necessarily have to represent characters.", "reflection": "the Go support for reflection, which allows an application to work with types that are not known when the project is compiled, which is useful for creating APIs that will be used by other projects, for example. Reflection allows types and values to be inspected at runtime, even if those types were not defined at compile time. use this word for get example: reflection example", "reflections": "the Go support for reflection, which allows an application to work with types that are not known when the project is compiled, which is useful for creating APIs that will be used by other projects, for example. Reflection allows types and values to be inspected at runtime, even if those types were not defined at compile time. use this word for get example: reflection example", "using reflection": "the Go support for reflection, which allows an application to work with types that are not known when the project is compiled, which is useful for creating APIs that will be used by other projects, for example. Reflection allows types and values to be inspected at runtime, even if those types were not defined at compile time. use this word for get example: reflection example", "what is reflection?": "the Go support for reflection, which allows an application to work with types that are not known when the project is compiled, which is useful for creating APIs that will be used by other projects, for example. Reflection allows types and values to be inspected at runtime, even if those types were not defined at compile time. use this word for get example: reflection example", "what is reflection ?": "the Go support for reflection, which allows an application to work with types that are not known when the project is compiled, which is useful for creating APIs that will be used by other projects, for example. Reflection allows types and values to be inspected at runtime, even if those types were not defined at compile time. use this word for get example: reflection example", "what is reflection in Go?": "the Go support for reflection, which allows an application to work with types that are not known when the project is compiled, which is useful for creating APIs that will be used by other projects, for example. Reflection allows types and values to be inspected at runtime, even if those types were not defined at compile time. use this word for get example: reflection example", "what is reflection in Go ?": "the Go support for reflection, which allows an application to work with types that are not known when the project is compiled, which is useful for creating APIs that will be used by other projects, for example. Reflection allows types and values to be inspected at runtime, even if those types were not defined at compile time. use this word for get example: reflection example", "why is reflection useful?": "Reflection is useful when writing code that relies on types that will be defined in the future, such as when writing an API that will be used in other projects. use this word for get example: reflection example", "why is reflection useful ?": "Reflection is useful when writing code that relies on types that will be defined in the future, such as when writing an API that will be used in other projects. use this word for get example: reflection example", "why is a reflection useful?": "Reflection is useful when writing code that relies on types that will be defined in the future, such as when writing an API that will be used in other projects. use this word for get example: reflection example", "why is a reflection useful ?": "Reflection is useful when writing code that relies on types that will be defined in the future, such as when writing an API that will be used in other projects. use this word for get example: reflection example", "how is reflection used?": "The reflect package provides features that allow types and values to be reflected, such that they can be used without explicit knowledge of the data types in use. use this word for get example: reflection example", "how is reflection used ?": "The reflect package provides features that allow types and values to be reflected, such that they can be used without explicit knowledge of the data types in use. use this word for get example: reflection example", "why the need for reflection?": "The Go type system is rigorously enforced, which means you can't use a value of one type when a different type is inspected. sometime this is broken for wrong implementing. in this case, the empty interface, which can be used to accept any type. use this word for get example: reflection example", "why the need for reflection ?": "The Go type system is rigorously enforced, which means you can't use a value of one type when a different type is inspected. sometime this is broken for wrong implementing. in this case, the empty interface, which can be used to accept any type. use this word for get example: reflection example", "reflect package": "The reflect package provides the Go reflection features, and the key functions are called TypeOf and ValueOf", "generic": "Generics help you to do more with less code, by using generic types instead of concrete types. While Generics have been part of other programming languages for quite some time, Generics was only recently added to Golang as of the 1.18 release in 2022. Generics can be used to define functions, structs and maps.", "generics": "Generics help you to do more with less code, by using generic types instead of concrete types. While Generics have been part of other programming languages for quite some time, Generics was only recently added to Golang as of the 1.18 release in 2022. Generics can be used to define functions, structs and maps.", "what is the generics": "Generics help you to do more with less code, by using generic types instead of concrete types. While Generics have been part of other programming languages for quite some time, Generics was only recently added to Golang as of the 1.18 release in 2022. Generics can be used to define functions, structs and maps.", "what is the generics?": "Generics help you to do more with less code, by using generic types instead of concrete types. While Generics have been part of other programming languages for quite some time, Generics was only recently added to Golang as of the 1.18 release in 2022. Generics can be used to define functions, structs and maps.", "what is the generics ?": "Generics help you to do more with less code, by using generic types instead of concrete types. While Generics have been part of other programming languages for quite some time, Generics was only recently added to Golang as of the 1.18 release in 2022. Generics can be used to define functions, structs and maps.", "go generics": "Generics help you to do more with less code, by using generic types instead of concrete types. While Generics have been part of other programming languages for quite some time, Generics was only recently added to Golang as of the 1.18 release in 2022. Generics can be used to define functions, structs and maps.", "generics go": "Generics help you to do more with less code, by using generic types instead of concrete types. While Generics have been part of other programming languages for quite some time, Generics was only recently added to Golang as of the 1.18 release in 2022. Generics can be used to define functions, structs and maps.", "generics in go": "Generics help you to do more with less code, by using generic types instead of concrete types. While Generics have been part of other programming languages for quite some time, Generics was only recently added to Golang as of the 1.18 release in 2022. Generics can be used to define functions, structs and maps.", "beego": "beego is used for rapid development of RESTful APIs, web apps and backend services in Go. It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding. Package beego provide a MVC framework beego: an open-source, high-performance, modular, full-stack web framework It is used for rapid development of RESTful APIs, web apps and backend services in Go. beego is inspired by Tornado, Sinatra and Flask with the added benefit of some Go-specific features such as interfaces and struct embedding.", "beego framework": "beego is used for rapid development of RESTful APIs, web apps and backend services in Go. It is inspired by Tornado, Sinatra and Flask. beego has some Go-specific features such as interfaces and struct embedding. Package beego provide a MVC framework beego: an open-source, high-performance, modular, full-stack web framework It is used for rapid development of RESTful APIs, web apps and backend services in Go. beego is inspired by Tornado, Sinatra and Flask with the added benefit of some Go-specific features such as interfaces and struct embedding.", "database": "There are drivers for a wide range of databases, and a list can be found at https://github.com/golang/go/wiki/sqldrivers", "data base": "There are drivers for a wide range of databases, and a list can be found at https://github.com/golang/go/wiki/sqldrivers", "what is the database?": "The database/sql package provides features for working with SQL databases.", "what is the sql package?": "The database/sql package provides features for working with SQL databases.", "what is the database-sql package?": "The database/sql package provides features for working with SQL databases.", "what is the database/sql package?": "The database/sql package provides features for working with SQL databases.", "how is the database used?": "Relational databases remain the most effective way of storing large amounts of structured data and are used in most large projects.", "how is the database/sql used?": "Relational databases remain the most effective way of storing large amounts of structured data and are used in most large projects.", "Are in database any pitfalls or limitations?": "These features do not automatically populate struct fields from result rows.", "Are in database/sql any pitfalls or limitations?": "These features do not automatically populate struct fields from result rows.", "Are in sql any pitfalls or limitations?": "These features do not automatically populate struct fields from result rows.", "installing a database driver": "Run the command:==> go get modernc.org/sqlite Most database servers are set up separately so that the database driver opens a connection to a separate process. SQLite is an embedded database and is included in the driver package, which means no additional configuration is required.", "kafka": "Apache Kafka is an open-source distributed event streaming platform used for building real-time data pipelines and streaming applications. It is designed to handle high-throughput, fault-tolerant, and scalable messaging. Kafka allows you to publish and subscribe to streams of records, store them in a fault-tolerant way, and process them as they occur.", "apacheKafka": "Apache Kafka is an open-source distributed event streaming platform used for building real-time data pipelines and streaming applications. It is designed to handle high-throughput, fault-tolerant, and scalable messaging. Kafka allows you to publish and subscribe to streams of records, store them in a fault-tolerant way, and process them as they occur.", "apache Kafka": "Apache Kafka is an open-source distributed event streaming platform used for building real-time data pipelines and streaming applications. It is designed to handle high-throughput, fault-tolerant, and scalable messaging. Kafka allows you to publish and subscribe to streams of records, store them in a fault-tolerant way, and process them as they occur.", "what is exactly kafka apache for golang?": "Apache Kafka is an open-source distributed event streaming platform used for building real-time data pipelines and streaming applications. It is designed to handle high-throughput, fault-tolerant, and scalable messaging. Kafka allows you to publish and subscribe to streams of records, store them in a fault-tolerant way, and process them as they occur.", "what is exactly kafka apache for golang ?": "Apache Kafka is an open-source distributed event streaming platform used for building real-time data pipelines and streaming applications. It is designed to handle high-throughput, fault-tolerant, and scalable messaging. Kafka allows you to publish and subscribe to streams of records, store them in a fault-tolerant way, and process them as they occur.", "what is kafka apache for golang?": "Apache Kafka is an open-source distributed event streaming platform used for building real-time data pipelines and streaming applications. It is designed to handle high-throughput, fault-tolerant, and scalable messaging. Kafka allows you to publish and subscribe to streams of records, store them in a fault-tolerant way, and process them as they occur.", "what is kafka apache for golang ?": "Apache Kafka is an open-source distributed event streaming platform used for building real-time data pipelines and streaming applications. It is designed to handle high-throughput, fault-tolerant, and scalable messaging. Kafka allows you to publish and subscribe to streams of records, store them in a fault-tolerant way, and process them as they occur.", "strings": "The strings package contains many useful functions to work on strings. For more information about string functions, check out the strings package documentation. Link: https://pkg.go.dev/strings", "package tar": "Package tar implements access to tar archives.", "package zip": "Package zip provides support for reading and writing ZIP archives.", "package arena": "The arena package provides the ability to allocate memory for a collection of Go values and free that space manually all at once, safely.", "package bufio": "bufio implements buffered I/O. It wraps an io.Reader or io.Writer object, creating another object (Reader or Writer) that also implements the interface but provides buffering and some help for textual I/O. ", "package builtin": "Package builtin provides documentation for Go's predeclared identifiers.", "package bytes": "bytes implements functions for the manipulation of byte slices.", "package cmp": "Package cmp provides types and functions related to comparing ordered values. Ordered is a constraint that permits any ordered type: any type // that supports the operators < <= >= >.// If future releases of Go add new ordered types,// this constraint will be modified to include them.", "package bzip2": "Package bzip2 implements bzip2 decompression.", "package flate": "Package flate implements the DEFLATE compressed data format, described in RFC 1951.", "package gzip": "Package gzip implements reading and writing of gzip format compressed files, as specified in RFC 1952.", "package lzw": "Package lzw implements the Lempel-Ziv-Welch compressed data format, described in T. A. Welch, “A Technique for High-Performance Data Compression”, Computer, 17(6) (June 1984), pp 8-19. ", "package zlib": "Package zlib implements reading and writing of zlib format compressed data, as specified in RFC 1950. ", "package reflect": "The reflect package provides features that allow types and values to be reflected, such that they can be used without explicit knowledge of the data types in use.", "server": "server is a service for connection between clients and hosts. for get example of Go please write this: server example", "identifying byte slices": "Using the comparison operator is also a good way of using the Bytes method safety. The Bytes method will panic if it is called on any type other than a slice of bytes, but the Kind method only indicates slices and not their contents.", "go tool dist list": `488🚀 This will provide you a list of operating systems and architectures separated by / characters:`, "tool dist list": `488🚀 This will provide you a list of operating systems and architectures separated by / characters:`, "dist list": `488🚀 This will provide you a list of operating systems and architectures separated by / characters:`, "atoi()": "47🚀 Atoi(str) 🔔 This function parses a string into a base 10 int and is equivalent to calling ParseInt(str, 10, 0)", "atoi(str)": "47🚀 Atoi(str) 🔔 This function parses a string into a base 10 int and is equivalent to calling ParseInt(str, 10, 0)", "atoi(string)": "47🚀 Atoi(str) 🔔 This function parses a string into a base 10 int and is equivalent to calling ParseInt(str, 10, 0)", "formatint()": "49🚀 FormatInt(value, base) 🔔 This function returns a string representation of the specified int64 value, expressed in the specified base.", "formatint(value, base)": "49🚀 FormatInt(value, base) 🔔 This function returns a string representation of the specified int64 value, expressed in the specified base.", "formatint(val, base)": "49🚀 FormatInt(value, base) 🔔 This function returns a string representation of the specified int64 value, expressed in the specified base.", "formatuint()": "50🚀 FormatUint(val, base) 🔔 This function returns a string representation of the specified uint64 value, expressed in the specified base.", "formatuint(val, base)": "50🚀 FormatUint(val, base) 🔔 This function returns a string representation of the specified uint64 value, expressed in the specified base.", "formatfloat()": "51🚀 FormatFloat(val, format, precision, size) 🔔 This function returns a string representation of the specified float64 value, expressed using the specified format, precision, and size.", "formatfloat(1,2,3,4)": "51🚀 FormatFloat(val, format, precision, size) 🔔This function returns a string representation of the specified float64 value, expressed using the specified format, precision, and size.", "formatfloat(val, format, precision, size)": "51🚀 FormatFloat(val, format, precision, size) 🔔This function returns a string representation of the specified float64 value, expressed using the specified format, precision, and size.", "itoa()": "52🚀 Itoa(val) 🔔 This function returns a string representation of the specified int value, expressed using base 10.", "itoa(val)": "52🚀 Itoa(val) 🔔 This function returns a string representation of the specified int value, expressed using base 10.", "itoa(value)": "52🚀 Itoa(val) 🔔 This function returns a string representation of the specified int value, expressed using base 10.", "islower(rune)": "165🚀 IsLower(rune) 🔔 This function returns true if the specified rune is lowercase.", "islower()": "165🚀 IsLower(rune) 🔔 This function returns true if the specified rune is lowercase.", "islower(r)": "165🚀 IsLower(rune) 🔔 This function returns true if the specified rune is lowercase.", "islower(runes)": "165🚀 IsLower(rune) 🔔 This function returns true if the specified rune is lowercase.", "tolower(rune)": "165🚀 ToLower(rune) 🔔 This function returns the lowercase rune associated with the specified rune.", "tolower()": "165🚀 ToLower(rune) 🔔 This function returns the lowercase rune associated with the specified rune.", "tolower(r)": "165🚀 ToLower(rune) 🔔 This function returns the lowercase rune associated with the specified rune.", "tolower(runes)": "165🚀 ToLower(rune) 🔔 This function returns the lowercase rune associated with the specified rune.", "isupper(rune)": "165🚀 IsUpper(rune) 🔔 This function returns true if the specified rune is uppercase.", "isupper(r)": "165🚀 IsUpper(rune) 🔔 This function returns true if the specified rune is uppercase.", "isupper(runes)": "165🚀 IsUpper(rune) 🔔 This function returns true if the specified rune is uppercase.", "isupper()": "165🚀 IsUpper(rune) 🔔 This function returns true if the specified rune is uppercase.", "toupper(rune)": "165🚀 ToUpper(rune) 🔔 This function returns the upper rune associated with the specified rune.", "toupper()": "165🚀 ToUpper(rune) 🔔 This function returns the upper rune associated with the specified rune.", "toupper(r)": "165🚀 ToUpper(rune) 🔔 This function returns the upper rune associated with the specified rune.", "toupper(runes)": "165🚀 ToUpper(rune) 🔔 This function returns the upper rune associated with the specified rune.", "istitle(rune)": "165🚀 IsTitle(rune) 🔔 This function returns true if the specified rune is title case.", "istitle()": "165🚀 IsTitle(rune) 🔔 This function returns true if the specified rune is title case.", "istitle(r)": "165🚀 IsTitle(rune) 🔔 This function returns true if the specified rune is title case.", "istitle(runes)": "165🚀 IsTitle(rune) 🔔 This function returns true if the specified rune is title case.", "totitle(rune)": "165🚀 ToTitle(rune) 🔔 This function returns the title case rune associated with the specified rune.", "totitle()": "165🚀 ToTitle(rune) 🔔 This function returns the title case rune associated with the specified rune.", "totitle(r)": "165🚀 ToTitle(rune) 🔔 This function returns the title case rune associated with the specified rune.", "totitle(runes)": "165🚀 ToTitle(rune) 🔔 This function returns the title case rune associated with the specified rune.", "count(s, sub)": "167🚀 Count(s, sub) 🔔 This function returns an int that reports how many times the specified substring is found in the string s.", "count()": "167🚀 Count(s, sub) 🔔 This function returns an int that reports how many times the specified substring is found in the string s.", "count(string, sub)": "167🚀 Count(s, sub) 🔔 This function returns an int that reports how many times the specified substring is found in the string s.", "index(s, sub)": "167🚀 Index(s, sub) 🔔 These functions return the index of the first or last occurrence of a specified", "index()": "167🚀 Index(s, sub) 🔔 These functions return the index of the first or last occurrence of a specified", "index(string, sub)": "167🚀 Index(s, sub) 🔔 These functions return the index of the first or last occurrence of a specified", "indexany(s, chars)": "167🚀 IndexAny(s, chars) 🔔 These functions return the first or last occurrence of any character in the...", "indexany()": "167🚀 IndexAny(s, chars) 🔔 These functions return the first or last occurrence of any character in the...", "indexany(string, characters)": "167🚀 IndexAny(s, chars) 🔔 These functions return the first or last occurrence of any character in the...", "lastindexAny(s, chars)": "167🚀 LastIndexAny(s, chars) 🔔 specified string within the string s, or -1 if there is no occurrence.", "lastindexAny()": "167🚀 LastIndexAny(s, chars) 🔔 specified string within the string s, or -1 if there is no occurrence.", "lastindexAny(string, characters)": "167🚀 LastIndexAny(s, chars) 🔔 specified string within the string s, or -1 if there is no occurrence.", "indexbyte(s, b)": "167🚀 IndexByte(s, b) 🔔 These functions return the index of the first or last occurrence of a specified", "indexbyte()": "167🚀 IndexByte(s, b) 🔔 These functions return the index of the first or last occurrence of a specified", "indexbyte(string, byte)": "167🚀 IndexByte(s, b) 🔔 These functions return the index of the first or last occurrence of a specified", "lastindexByte(s, b)": "167🚀 LastIndexByte(s, b) 🔔 byte within the string s, or -1 if there is no occurrence.", "lastindexByte()": "167🚀 LastIndexByte(s, b) 🔔 byte within the string s, or -1 if there is no occurrence.", "lastindexByte(string, byte)": "167🚀 LastIndexByte(s, b) 🔔 byte within the string s, or -1 if there is no occurrence.", "indexfunc(s, func)": "167🚀 IndexFunc(s, func) 🔔 These functions return the index of the first or last occurrence of the...", "indexfunc()": "167🚀 IndexFunc(s, func) 🔔 These functions return the index of the first or last occurrence of the...", "indexfunc(string, function)": "167🚀 IndexFunc(s, func) 🔔 These functions return the index of the first or last occurrence of the...", "lastindexFunc(s, func)": "167🚀 LastIndexFunc(s, func) 🔔 character in the string s for which the specified function returns true, as described in the “Inspecting Strings with Custom Functions” section.", "lastindexFunc()": "167🚀 LastIndexFunc(s, func) 🔔 character in the string s for which the specified function returns true, as described in the “Inspecting Strings with Custom Functions” section.", "lastindexFunc(string, function)": "167🚀 LastIndexFunc(s, func) 🔔 character in the string s for which the specified function returns true, as described in the “Inspecting Strings with Custom Functions” section.", "contains(s, substr)": "161🚀 Contains(s, substr) 🔔 This function returns true if the string s contains substr and false if it does not.", "contains()": "161🚀 Contains(s, substr) 🔔 This function returns true if the string s contains substr and false if it does not.", "contains(string, substring)": "161🚀 Contains(s, substr) 🔔 This function returns true if the string s contains substr and false if it does not.", "containsany(s, substr)": "161🚀 ContainsAny(s, substr) 🔔 This function returns true if the string s contains any of the characters contained in the string substr.", "containsany()": "161🚀 ContainsAny(s, substr) 🔔 This function returns true if the string s contains any of the characters contained in the string substr.", "containsany(string, substring)": "161🚀 ContainsAny(s, substr) 🔔 This function returns true if the string s contains any of the characters contained in the string substr.", "containsrune(s, rune)": "161🚀 ContainsRune(s, rune) 🔔 This function returns true if the string s contains a specific rune.", "containsrune()": "161🚀 ContainsRune(s, rune) 🔔 This function returns true if the string s contains a specific rune.", "containsrune(string, rune)": "161🚀 ContainsRune(s, rune) 🔔 This function returns true if the string s contains a specific rune.", "containsrune(string, r)": "161🚀 ContainsRune(s, rune) 🔔 This function returns true if the string s contains a specific rune.", "containsrune(string, R)": "161🚀 ContainsRune(s, rune) 🔔 This function returns true if the string s contains a specific rune.", "containsrune(s, R)": "161🚀 ContainsRune(s, rune) 🔔 This function returns true if the string s contains a specific rune.", "containsrune(S, R)": "161🚀 ContainsRune(s, rune) 🔔 This function returns true if the string s contains a specific rune.", "equalfold(s1, s2)": "161🚀 EqualFold(s1, s2) 🔔 This function performs a case-insensitive comparison and returns true of strings s1 and s2 are the same.", "equalfold()": "161🚀 EqualFold(s1, s2) 🔔 This function performs a case-insensitive comparison and returns true of strings s1 and s2 are the same.", "equalfold(string, string)": "161🚀 EqualFold(s1, s2) 🔔 This function performs a case-insensitive comparison and returns true of strings s1 and s2 are the same.", "equalfold(string1, string2)": "161🚀 EqualFold(s1, s2) 🔔 This function performs a case-insensitive comparison and returns true of strings s1 and s2 are the same.", "hasprefix(s, prefix)": "161🚀 HasPrefix(s, prefix) 🔔 This function returns true if the string s begins with the string prefix.", "hasprefix(s, p)": "161🚀 HasPrefix(s, prefix) 🔔 This function returns true if the string s begins with the string prefix.", "hasprefix(string, p)": "161🚀 HasPrefix(s, prefix) 🔔 This function returns true if the string s begins with the string prefix.", "hasprefix(string, prefix)": "161🚀 HasPrefix(s, prefix) 🔔 This function returns true if the string s begins with the string prefix.", "hasprefix()": "161🚀 HasPrefix(s, prefix) 🔔 This function returns true if the string s begins with the string prefix.", "hassuffix(s, suffix)": "161🚀 HasSuffix(s, suffix) 🔔 This function returns true if the string ends with the string suffix.", "hassuffix(s, suf)": "161🚀 HasSuffix(s, suffix) 🔔 This function returns true if the string ends with the string suffix.", "hassuffix(string, suf)": "161🚀 HasSuffix(s, suffix) 🔔 This function returns true if the string ends with the string suffix.", "hassuffix(string, sufix)": "161🚀 HasSuffix(s, suffix) 🔔 This function returns true if the string ends with the string suffix.", "hassuffix()": "161🚀 HasSuffix(s, suffix) 🔔 This function returns true if the string ends with the string suffix.", "hassuffix(string, string)": "161🚀 HasSuffix(s, suffix) 🔔 This function returns true if the string ends with the string suffix.", "fields(s)": "169🚀 Fields(s) 🔔 This function splits a string on whitespace characters and returns a slice containing the nonwhitespace sections of the string s.", "fields()": "169🚀 Fields(s) 🔔 This function splits a string on whitespace characters and returns a slice containing the nonwhitespace sections of the string s.", "fields(string)": "169🚀 Fields(s) 🔔 This function splits a string on whitespace characters and returns a slice containing the nonwhitespace sections of the string s.", "fieldsfunc(s, func)": "169🚀 FieldsFunc(s, func) 🔔 This function splits the string s on the characters for which a custom function returns true and returns a slice containing the remaining sections of the string.", "fieldsfunc()": "169🚀 FieldsFunc(s, func) 🔔 This function splits the string s on the characters for which a custom function returns true and returns a slice containing the remaining sections of the string.", "fieldsfunc(string, function)": "169🚀 FieldsFunc(s, func) 🔔 This function splits the string s on the characters for which a custom function returns true and returns a slice containing the remaining sections of the string.", "split(s, sub)": "169🚀 Split(s, sub) 🔔 This function splits the string s on every occurrence of the specified substring, returning a string slice. If the separator is the empty string, then the slice will contain strings for each character.", "split()": "169🚀 Split(s, sub) 🔔 This function splits the string s on every occurrence of the specified substring, returning a string slice. If the separator is the empty string, then the slice will contain strings for each character.", "split(string, sub)": "169🚀 Split(s, sub) 🔔 This function splits the string s on every occurrence of the specified substring, returning a string slice. If the separator is the empty string, then the slice will contain strings for each character.", "splitn(s, sub, max)": "169🚀 SplitN(s, sub, max) 🔔 This function is similar to Split, but accepts an additional int argument that specifies the maximum number of substrings to return. The last substring in the result slice will contain the unsplit portion of the source string.", "splitn()": "169🚀 SplitN(s, sub, max) 🔔 This function is similar to Split, but accepts an additional int argument that specifies the maximum number of substrings to return. The last substring in the result slice will contain the unsplit portion of the source string.", "splitn(string, sub, max)": "169🚀 SplitN(s, sub, max) 🔔 This function is similar to Split, but accepts an additional int argument that specifies the maximum number of substrings to return. The last substring in the result slice will contain the unsplit portion of the source string.", "splitafter(s, sub)": "169🚀 SplitAfter(s, sub) 🔔 This function is similar to Split but includes the substring used in the results.", "splitafter()": "169🚀 SplitAfter(s, sub) 🔔 This function is similar to Split but includes the substring used in the results.", "splitafter(string, sub)": "169🚀 SplitAfter(s, sub) 🔔 This function is similar to Split but includes the substring used in the results.", "splitaftern(s, sub, max)": "169🚀 SplitAfterN(s, sub, max) 🔔 This function is similar to SplitAfter, but accepts an additional int argument that specifies the maximum number of substrings to return.", "splitaftern()": "169🚀 SplitAfterN(s, sub, max) 🔔 This function is similar to SplitAfter, but accepts an additional int argument that specifies the maximum number of substrings to return.", "splitaftern(string, sub, max)": "169🚀 SplitAfterN(s, sub, max) 🔔 This function is similar to SplitAfter, but accepts an additional int argument that specifies the maximum number of substrings to return.", "replace(s, old, new, n)": "179🚀 Replace(s, old, new, n) 🔔 This function alters the string s by replacing occurrences of the string old with the string new. The maximum number of occurrences that will be replaced is specified by the int argument n.", "replace()": "179🚀 Replace(s, old, new, n) 🔔 This function alters the string s by replacing occurrences of the string old with the string new. The maximum number of occurrences that will be replaced is specified by the int argument n. Replace(s): This method returns a string for which all the replacements specified with the constructor have been performed on the string s.", "replace(1, 2, 3, 4)": "179🚀 Replace(s, old, new, n) 🔔 This function alters the string s by replacing occurrences of the string old with the string new. The maximum number of occurrences that will be replaced is specified by the int argument n.", "replace(1,2,3,4)": "179🚀 Replace(s, old, new, n) 🔔 This function alters the string s by replacing occurrences of the string old with the string new. The maximum number of occurrences that will be replaced is specified by the int argument n.", "replace(string, old, new, number)": "179🚀 Replace(s, old, new, n) 🔔 This function alters the string s by replacing occurrences of the string old with the string new. The maximum number of occurrences that will be replaced is specified by the int argument n.", "replaceall(s, old, new)": "179🚀 ReplaceAll(s, old, new) 🔔 This function alters the string s by replacing all occurrences of the string old with the string new. Unlike the Replace function, there is no limit on the number of occurrences that will be replaced.", "replaceall()": "179🚀 ReplaceAll(s, old, new) 🔔 This function alters the string s by replacing all occurrences of the string old with the string new. Unlike the Replace function, there is no limit on the number of occurrences that will be replaced.", "replaceall(string, old, new)": "179🚀 ReplaceAll(s, old, new) 🔔 This function alters the string s by replacing all occurrences of the string old with the string new. Unlike the Replace function, there is no limit on the number of occurrences that will be replaced.", "map(func, s)": "179🚀 Map(func, s) 🔔 This function generates a string by invoking the custom function for each character in the string s and concatenating the results. If the function produces a negative value, the current character is dropped without a replacement.", "map()": "179🚀 Map(func, s) 🔔 This function generates a string by invoking the custom function for each character in the string s and concatenating the results. If the function produces a negative value, the current character is dropped without a replacement.", "map(function, string)": "179🚀 Map(func, s) 🔔 This function generates a string by invoking the custom function for each character in the string s and concatenating the results. If the function produces a negative value, the current character is dropped without a replacement.", "replace(s)": "183🚀 Replace(s) 🔔 This method returns a string for which all the replacements specified with the constructor have been performed on the string s. Replace(s): This method returns a string for which all the replacements specified with the constructor have been performed on the string s.", "replace(string)": "183🚀 Replace(s) 🔔 This method returns a string for which all the replacements specified with the constructor have been performed on the string s.", "replace(str)": "183🚀 Replace(s) 🔔 This method returns a string for which all the replacements specified with the constructor have been performed on the string s.", "writestring(writer, s)": "183🚀 WriteString(writer, s) 🔔 This method is used to perform the replacements specified with the constructor and write the results to an io.Writer. This method is used to perform the replacements specified with the constructor and write the results to an io.Writer", "writestring(w, s)": "183🚀 WriteString(writer, s) 🔔 This method is used to perform the replacements specified with the constructor and write the results to an io.Writer", "writestring(w, str)": "183🚀 WriteString(writer, s) 🔔 This method is used to perform the replacements specified with the constructor and write the results to an io.Writer", "writestring(w, string)": "183🚀 WriteString(writer, s) 🔔 This method is used to perform the replacements specified with the constructor and write the results to an io.Writer", "join(slice, sep)": "184🚀 Join(slice, sep) 🔔 This function combines the elements in the specified string slice, with the specified separator string placed between elements. Join(slice, sep): function combines the elements in the specified string slice, with the specified separator string placed between elements.", "join(slice, specified)": "184🚀 Join(slice, sep) 🔔 This function combines the elements in the specified string slice, with the specified separator string placed between elements.", "join()": "184🚀 Join(slice, sep) 🔔 This function combines the elements in the specified string slice, with the specified separator string placed between elements.", "repeat(s, count)": "184🚀 Repeat(s, count) 🔔 This function generates a string by repeating the string s for a specified number of times.", "repeat(str, count)": "184🚀 Repeat(s, count) 🔔 This function generates a string by repeating the string s for a specified number of times.", "writestring(s)": "186🚀 WriteString(s) 🔔 This method appends the string s to the string being built.", "writestring(string)": "186🚀 WriteString(s) 🔔 This method appends the string s to the string being built.", "writestring()": "186🚀 WriteString(s) 🔔 This method appends the string s to the string being built.", "writerune(r)": "186🚀 WriteRune(r) 🔔 This method appends the character r to the string being built.", "writerune()": "186🚀 WriteRune(r) 🔔 This method appends the character r to the string being built.", "writebyte(b)": "186🚀 WriteByte(b) 🔔 This method appends the byte b to the string being built.", "writebyte(byte)": "186🚀 WriteByte(b) 🔔 This method appends the byte b to the string being built.", "writebyte()": "186🚀 WriteByte(b) 🔔 This method appends the byte b to the string being built.", "string()": "186🚀 String() 🔔 This method returns the string that has been created by the builder.", "reset()": "186🚀 Reset() 🔔 This method resets the string created by the builder.", "len()": "186🚀 Len() 🔔 This method returns the number of bytes used to store the string created by the builder.", "cap()": "186🚀 Cap() 🔔 This method returns the number of bytes that have been allocated by the builder.", "grow(size)": "186🚀 Grow(size) 🔔 This method increases the number of bytes used allocated by the builder to store the string that is being built.", "grow(siz)": "186🚀 Grow(size) 🔔 This method increases the number of bytes used allocated by the builder to store the string that is being built.", "grow(sizes)": "186🚀 Grow(size) 🔔 This method increases the number of bytes used allocated by the builder to store the string that is being built.", "matchstring(s)": "192🚀 MatchString(s) 🔔 This method returns true if the string s matches the compiled pattern.", "matchstring(str)": "192🚀 MatchString(s) 🔔 This method returns true if the string s matches the compiled pattern.", "matchstring(string)": "192🚀 MatchString(s) 🔔 This method returns true if the string s matches the compiled pattern.", "matchstring(strings)": "192🚀 MatchString(s) 🔔 This method returns true if the string s matches the compiled pattern.", "findstringindex(s)": "192🚀 FindStringIndex(s) 🔔 This method returns an int slice containing the location for the left most match made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findstringindex(str)": "192🚀 FindStringIndex(s) 🔔 This method returns an int slice containing the location for the left most match made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findstringindex(string)": "192🚀 FindStringIndex(s) 🔔 This method returns an int slice containing the location for the left most match made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findstringindex(strings)": "192🚀 FindStringIndex(s) 🔔 This method returns an int slice containing the location for the left most match made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findallstringindex(s, max)": "192🚀 FindAllStringIndex(s, max) 🔔 This method returns a slice of int slices that contain the location for all the matches made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findallstringindex(str, max)": "192🚀 FindAllStringIndex(s, max) 🔔 This method returns a slice of int slices that contain the location for all the matches made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findallstringindex(string, max)": "192🚀 FindAllStringIndex(s, max) 🔔 This method returns a slice of int slices that contain the location for all the matches made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findallstringindex(strings, max)": "192🚀 FindAllStringIndex(s, max) 🔔 This method returns a slice of int slices that contain the location for all the matches made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findstring(s)": "192🚀 FindString(s) 🔔 This method returns a string containing the left-most match made by the compiled pattern in the string s. An empty string will be returned if no match is made.", "findstring(str)": "192🚀 FindString(s) 🔔 This method returns a string containing the left-most match made by the compiled pattern in the string s. An empty string will be returned if no match is made.", "findstring(string)": "192🚀 FindString(s) 🔔 This method returns a string containing the left-most match made by the compiled pattern in the string s. An empty string will be returned if no match is made.", "findstring(strings)": "192🚀 FindString(s) 🔔 This method returns a string containing the left-most match made by the compiled pattern in the string s. An empty string will be returned if no match is made.", "findallstring(s, max)": "192🚀 FindAllString(s, max) 🔔 This method returns a string slice containing the matches made by the compiled pattern in the string s. The int argument max specifies the maximum number of matches, with -1 specifying no limit. A nil result is returned if there are no matches.", "findallstring(str, max)": "192🚀 FindAllString(s, max) 🔔 This method returns a string slice containing the matches made by the compiled pattern in the string s. The int argument max specifies the maximum number of matches, with -1 specifying no limit. A nil result is returned if there are no matches.", "findallstring(string, max)": "192🚀 FindAllString(s, max) 🔔 This method returns a string slice containing the matches made by the compiled pattern in the string s. The int argument max specifies the maximum number of matches, with -1 specifying no limit. A nil result is returned if there are no matches.", "split(s, max)": "192🚀 Split(s, max) 🔔 This method splits the string s using matches from the compiled pattern as separators and returns a slice containing the split substrings.", "split(str, max)": "192🚀 Split(s, max) 🔔 This method splits the string s using matches from the compiled pattern as separators and returns a slice containing the split substrings.", "split(string, max)": "192🚀 Split(s, max) 🔔 This method splits the string s using matches from the compiled pattern as separators and returns a slice containing the split substrings.", "findstringsubmatch(s)": "197🚀 FindStringSubmatch(s) 🔔 This method returns a slice containing the first match made by the pattern and the text for the subexpressions that the pattern defines.", "findstringsubmatch(str)": "197🚀 FindStringSubmatch(s) 🔔 This method returns a slice containing the first match made by the pattern and the text for the subexpressions that the pattern defines.", "findstringsubmatch(string)": "197🚀 FindStringSubmatch(s) 🔔 This method returns a slice containing the first match made by the pattern and the text for the subexpressions that the pattern defines.", "findallstringsubmatch(s, max)": "197🚀 FindAllStringSubmatch(s, max) 🔔 This method returns a slice containing all the matches and the text for the subexpressions. The int argument is used to specify the maximum number of matches. A value of -1 specifies all matches.", "findallstringsubmatch(str, max)": "197🚀 FindAllStringSubmatch(s, max) 🔔 This method returns a slice containing all the matches and the text for the subexpressions. The int argument is used to specify the maximum number of matches. A value of -1 specifies all matches.", "findallstringsubmatch(string, max)": "197🚀 FindAllStringSubmatch(s, max) 🔔 This method returns a slice containing all the matches and the text for the subexpressions. The int argument is used to specify the maximum number of matches. A value of -1 specifies all matches.", "findstringsubmatchindex(s)": "197🚀 FindStringSubmatchIndex(s) 🔔 This method is equivalent to FindStringSubmatch but returns indices rather than substrings. FindAllStringSubmatchIndex", "findstringsubmatchindex(str)": "197🚀 FindStringSubmatchIndex(s) 🔔 This method is equivalent to FindStringSubmatch but returns indices rather than substrings. FindAllStringSubmatchIndex", "findstringsubmatchindex(string)": "197🚀 FindStringSubmatchIndex(s) 🔔 This method is equivalent to FindStringSubmatch but returns indices rather than substrings. FindAllStringSubmatchIndex", "numsubexp()": "197🚀 NumSubexp() 🔔 This method returns the number of subexpressions.", "subexpindex(name)": "197🚀 SubexpIndex(name) 🔔 This method returns the index of the subexpression with the specified name or -1 if there is no such subexpression.", "subexpindex(names)": "197🚀 SubexpIndex(name) 🔔 This method returns the index of the subexpression with the specified name or -1 if there is no such subexpression.", "subexpindex(n)": "197🚀 SubexpIndex(name) 🔔 This method returns the index of the subexpression with the specified name or -1 if there is no such subexpression.", "subexpnames()": "197🚀 SubexpNames() 🔔 This method returns the names of the subexpressions, expressed in the order in which they are defined.", "replaceallstring(s, template)": "199🚀 ReplaceAllString(s, template) 🔔 This method replaces the matched portion of the string s with the specified template, which is expanded before it is included in the result to incorporate subexpressions.", "replaceallstring(str, temp)": "199🚀 ReplaceAllString(s, template) 🔔 This method replaces the matched portion of the string s with the specified template, which is expanded before it is included in the result to incorporate subexpressions.", "replaceallstring(string, temp)": "199🚀 ReplaceAllString(s, template) 🔔 This method replaces the matched portion of the string s with the specified template, which is expanded before it is included in the result to incorporate subexpressions.", "replaceallstring(strings, temp)": "199🚀 ReplaceAllString(s, template) 🔔 This method replaces the matched portion of the string s with the specified template, which is expanded before it is included in the result to incorporate subexpressions.", "replaceallstring(strings, template)": "199🚀 ReplaceAllString(s, template) 🔔 This method replaces the matched portion of the string s with the specified template, which is expanded before it is included in the result to incorporate subexpressions.", "replaceallstring(string, template)": "199🚀 ReplaceAllString(s, template) 🔔 This method replaces the matched portion of the string s with the specified template, which is expanded before it is included in the result to incorporate subexpressions.", "replaceallliteralstring(s, sub)": "199🚀 ReplaceAllLiteralString(s, sub) 🔔 This method replaces the matched portion of the string s with the specified content, which is included in the result without being expanded for subexpressions.", "replaceallliteralstring(str, sub)": "199🚀 ReplaceAllLiteralString(s, sub) 🔔 This method replaces the matched portion of the string s with the specified content, which is included in the result without being expanded for subexpressions.", "replaceallliteralstring(string, sub)": "199🚀 ReplaceAllLiteralString(s, sub) 🔔 This method replaces the matched portion of the string s with the specified content, which is included in the result without being expanded for subexpressions.", "replaceallliteralstring(string, specified)": "199🚀 ReplaceAllLiteralString(s, sub) 🔔 This method replaces the matched portion of the string s with the specified content, which is included in the result without being expanded for subexpressions.", "replaceallstringfunc(s, func)": "199🚀 ReplaceAllStringFunc(s, func) 🔔 This method replaces the matched portion of the string s with the result produced by the specified function.", "replaceallstringfunc(str, func)": "199🚀 ReplaceAllStringFunc(s, func) 🔔 This method replaces the matched portion of the string s with the result produced by the specified function.", "replaceallstringfunc(string, function)": "199🚀 ReplaceAllStringFunc(s, func) 🔔 This method replaces the matched portion of the string s with the result produced by the specified function.", "print(...vals)": "203🚀 Print(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out. Spaces are added between values that are not strings.", "print()": "203🚀 Print(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out. Spaces are added between values that are not strings.", "print(...)": "203🚀 Print(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out. Spaces are added between values that are not strings.", "print(...values)": "203🚀 Print(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out. Spaces are added between values that are not strings.", "println(...vals)": "203🚀 Println(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out, separated by spaces and followed by a newline character.", "println(vals)": "203🚀 Println(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out, separated by spaces and followed by a newline character.", "println()": "203🚀 Println(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out, separated by spaces and followed by a newline character.", "println(values)": "203🚀 Println(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out, separated by spaces and followed by a newline character.", "println(...value)": "203🚀 Println(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out, separated by spaces and followed by a newline character.", "println(...)": "203🚀 Println(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out, separated by spaces and followed by a newline character.", "fprint(writer, ...vals)": "203🚀 Fprint(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer, Spaces are added between values that are not strings.", "fprint(w, ...vals)": "203🚀 Fprint(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer, Spaces are added between values that are not strings.", "fprint(w, vals)": "203🚀 Fprint(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer, Spaces are added between values that are not strings.", "fprint()": "203🚀 Fprint(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer, Spaces are added between values that are not strings.", "fprint(wr, vals)": "203🚀 Fprint(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer, Spaces are added between values that are not strings.", "fprintln(writer, ...vals)": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "fprintln(wri, ...vals)": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "fprintln(w, ...vals)": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "fprintln(w, vals)": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "fprintln(w, values)": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "fprintln(w, v)": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "fprintln()": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "fprintln(wrt, vls)": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "sprintf(t, ...vals)": "206🚀 Sprintf(t, ...vals) 🔔 This function returns a string, which is created by processing the template t.", "sprintf(t, vals)": "206🚀 Sprintf(t, ...vals) 🔔 This function returns a string, which is created by processing the template t.", "sprintf()": "206🚀 Sprintf(t, ...vals) 🔔 This function returns a string, which is created by processing the template t.", "sprintf(1,...2)": "206🚀 Sprintf(t, ...vals) 🔔 This function returns a string, which is created by processing the template t.", "printf(t, ...vals)": "206🚀 Printf(t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to the standard out.", "printf(t, ...)": "206🚀 Printf(t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to the standard out.", "printf()": "206🚀 Printf(t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to the standard out.", "printf(1,2)": "206🚀 Printf(t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to the standard out.", "printf(1,...2)": "206🚀 Printf(t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to the standard out.", "fprintf(writer, t, ...vals)": "206🚀 Fprintf(writer, t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to a Writer, which is described in Chapter 20.", "fprintf(write, t, ...vals)": "206🚀 Fprintf(writer, t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to a Writer, which is described in Chapter 20.", "fprintf()": "206🚀 Fprintf(writer, t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to a Writer, which is described in Chapter 20.", "fprintf(1,2,3)": "206🚀 Fprintf(writer, t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to a Writer, which is described in Chapter 20.", "errorf(t, ...values)": "206🚀 Errorf(t, ...values) 🔔 This function creates an error by processing the template t. The remaining arguments are used as values for the template verbs. The result is an error value whose Error method returns the formatted string.", "errorf(t, ...val)": "206🚀 Errorf(t, ...values) 🔔 This function creates an error by processing the template t. The remaining arguments are used as values for the template verbs. The result is an error value whose Error method returns the formatted string.", "errorf(t, ...vals)": "206🚀 Errorf(t, ...values) 🔔 This function creates an error by processing the template t. The remaining arguments are used as values for the template verbs. The result is an error value whose Error method returns the formatted string.", "errorf()": "206🚀 Errorf(t, ...values) 🔔 This function creates an error by processing the template t. The remaining arguments are used as values for the template verbs. The result is an error value whose Error method returns the formatted string.", "errorf(1,2)": "206🚀 Errorf(t, ...values) 🔔 This function creates an error by processing the template t. The remaining arguments are used as values for the template verbs. The result is an error value whose Error method returns the formatted string.", "errorf(1,...2)": "206🚀 Errorf(t, ...values) 🔔 This function creates an error by processing the template t. The remaining arguments are used as values for the template verbs. The result is an error value whose Error method returns the formatted string.", "scan(...vals)": "220🚀 Scan(...vals) 🔔 This function reads text from the standard in and stores the space- separated values into specified arguments. Newlines are treated as spaces, and the function reads until it has received values for all of its arguments. The result is the number of values that have been read and an error that describes any problems.", "scan(...)": "220🚀 Scan(...vals) 🔔 This function reads text from the standard in and stores the space- separated values into specified arguments. Newlines are treated as spaces, and the function reads until it has received values for all of its arguments. The result is the number of values that have been read and an error that describes any problems.", "scan()": "220🚀 Scan(...vals) 🔔 This function reads text from the standard in and stores the space- separated values into specified arguments. Newlines are treated as spaces, and the function reads until it has received values for all of its arguments. The result is the number of values that have been read and an error that describes any problems.", "scan(...2)": "220🚀 Scan(...vals) 🔔 This function reads text from the standard in and stores the space- separated values into specified arguments. Newlines are treated as spaces, and the function reads until it has received values for all of its arguments. The result is the number of values that have been read and an error that describes any problems.", "scanln(...vals)": "220🚀 Scanln(...vals) 🔔 This function works in the same way as Scan but stops reading when it encounters a newline character.", "scanln(...)": "220🚀 Scanln(...vals) 🔔 This function works in the same way as Scan but stops reading when it encounters a newline character.", "scanln()": "220🚀 Scanln(...vals) 🔔 This function works in the same way as Scan but stops reading when it encounters a newline character.", "scanln(...2)": "220🚀 Scanln(...vals) 🔔 This function works in the same way as Scan but stops reading when it encounters a newline character.", "scanf(template, ...vals)": "220🚀 Scanf(template, ...vals) 🔔 This function works in the same way as Scan but uses a template string to select the values from the input it receives.", "scanf(temp, ...vals)": "220🚀 Scanf(template, ...vals) 🔔 This function works in the same way as Scan but uses a template string to select the values from the input it receives.", "scanf(temp, ...values)": "220🚀 Scanf(template, ...vals) 🔔 This function works in the same way as Scan but uses a template string to select the values from the input it receives.", "scanf(1,2)": "220🚀 Scanf(template, ...vals) 🔔 This function works in the same way as Scan but uses a template string to select the values from the input it receives.", "scanf(1,...2)": "220🚀 Scanf(template, ...vals) 🔔 This function works in the same way as Scan but uses a template string to select the values from the input it receives.", "scanf()": "220🚀 Scanf(template, ...vals) 🔔 This function works in the same way as Scan but uses a template string to select the values from the input it receives.", "fscan(reader, ...vals)": "220🚀 Fscan(reader, ...vals) 🔔 This function reads space-separated values from the specified reader, which is described in Chapter 20. Newlines are treated as spaces, and the function returns the number of values that have been read and an error that describes any problems.", "fscan(reade, ...vals)": "220🚀 Fscan(reader, ...vals) 🔔 This function reads space-separated values from the specified reader, which is described in Chapter 20. Newlines are treated as spaces, and the function returns the number of values that have been read and an error that describes any problems.", "fscan(reader, ...values)": "220🚀 Fscan(reader, ...vals) 🔔 This function reads space-separated values from the specified reader, which is described in Chapter 20. Newlines are treated as spaces, and the function returns the number of values that have been read and an error that describes any problems.", "fscan(reader, values)": "220🚀 Fscan(reader, ...vals) 🔔 This function reads space-separated values from the specified reader, which is described in Chapter 20. Newlines are treated as spaces, and the function returns the number of values that have been read and an error that describes any problems.", "fscan(1,2)": "220🚀 Fscan(reader, ...vals) 🔔 This function reads space-separated values from the specified reader, which is described in Chapter 20. Newlines are treated as spaces, and the function returns the number of values that have been read and an error that describes any problems.", "fscan()": "220🚀 Fscan(reader, ...vals) 🔔 This function reads space-separated values from the specified reader, which is described in Chapter 20. Newlines are treated as spaces, and the function returns the number of values that have been read and an error that describes any problems.", "fscanln(reader, ...vals)": "220🚀 Fscanln(reader, ...vals) 🔔 This function works in the same way as Fscan but stops reading when it encounters a newline character.", "fscanln(reader, ...values)": "220🚀 Fscanln(reader, ...vals) 🔔 This function works in the same way as Fscan but stops reading when it encounters a newline character.", "fscanln(reader, values)": "220🚀 Fscanln(reader, ...vals) 🔔 This function works in the same way as Fscan but stops reading when it encounters a newline character.", "fscanln(read, values)": "220🚀 Fscanln(reader, ...vals) 🔔 This function works in the same way as Fscan but stops reading when it encounters a newline character.", "fscanln(1,2)": "220🚀 Fscanln(reader, ...vals) 🔔 This function works in the same way as Fscan but stops reading when it encounters a newline character.", "fscanln()": "220🚀 Fscanln(reader, ...vals) 🔔 This function works in the same way as Fscan but stops reading when it encounters a newline character.", "fscanf(reader, template, ...vals)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(reader, template, ...values)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(reader, template, values)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(reader, temp, values)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(read, temp, values)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(read, temp, val)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(read, temp, vals)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(1,2,3)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(1, 2, 3)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf()": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "sscan(str, ...vals)": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscan(str, vals)": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscan(str, values)": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscan(string, values)": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscan(string, ...values)": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscan()": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscan(1,2)": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscan(1, 2)": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscanf(str, template, ...vals)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(string, template, ...vals)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(string, template, vals)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(string, template, values)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(str, template, values)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(str, temp, values)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(str, temp, vals)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(1,2,3)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(1, 2, 3)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf()": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanln(str, template, ...vals)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(string, template, ...vals)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(string, template, vals)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(string, template, values)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(str, temp, vals)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(str, template, vals)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(str, template, values)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(1,2,3)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(1, 2, 3)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln()": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "abs(val)": "226🚀 Abs(val) 🔔 This function returns the absolute value of a float64 value, meaning the distance from zero without considering direction.", "abs()": "226🚀 Abs(val) 🔔 This function returns the absolute value of a float64 value, meaning the distance from zero without considering direction.", "ceil(val)": "226🚀 Ceil(val) 🔔 This function returns the smallest integer that is equal to or greater than the specified float64 value. The result is also a float64 value, even though it represents an integer number.", "ceil()": "226🚀 Ceil(val) 🔔 This function returns the smallest integer that is equal to or greater than the specified float64 value. The result is also a float64 value, even though it represents an integer number.", "copysign(x, y)": "226🚀 Copysign(x, y) 🔔 This function returns a float64 value, which is the absolute value of x with the sign of y.", "copysign(1,2)": "226🚀 Copysign(x, y) 🔔 This function returns a float64 value, which is the absolute value of x with the sign of y.", "copysign(1, 2)": "226🚀 Copysign(x, y) 🔔 This function returns a float64 value, which is the absolute value of x with the sign of y.", "copysign()": "226🚀 Copysign(x, y) 🔔 This function returns a float64 value, which is the absolute value of x with the sign of y.", "floor(val)": "226🚀 Floor(val) 🔔 This function returns the largest integer that is smaller or equal to the specified float64 value. The result is also a float64 value, even though it represents an integer number.", "floor()": "226🚀 Floor(val) 🔔 This function returns the largest integer that is smaller or equal to the specified float64 value. The result is also a float64 value, even though it represents an integer number.", "max(x, y)": "226🚀 Max(x, y) 🔔 This function returns whichever of the specified float64 value is the largest.", "max(x,y)": "226🚀 Max(x, y) 🔔 This function returns whichever of the specified float64 value is the largest.", "max(1,2)": "226🚀 Max(x, y) 🔔 This function returns whichever of the specified float64 value is the largest.", "max(1, 2)": "226🚀 Max(x, y) 🔔 This function returns whichever of the specified float64 value is the largest.", "max()": "226🚀 Max(x, y) 🔔 This function returns whichever of the specified float64 value is the largest.", "min(x, y)": "226🚀 Min(x, y) 🔔 This function returns whichever of the specified float64 value is smallest.", "min(x,y)": "226🚀 Min(x, y) 🔔 This function returns whichever of the specified float64 value is smallest.", "min(1,2)": "226🚀 Min(x, y) 🔔 This function returns whichever of the specified float64 value is smallest.", "min(1, 2)": "226🚀 Min(x, y) 🔔 This function returns whichever of the specified float64 value is smallest.", "min()": "226🚀 Min(x, y) 🔔 This function returns whichever of the specified float64 value is smallest.", "mod(x, y)": "226🚀 Mod(x, y) 🔔 This function returns the remainder of x/y.", "mod(x,y)": "226🚀 Mod(x, y) 🔔 This function returns the remainder of x/y.", "mod(1,2)": "226🚀 Mod(x, y) 🔔 This function returns the remainder of x/y.", "mod(1, 2)": "226🚀 Mod(x, y) 🔔 This function returns the remainder of x/y.", "mod()": "226🚀 Mod(x, y) 🔔 This function returns the remainder of x/y.", "pow(x, y)": "226🚀 Pow(x, y) 🔔 This function returns x raised to the exponent y.", "pow(x,y)": "226🚀 Pow(x, y) 🔔 This function returns x raised to the exponent y.", "pow()": "226🚀 Pow(x, y) 🔔 This function returns x raised to the exponent y.", "pow(1,2)": "226🚀 Pow(x, y) 🔔 This function returns x raised to the exponent y.", "pow(1, 2)": "226🚀 Pow(x, y) 🔔 This function returns x raised to the exponent y.", "round(val)": "226🚀 Round(val) 🔔 This function rounds the specified value to the nearest integer, rounding half values up. The result is a float64 value, even though it represents an integer.", "round()": "226🚀 Round(val) 🔔 This function rounds the specified value to the nearest integer, rounding half values up. The result is a float64 value, even though it represents an integer.", "roundtoeven(val)": "226🚀 RoundToEven(val) 🔔 This function rounds the specified value to the nearest integer, rounding half values to the nearest even number. The result is a float64 value, even though it represents an integer.", "roundtoeven()": "226🚀 RoundToEven(val) 🔔 This function rounds the specified value to the nearest integer, rounding half values to the nearest even number. The result is a float64 value, even though it represents an integer.", "seed(s)": "228 🚀 Seed(s) 🔔 This function sets the seed value using the specified int64 value.", "seed()": "228 🚀 Seed(s) 🔔 This function sets the seed value using the specified int64 value.", "float32()": "228 🚀 Float32() 🔔 This function generates a random float32 value between 0 and 1.", "float64()": "228 🚀 Float64() 🔔 This function generates a random float64 value between 0 and 1.", "int()": "228 🚀 Int() 🔔 This function generates a random int value.", "intn(max)": "228 🚀 Intn(max) 🔔 This function generates a random int smaller than a specified value, as described after the table.", "intn()": "228 🚀 Intn(max) 🔔 This function generates a random int smaller than a specified value, as described after the table.", "uint32()": "228 🚀 UInt32() 🔔 This function generates a random uint32 value.", "uint64()": "228 🚀 UInt64() 🔔 This function generates a random uint64 value.", "shuffle(count, func)": "228 🚀 Shuffle(count, func) 🔔 This function is used to randomize the order of elements, as described after the table.", "float64s(slice)": "234🚀 Float64s(slice) 🔔 This function sorts a slice of float64 values. The elements are sorted in place.", "float64s()": "234🚀 Float64s(slice) 🔔 This function sorts a slice of float64 values. The elements are sorted in place.", "float64saresorted(slice)": "234🚀 Float64sAreSorted(slice) 🔔 This function returns true if the elements in the specified float64 slice are in order.", "float64saresorted()": "234🚀 Float64sAreSorted(slice) 🔔 This function returns true if the elements in the specified float64 slice are in order.", "ints(slice)": "234🚀 Ints(slice) 🔔 This function sorts a slice of int values. The elements are sorted in place.", "ints()": "234🚀 Ints(slice) 🔔 This function sorts a slice of int values. The elements are sorted in place.", "intsaresorted(slice)": "234🚀 IntsAreSorted(slice) 🔔 This function returns true if the elements in the specified int slice are in order.", "intsaresorted()": "234🚀 IntsAreSorted(slice) 🔔 This function returns true if the elements in the specified int slice are in order.", "strings(slice)": "234🚀 Strings(slice) 🔔 This function sorts a slice of string values. The elements are sorted in place.", "strings()": "234🚀 Strings(slice) 🔔 This function sorts a slice of string values. The elements are sorted in place.", "stringsaresorted(slice)": "234🚀 StringsAreSorted(slice) 🔔 This function returns true if the elements in the specified string slice are in order.", "stringsaresorted()": "234🚀 StringsAreSorted(slice) 🔔 This function returns true if the elements in the specified string slice are in order.", "typeof(val)": "478🚀 TypeOf(val) 🔔 This function returns a value that implements the Type interface, which describes the type of the specified value. There is a lot of detail behind the TypeOf and ValueOf functions and their results, and it is easy to lose sight of why reflection can be useful.", "valueof(val)": "478🚀 ValueOf(val) 🔔 This function returns a Value struct, which allows the specified value to be inspected and manipulated. There is a lot of detail behind the TypeOf and ValueOf functions and their results, and it is easy to lose sight of why reflection can be useful.", "name()": "481🚀 Name() 🔔 This method returns the name of the type.", "pkgpath()": "481🚀 PkgPath() 🔔 This method returns the package path for the type. The empty string is returned for built-in types, such as int and bool.", "kind()": "481🚀 Kind() 🔔 This method returns the kind of type, using a value that matches one of the constant values defined by the reflect package, as described in Table 27-5.", "481 string()": "481🚀 String() 🔔 This method returns a string representation of the type name, including the package name.", "comparable()": "481🚀 Comparable() 🔔 This method returns true if values of this type can be compared using the standard comparison operator, as described in the “Comparing Values” section.", "assignableto(type)": "481🚀 AssignableTo(type) 🔔 This method returns true if values of this type can be assigned to variables or fields of the specified reflected type.", "assignableto()": "481🚀 AssignableTo(type) 🔔 This method returns true if values of this type can be assigned to variables or fields of the specified reflected type.", "484 kind()": "484🚀 Kind() 🔔 This method returns the kind of the value's type.", "type()": "484🚀 Type() 🔔 This method returns the Type for the Value.", "isnil()": "484🚀 IsNil() 🔔 This method returns true if the value is nil. This method will panic if the underlying value isn't a function, an interface, a pointer, a slice, or a channel.", "iszero()": "484🚀 IsZero() 🔔 This method returns true if the underlying value is the zero value for its type.", "bool()": "484🚀 Bool() 🔔 This method returns the underlying bool value. The method panics if the underlying value's Kind is not Bool.", "bytes()": "484🚀 Bytes() 🔔 This method returns the underlying []byte value. The method panics if the underlying value is not a byte slice. I demonstrate how to determine the type of a slice in the “Identifying Byte Slices” section.", "484 int()": "484🚀 Int() 🔔 This method returns the underlying value as an int64. The method panics if the underlying value's Kind is not Int, Int8, Int16, Int32, or Int64.", "unit()": "484🚀 Uint() 🔔 This method returns the underlying value as an uint64. The method panics if the underlying value's Kind is not Uint, Uint8, Uint16, Uint32, or Uint64.", "float()": "484🚀 Float() 🔔 This method returns the underlying value as an float64. The method panics if the underlying value's Kind is not Float32, or Float64.", "484 string()": "484🚀 String() 🔔 This method returns the underlying value as a string if the value's Kind is String. For other Kind values, this method returns the string <T Value> where T is the underlying type, such as <int Value>.", "elem()": "484🚀 Elem() 🔔 This method returns the Value to which a pointer refers. This method can also be used with interfaces, as described in Chapter 29. This method panics if the underlying value's Kind is not Ptr.", "isvalid()": "484🚀 IsValid() 🔔 This method returns false if the Value is the zero value, created as Value{} rather than obtained using ValueOf, for example. This method doesn't relate to reflected values that are the zero value of their reflected type. If this method returns false, then all other Value methods will panic.", }, }
View Source
var OriginalAllRegex = DataBase{
Alldatafield: `
189.Regular Expressions
The regular expressions used in this section perform basic matches, but the regexp package
supports an extensive pattern syntax, which is described at https://pkg.go.dev/regexp/syntax@go1.17.1.
The Basic Functions Provided by the regexp Package
Function Description
----------------- --------------------------------------------------------------------------
Match(pattern, b) This function returns a bool that indicates whether a pattern is matched by
the byte slice b.
MatchString(patten, s) This function returns a bool that indicates whether a pattern is matched by
the string s.
Compile(pattern) This function returns a RegExp that can be used to perform repeated pattern
matching with the specified pattern.
MustCompile(pattern) This function provides the same feature as Compile but panics,
if the specified pattern cannot be compiled.
example:
package main
import (
"fmt"
//"strings"
"regexp"
)
func main() {
description := "A boat for one person"
match, err := regexp.MatchString("[A-z]oat", description)
if (err == nil) {
fmt.Println("Match:", match)
} else {
fmt.Println("Error:", err)
}
}
Output:
Match: true
████████████████████████████████████████████████████████████████████████
160.String Processing and Regular Expressions
What are they?
String processing includes a wide range of operations, from trimming
whitespace to splitting a string into components. Regular expressions
are patterns that allow string matching rules to be concisely defined.
Why are they useful?
These operations are useful when an application needs to process
string values. A common example is processing HTTP requests.
How are they used?
These features are contained in the strings and regexp packages,
which are part of the standard library.
Are there any pitfalls or limitations?
There are some quirks in the way that some of these operations are
performed, but they mostly behave as you would expect.
Are there any alternatives?
The use of these packages is optional, and they do not have
to be used. That said, there is little point in creating your own
implementations of these features since the standard library is well-
written and thoroughly tested.
Problem Solution
------------------- -------------------------------------------------------------------
Compare strings Use the Contains, EqualFold, or Has* function in the strings package
Convert string case Use the ToLower, ToUpper, Title, or ToTitle function in the
strings package
Check or change Use the functions provided by the unicode package
character case
Find content in strings Use the functions provided by the strings or regexp package
Split a string Use the Fields or Split* function in the strings and regexp packages
Join strings Use the Join or Repeat function in the strings package
Trim characters from Use the Trim* functions in the strings package
a string
Perform a substitution Use the Replace* or Map function in the strings package,
تعویض انجام دهید use a Replacer, or use the Replace* functions in the regexp package
Efficiently build a Use the Builder type in the strings package
string
████████████████████████████████████████████████████████████████████████
161.Comparing Strings
The strings Functions for Comparing Strings
Function Description
------------- ------------------------
Contains(s, substr) This function returns true if the string s contains substr and false if it does not.
ContainsAny(s, substr) This function returns true if the string s contains any of the characters
contained in the string substr.
ContainsRune(s, rune) This function returns true if the string s contains a specific rune.
EqualFold(s1, s2) This function performs a case-insensitive comparison and returns true of
strings s1 and s2 are the same.
HasPrefix(s, prefix) This function returns true if the string s begins with the string prefix.
HasSuffix(s, suffix) This function returns true if the string ends with the string suffix.
example:
package main
import (
"fmt"
"strings"
)
func main() {
product := "Kayak"
fmt.Println("Contains:", strings.Contains(product, "yak"))
fmt.Println("ContainsAny:", strings.ContainsAny(product, "abc"))
fmt.Println("ContainsRune:", strings.ContainsRune(product, 'K'))
fmt.Println("EqualFold:", strings.EqualFold(product, "KAYAK"))
fmt.Println("HasPrefix:", strings.HasPrefix(product, "Ka"))
fmt.Println("HasSuffix:", strings.HasSuffix(product, "yak"))
}
Output:
Contains: true
ContainsAny: true
ContainsRune: true
HasPrefix: true
HasSuffix: true
EqualFold: true
████████████████████████████████████████████████████████████████████████
162.Using The Byte-Oriented Functions
example:
package main
import (
"fmt"
"strings"
"bytes"
)
func main() {
price := "€100"
fmt.Println("Strings Prefix:", strings.HasPrefix(price, "€"))
fmt.Println("Bytes Prefix:", bytes.HasPrefix([]byte(price),
[]byte { 226, 130 }))
}
Output:
Strings Prefix: true
Bytes Prefix: true
████████████████████████████████████████████████████████████████████████
163.Converting String Case
The Case Functions in the strings Package
Function Description
-------------- ------------------------------------------------------
ToLower(str) This function returns a new string containing the characters in the specified string
mapped to lowercase.
ToUpper(str) This function returns a new string containing the characters in the specified string
mapped to lowercase.
Title(str) This function converts the specific string so that the first character of each word is
uppercase and the remaining characters are lowercase.
ToTitle(str) This function returns a new string containing the characters in the specified string
mapped to title case.
Care must be taken with the Title and ToTitle functions, which don't work the way you might expect.
The Title function returns a string that is suitable for use as a title, but it treats all words the same
Creating a Title:
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "A boat for sailing"
fmt.Println("Original:", description)
fmt.Println("Title:", strings.Title(description))
fmt.Println("Title:", strings.ToTitle(description))
}
Output:
Original: A boat for sailing
Title: A Boat For Sailing
Title: A BOAT FOR SAILING
████████████████████████████████████████████████████████████████████████
164.Title Case
example:
package main
import (
"fmt"
"strings"
)
func main() {
specialChar := "\u01c9"
fmt.Println("Original:", specialChar, []byte(specialChar))
upperChar := strings.ToUpper(specialChar)
fmt.Println("Upper:", upperChar, []byte(upperChar))
titleChar := strings.ToTitle(specialChar)
fmt.Println("Title:", titleChar, []byte(titleChar))
}
Output:
Original: lj [199 137]
Upper: LJ [199 135]
Title: Lj [199 136]
████████████████████████████████████████████████████████████████████████
165.Working with Character Case
The unicode package provides functions that can be used to determine or change the case of individual
characters
Functions in the unicode Package for Character Case
Function Description
------------- -----------------------------------------------------------------
IsLower(rune) This function returns true if the specified rune is lowercase.
ToLower(rune) This function returns the lowercase rune associated with the specified rune.
IsUpper(rune) This function returns true if the specified rune is uppercase.
ToUpper(rune) This function returns the upper rune associated with the specified rune.
IsTitle(rune) This function returns true if the specified rune is title case.
ToTitle(rune) This function returns the title case rune associated with the specified rune.
████████████████████████████████████████████████████████████████████████
166.the Rune Case Functions
example:
package main
import (
"fmt"
"unicode"
)
func main() {
product := "Kayak"
for _, char := range product {
fmt.Println(string(char), "Upper case:", unicode.IsUpper(char))
}
}
Output:
K Upper case: true
a Upper case: false
y Upper case: false
a Upper case: false
k Upper case: false
████████████████████████████████████████████████████████████████████████
167.Inspecting Strings
The strings Functions for Inspecting Strings
Function Description
------------ --------------------------------------------------
Count(s, sub) This function returns an int that reports how many times the specified
substring is found in the string s.
Index(s, sub) These functions return the index of the first or last occurrence of a specified
LastIndex(s, sub) substring string within the string s, or -1 if there is no occurrence.
IndexAny(s, chars) These functions return the first or last occurrence of any character in the
LastIndexAny(s, chars) specified string within the string s, or -1 if there is no occurrence.
IndexByte(s, b) These functions return the index of the first or last occurrence of a specified
LastIndexByte(s, b) byte within the string s, or -1 if there is no occurrence.
IndexFunc(s, func) These functions return the index of the first or last occurrence of the
LastIndexFunc(s, func) character in the string s for which the specified function returns true, as
described in the “Inspecting Strings with Custom Functions” section.
example:
package main
import (
"fmt"
"strings"
//"unicode"
)
func main() {
description := "A boat for one person"
fmt.Println("Count:", strings.Count(description, "o"))
fmt.Println("Index:", strings.Index(description, "o"))
fmt.Println("LastIndex:", strings.LastIndex(description, "o"))
fmt.Println("IndexAny:", strings.IndexAny(description, "abcd"))
fmt.Println("LastIndex:", strings.LastIndex(description, "o"))
fmt.Println("LastIndexAny:", strings.LastIndexAny(description, "abcd"))
}
Output:
Count: 4
Index: 3
LastIndex: 19
IndexAny: 2
LastIndex: 19
LastIndexAny: 4
████████████████████████████████████████████████████████████████████████
168.IndexFunc and LastIndexFunc functions
Inspecting Strings with Custom Functions
The IndexFunc and LastIndexFunc functions use a custom function to inspect strings, using custom functions
Custom functions receive a rune and return a bool result that indicates if the character meets the
desired condition. The IndexFunc function invokes the custom function for each character in the string until
a true result is obtained, at which point the index is returned.
The isLetterB variable is assigned a custom function that receives a rune and returns true if the rune
is a uppercase or lowercase B. The custom function is passed to the strings.IndexFunc function
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "A boat for one person"
isLetterB := func (r rune) bool {
return r == 'B' || r == 'b'
}
fmt.Println("IndexFunc:", strings.IndexFunc(description, isLetterB))
}
Output:
IndexFunc: 2
████████████████████████████████████████████████████████████████████████
169.Splitting Strings
The Functions for Splitting Strings in the strings Package
Function Description
--------------- --------------------------------
Fields(s) This function splits a string on whitespace characters and returns a slice
containing the nonwhitespace sections of the string s.
FieldsFunc(s, func) This function splits the string s on the characters for which a custom function
returns true and returns a slice containing the remaining sections of the string.
Split(s, sub) This function splits the string s on every occurrence of the specified substring,
returning a string slice. If the separator is the empty string, then the slice will
contain strings for each character.
SplitN(s, sub, max) This function is similar to Split, but accepts an additional int argument that
specifies the maximum number of substrings to return. The last substring in the
result slice will contain the unsplit portion of the source string.
SplitAfter(s, sub) This function is similar to Split but includes the substring used in the results.
SplitAfterN(s, sub, max) This function is similar to SplitAfter, but accepts an additional int argument
that specifies the maximum number of substrings to return.
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "A boat for one person"
splits := strings.Split(description, " ")
for _, x := range splits {
fmt.Println("Split >>" + x + "<<")
}
splitsAfter := strings.SplitAfter(description, " ")
for _, x := range splitsAfter {
fmt.Println("SplitAfter >>" + x + "<<")
}
}
Output:
Split >>A<<
Split >>boat<<
Split >>for<<
Split >>one<<
Split >>person<<
SplitAfter >>A <<
SplitAfter >>boat <<
SplitAfter >>for <<
SplitAfter >>one <<
SplitAfter >>person<<
████████████████████████████████████████████████████████████████████████
170.SplitN and SplitAfterN functions
Restricting the Number of Results
The SplitN and SplitAfterN functions accept an int argument that specifies the maximum number of
results that should be included in the results
Restricting the Results
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "A boat for one person"
splits := strings.SplitN(description, " ", 3)
for _, x := range splits {
fmt.Println("Split >>" + x + "<<")
}
}
Output:
Split >>A<<
Split >>boat<<
Split >>for one person<<
████████████████████████████████████████████████████████████████████████
171.strings.SplitN function
Splitting on Whitespace Characters
One limitation of the Split, SplitN, SplitAfter, and SplitAfterN functions is they do not deal with
repeated sequences of characters, which can be a problem when splitting a string on whitespace characters
The words in the source string are double-spaced, but the SplitN function splits only on the first space
character, which produces odd results.
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "This is double spaced"
splits := strings.SplitN(description, " ", 3)
for _, x := range splits {
fmt.Println("Split >>" + x + "<<")
}
}
Output:
Split >>This<<
Split >><<
Split >>is double spaced<<
████████████████████████████████████████████████████████████████████████
172.Fields Function
The Fields function doesn't support a limit on the number of results
but does deal with the double spaces properly.
The Fields function has a better approach, which is to split on any character for
which the IsSpace function in the unicode package returns true.
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "This is double spaced"
splits := strings.Fields(description)
for _, x := range splits {
fmt.Println("Field >>" + x + "<<")
}
}
Output:
Field >>This<<
Field >>is<<
Field >>double<<
Field >>spaced<<
████████████████████████████████████████████████████████████████████████
173.FieldsFunc function
Splitting Using a Custom Function to Split Strings
The FieldsFunc function splits a string by passing each character to a custom function and splitting when
that function returns true
The custom function receives a rune and returns true if that rune should cause the string to split.
The FieldsFunc function is smart enough to deal with repeated characters
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "This is double spaced"
splitter := func(r rune) bool {
return r == ' '
}
splits := strings.FieldsFunc(description, splitter)
for _, x := range splits {
fmt.Println("Field >>" + x + "<<")
}
}
Output:
Field >>This<<
Field >>is<<
Field >>double<<
Field >>spaced<<
████████████████████████████████████████████████████████████████████████
174.Trimming Strings
The Functions for Trimming Strings in the strings Package
Function Description
------------ ---------------------------------------
TrimSpace(s) This function returns the string s without leading or trailing whitespace characters.
Trim(s, set) This function returns a string from which any leading or trailing characters
contained in the string set are removed from the string s.
TrimLeft(s, set) This function returns the string s without any leading character contained
in the string set. This function matches any of the specified characters—use
the TrimPrefix function to remove a complete substring.
TrimRight(s, set) This function returns the string s without any trailing character contained
in the string set. This function matches any of the specified characters—use
the TrimSuffix function to remove a complete substring.
TrimPrefix(s, prefix) This function returns the string s after removing the specified prefix string.
This function removes the complete prefix string—use the TrimLeft
function to remove characters from a set.
TrimSuffix(s, suffix) This function returns the string s after removing the specified suffix string.
This function removes the complete suffix string—use the TrimRight
function to remove characters from a set.
TrimFunc(s, func) This function returns the string s from which any leading or trailing
character for which a custom function returns true are removed.
TrimLeftFunc(s, func) This function returns the string s from which any leading character for
which a custom function returns true are removed.
TrimRightFunc(s, func) This function returns the string s from which any trailing character for
which a custom function returns true are removed.
████████████████████████████████████████████████████████████████████████
175.TrimSpace function
Trimming Whitespace
example:
package main
import (
"fmt"
"strings"
)
func main() {
username := " Alice"
trimmed := strings.TrimSpace(username)
fmt.Println("Trimmed:", ">>" + trimmed + "<<")
}
Output:
Trimmed: >>Alice<<
████████████████████████████████████████████████████████████████████████
176.Trim, TrimLeft, and TrimRight functions
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "A boat for one person"
trimmed := strings.Trim(description, "Anor ")
fmt.Println("Trimmed:>>"+ trimmed+ "<<")
}
Ourput:
Trimmed:>>boat for one pers<<
████████████████████████████████████████████████████████████████████████
177.TrimPrefix and TrimSuffix functions
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "A boat for one person"
prefixTrimmed := strings.TrimPrefix(description, "A boat ")
wrongPrefix := strings.TrimPrefix(description, "A hat ")
fmt.Println("Trimmed:", prefixTrimmed)
fmt.Println("Not trimmed:", wrongPrefix)
}
Output:
Trimmed: for one person
Not trimmed: A boat for one person
████████████████████████████████████████████████████████████████████████
178.TrimFunc, TrimLeftFunc, and TrimRightFunc functions
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "A boat for one person"
trimmer := func(r rune) bool {
return r == 'A' || r == 'n'
}
trimmed := strings.TrimFunc(description, trimmer)
fmt.Println("Trimmed:", trimmed)
}
Output:
Trimmed: boat for one perso
████████████████████████████████████████████████████████████████████████
179.Altering Strings
تغییر رشتهها
The Functions for Altering Strings in the strings Package
Function Description
---------------- -----------------------------------
Replace(s, old, new, n) This function alters the string s by replacing occurrences of the string old with the
string new. The maximum number of occurrences that will be replaced is specified by
the int argument n.
ReplaceAll(s, old, new) This function alters the string s by replacing all occurrences of the string old with
the string new. Unlike the Replace function, there is no limit on the number of
occurrences that will be replaced.
Map(func, s) This function generates a string by invoking the custom function for each character in
the string s and concatenating the results. If the function produces a negative value,
the current character is dropped without a replacement.
████████████████████████████████████████████████████████████████████████
180.Replace and ReplaceAll functions
The Replace function allows a maximum number of changes to be specified,
while the ReplaceAll function will replace all the
occurrences of the substring it finds
example:
package main
import (
"fmt"
"strings"
)
func main() {
text := "It was a boat. A small boat."
replace := strings.Replace(text, "boat", "canoe", 1)
replaceAll := strings.ReplaceAll(text, "boat", "truck")
fmt.Println("Replace:", replace)
fmt.Println("Replace All:", replaceAll)
}
Output:
Replace: It was a canoe. A small boat.
Replace All: It was a truck. A small truck.
████████████████████████████████████████████████████████████████████████
181.Map function
example:
package main
import (
"fmt"
"strings"
)
func main() {
text := "It was a boat. A small boat."
mapper := func(r rune) rune {
if r == 'b' {
return 'c'
}
return r
}
mapped := strings.Map(mapper, text)
fmt.Println("Mapped:", mapped)
}
Output:
Mapped: It was a coat. A small coat.
████████████████████████████████████████████████████████████████████████
182.NewReplacer function
The strings package exports a struct type named Replacer that is used to replace strings
example:
package main
import (
"fmt"
"strings"
)
func main() {
text := "It was a boat. A small boat. 111"
replacer := strings.NewReplacer("boat", "kayak",
"small", "huge",
"111", "222",
)
replaced := replacer.Replace(text)
fmt.Println("Replaced:", replaced)
}
Output:
Replaced: It was a kayak. A huge kayak. 222
████████████████████████████████████████████████████████████████████████
183.The Replacer Methods
Name Description
------------------- --------------------------------------------
Replace(s) This method returns a string for which all the replacements specified with the
constructor have been performed on the string s.
WriteString(writer, s) This method is used to perform the replacements specified with the constructor
and write the results to an io.Writer
████████████████████████████████████████████████████████████████████████
184.Building and Generating Strings
The strings Functions for Generating Strings
Function Description
---------------- ----------------------------------------------------------------------------------------
Join(slice, sep) This function combines the elements in the specified string slice, with the specified
separator string placed between elements.
Repeat(s, count) This function generates a string by repeating the string s for a specified number of times.
████████████████████████████████████████████████████████████████████████
185.Join and Repeat functions
example:
package main
import (
"fmt"
"strings"
)
func main() {
text := "It was a boat. A small boat."
elements := strings.Fields(text)
joined := strings.Join(elements, "--")
fmt.Println("Joined:", joined)
esplited := strings.Split(text, " ")
fmt.Printf("%q\n",esplited)
}
Output:
Joined: It--was--a--boat.--A--small--boat.
["It" "was" "a" "boat." "A" "small" "boat."]
████████████████████████████████████████████████████████████████████████
186.Building Strings
The strings.Builder Methods
Name Description
--------------- --------------------------------------------
WriteString(s) This method appends the string s to the string being built.
WriteRune(r) This method appends the character r to the string being built.
WriteByte(b) This method appends the byte b to the string being built.
String() This method returns the string that has been created by the builder.
Reset() This method resets the string created by the builder.
Len() This method returns the number of bytes used to store the string created by the builder.
Cap() This method returns the number of bytes that have been allocated by the builder.
Grow(size) This method increases the number of bytes used allocated by the builder to store the
string that is being built.
████████████████████████████████████████████████████████████████████████
187.builder.String()
Creating the string using the Builder is more efficient than using the concatenation operator on regular
string values, especially if the Grow method is used to allocate storage in advance.
Care must be taken to use pointers when passing Builder values to and from functions and
methods; otherwise, the efficiency gains will be lost when the Builder is copied.
example:
package main
import (
"fmt"
"strings"
)
func main() {
text := "It was a boat. A small boat."
var builder strings.Builder
for _, sub := range strings.Fields(text) {
if (sub == "small") {
builder.WriteString("very ")
}
builder.WriteString(sub)
builder.WriteRune(' ')
}
fmt.Println("String:", builder.String())
}
`,
}
View Source
var OriginalDataBases = DataBase{
Alldatafield: `
464.Working with Databases
There are drivers for a wide range of databases, and a list can be found at
https://github.com/golang/go/wiki/sqldrivers
Putting Working with Databases in Context
What is it?
The database/sql package provides features for working with SQL databases.
Why is it useful?
Relational databases remain the most effective way of storing large amounts of
structured data and are used in most large projects.
How is it used?
Driver packages provide support for specific databases, while the database/sql
package provides a set of types that allow databases to be used consistently.
Are there any pitfalls or limitations?
These features do not automatically populate struct fields from result rows.
Are there any alternatives?
There are third-party packages that build on these features to simplify or enhance their use.
Preparing for This Chapter:
1-Initializing the Module
go mod init data
2-Add a file named printer.go to the data folder
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
3-Add a file named main.go
package main
func main() {
Printfln("Hello, Data")
}
4-Compiling and Executing the Project
go run .
====================================================================
Output:
Hello, Data
Preparing the Database:
add a file named products.sql to the data folder:
DROP TABLE IF EXISTS Categories;
DROP TABLE IF EXISTS Products;
CREATE TABLE IF NOT EXISTS Categories (
Id INTEGER NOT NULL PRIMARY KEY,
Name TEXT
);
CREATE TABLE IF NOT EXISTS Products (
Id INTEGER NOT NULL PRIMARY KEY,
Name TEXT,
Category INTEGER,
Price decimal(8, 2),
CONSTRAINT CatRef FOREIGN KEY(Category) REFERENCES Categories (Id)
);
INSERT INTO
Categories (Id, Name)
VALUES
(1, "Watersports"),
(2, "Soccer");
INSERT INTO
Products (Id, Name, Category, Price)
VALUES
(1, "Kayak", 1, 279),
(2, "Lifejacket", 1, 48.95),
(3, "Soccer Ball", 2, 19.50),
(4, "Corner Flags", 2, 34.95);
Go to https://www.sqlite.org/download.html , look for the precompiled binaries section for your
operating system, and download the tools package.
Unpack the zip archive and copy the sqlite3 or sqlite3.exe file into the data folder. Run the command
Creating the Database command:
./sqlite3 products.db ".read products.sql"
████████████████████████████████████████████████████████████████████████
465.Creating the Database command:
./sqlite3 products.db ".read products.sql"
████████████████████████████████████████████████████████████████████████
466.Installing a Database Driver
Run the command:
go get modernc.org/sqlite
Most database servers are set up separately so that the database driver opens a connection to a separate
process. SQLite is an embedded database and is included in the driver package, which means no additional
configuration is required.
████████████████████████████████████████████████████████████████████████
467.Opening a Database
The standard library provides the database/sql package for working with databases.
The functions described here:
The database/sql Functions for Opening a Database
Name Description
---------------- ------------------------------
Drivers() This function returns a slice of strings, each of which contains the name of a database driver.
Open(driver,connectionStr) This function opens a database using the specified driver and connection string. The
results are a pointer to a DB struct, which is used to interact with the database and an
error that indicates problems opening the database.
████████████████████████████████████████████████████████████████████████
468.The Contents of the database.go
package main
// The blank identifier is used to import the database driver package, which loads the driver and allows it
// to register as a provider of the SQL API:
import (
"database/sql"
_ "modernc.org/sqlite"
)
func listDrivers() {
for _, driver := range sql.Drivers() {
Printfln("Driver: %v", driver)
}
}
func openDatabase() (db *sql.DB, err error) {
db, err = sql.Open("sqlite", "products.db")
if err == nil {
Printfln("Opened database")
}
return
}
====================================================================
Using the DB Struct in the main.go:
package main
func main() {
listDrivers()
db, err := openDatabase()
if (err == nil) {
db.Close()
} else {
panic(err)
}
}
====================================================================
in Terminal:
go mod tidy
Output in Terminal:
go: finding module for package modernc.org/sqlite
go: downloading modernc.org/sqlite v1.27.0
go: found modernc.org/sqlite in modernc.org/sqlite v1.27.0
go: downloading golang.org/x/sys v0.9.0
go: downloading modernc.org/ccgo/v3 v3.16.13
go: downloading modernc.org/libc v1.29.0
go: downloading modernc.org/mathutil v1.6.0
go: downloading github.com/mattn/go-sqlite3 v1.14.16
go: downloading github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26
go: downloading modernc.org/tcl v1.15.2
go: downloading github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec
go: downloading github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
go: downloading golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78
go: downloading modernc.org/cc/v3 v3.40.0
go: downloading modernc.org/opt v0.1.3
go: downloading github.com/dustin/go-humanize v1.0.1
go: downloading github.com/google/uuid v1.3.0
go: downloading github.com/mattn/go-isatty v0.0.16
go: downloading modernc.org/memory v1.7.2
go: downloading golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
go: downloading golang.org/x/mod v0.3.0
go: downloading lukechampine.com/uint128 v1.2.0
go: downloading modernc.org/strutil v1.1.3
go: downloading modernc.org/token v1.0.1
go: downloading modernc.org/httpfs v1.0.6
go: downloading modernc.org/z v1.7.3
go: downloading modernc.org/ccorpus v1.11.6
====================================================================
Output:
The main method calls the listDrivers function to print out the names of the loaded drivers and then
calls the openDatabase function to open the database. Nothing is done with the database yet, but Close
method is called.
████████████████████████████████████████████████████████████████████████
469.The DB Method for Closing the Database:
Name Description
------- ----------------------
Close() This function closes the database and prevents further operations from being performed.
████████████████████████████████████████████████████████████████████████
470.Executing Statements and Queries
The DB Methods for Executing SQL Statements
Name Description
-------------------- -----------------------------
Query(query,...args) This method executes the specified query, using the optional placeholder arguments.
The results are a Rows struct, which contains the query results, and an error that
indicates problems executing the query.
QueryRow(query, ..args) This method executes the specified query, using the optional placeholder arguments.
The result is a Row struct, which represents the first row from the query results. See
the “Executing Queries for Single Rows” section.
Exec(query,...args) This method executes statements or queries that do not return rows of data. The
method returns a Result, which describes the response from the database, and
an error that signals problems with execution. See the “Executing Other Queries” section.
████████████████████████████████████████████████████████████████████████
471.Using Contexts with Databases
the context package and the Context interface it defines, which is used
to manage requests as they are processed by a server. All the important methods defined in the
database/sql package also have versions that accept a Context argument, which is useful if you
want to take advantage of features like request handling timeouts.
████████████████████████████████████████████████████████████████████████
472.Querying for Multiple Rows
The Query method executes a query that retrieves one or more rows from the database. The Query method
returns a Rows struct, which contains the query results and an error that indicates problems. The row data is
accessed through the methods described in below
The Rows Struct Methods
Name Description
---------------- -----------------------------
Next() This method advances to the next result row. The result is a bool, which is true when
there is data to read and false when the end of the data has been reached, at which point
the Close method is automatically called.
NextResultSet() This method advances to the next result set when there are multiple result sets in the
same database response. The method returns true if there is another set of rows to process.
Scan(...targets) This method assigns the SQL values from the current row to the specified variables. The
values are assigned via pointers and the method returns an error that indicates when the
values cannot be scanned. See the “Understanding the Scan Method” section for details.
Close() This method prevents further enumeration of the results and is used when not all of
the data is required. There is no need to call this method if the Next method is used to
advance until it returns false.
████████████████████████████████████████████████████████████████████████
473.Querying the Database in the main.go
example:
package main
import "database/sql"
func queryDatabase(db *sql.DB) {
rows, err := db.Query("SELECT * from Products")
if err == nil {
for rows.Next() {
var id, category int
var name string
var price float64
rows.Scan(&id, &name, &category, &price)
Printfln("Row: %v %v %v %v", id, name, category, price)
}
} else {
Printfln("Error: %v", err)
}
}
func main() {
//listDrivers()
db, err := openDatabase()
if err == nil {
queryDatabase(db)
db.Close()
} else {
panic(err)
}
}
====================================================================
The queryDatabase function performs a simple SELECT query on the Products table with the Query
method, which produces a Rows result and an error. If the error is nil, a for loop is used to move through
the result rows by calling the Next method, which returns true if there is a row to process and returns false
when the end of the data has been reached.
`,
}
View Source
var OriginalHTMLAndTemplates = DataBase{
Alldatafield: `
366.Putting HTML and Text Templates in Context
Question Answer:
What are they?
These templates allow HTML and text content to be generated dynamically
from Go data values.
Why are they useful?
Templates are useful when large amounts of content are required, such that
defining the content as strings would be unmanageable.
How are they used?
The templates are HTML or text files, which are annotated with instructions
for the template processing engine. When a template is rendered, the
instructions are processed to generate HTML or text content.
Are there any pitfalls or limitations?
The template syntax is counterintuitive and is not checked by the Go
compiler. This means that care must be taken to use the correct syntax,
which can be a frustrating process.
Are there any alternatives?
Templates are optional, and smaller amounts of content can be produced
using strings.
Summary:
Problem Solution
------------------ -----------------------------------
Generate an HTML document Define an HTML template with actions that
incorporate data values into the output. Load and
execute the templates, providing data for the actions.
Enumerate loaded templates Enumerate the results of the Templates method.
Locate a specific template Use the Lookup method.
Produce dynamic content Use a template action.
Format a data value Use the formatting functions.
Suppress whitespace Add hyphens to the template.
Process a slice Use the slice functions.
Conditionally execute template content Use the conditional actions and functions.
Create a nested template Use the define and template actions.
Define a default template Use the block and template actions.
Create functions for use in a template Define template functions.
Disable encoding for function results Return one of the type aliases defined by the html/template package.
Store data values for later use in a template Define template variables.
Generate a text document Use the text/template package.
Preparing for This Chapter:
1- go mod init htmltext
2- printer.go:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
3- product.go:
package main
type Product struct {
Name, Category string
Price float64
}
var Kayak = Product {
Name: "Kayak",
Category: "Watersports",
Price: 279,
}
var Products = []Product {
{ "Kayak", "Watersports", 279 },
{ "Lifejacket", "Watersports", 49.95 },
{ "Soccer Ball", "Soccer", 19.50 },
{ "Corner Flags", "Soccer", 34.95 },
{ "Stadium", "Soccer", 79500 },
{ "Thinking Cap", "Chess", 16 },
{ "Unsteady Chair", "Chess", 75 },
{ "Bling-Bling King", "Chess", 1200 },
}
func (p *Product) AddTax() float64 {
return p.Price * 1.2
}
func (p * Product) ApplyDiscount(amount float64) float64 {
return p.Price - amount
}
4- main.go:
package main
func main() {
for _, p := range Products {
Printfln("Product: %v, Category: %v, Price: $%.2f",
p.Name, p.Category, p.Price)
}
}
████████████████████████████████████████████████████████████████████████
367.Creating HTML Templates
The html/template package provides support for creating templates that are processed using a data
structure to generate dynamic HTML output.
Templates contain static content mixed with expressions that are enclosed in double curly braces,
known as actions.
The template uses the simplest action, which is a period (the . character)
and which prints out the data used to execute the template,
which I explain in the next section.
example:
template.html:
<h1>Template Value: {{ . }}</h1>
A project can contain multiple templates files.
extras.html:
<h1>Extras Template Value: {{ . }}</h1>
The new template uses the same action as the previous example but has different static content to make
it clear which template has been executed in the next section. Once I have described the basic techniques for
using templates, I'll introduce more complex template actions.
████████████████████████████████████████████████████████████████████████
368.Loading and Executing Templates
Using templates is a two-step process.
First, the templates files are loaded and processed to create Template values.
The html/template Functions for Loading Template Files:
Name Description
--------------------- --------------------------------------------
ParseFiles(...files) This function loads one or more files, which are specified by name. The result
is a Template that can be used to generate content and an error that reports
problems loading the templates.
ParseGlob(pattern) This function loads one or more files, which are selected with a pattern. The
result is a Template that can be used to generate content and an error that
reports problems loading the templates.
If you name your template files consistently,
then you can use the ParseGlob function to load them with a simple pattern.
If you want specific files—or the files are not named consistently—then you can specify
individual files using the ParseFiles function.
████████████████████████████████████████████████████████████████████████
369.The Template Methods for Selecting and Executing Templates
Name Description
---------------------------- -------------------------------------------
Templates() This function returns a slice containing pointers to the Template values that
have been loaded.
Lookup(name) This function returns a *Template for the specified loaded template.
Name() This method returns the name of the Template.
Execute(writer, data) This function executes the Template, using the specified data and writes
the output to the specified Writer.
ExecuteTemplate(writer, templateName, data) This function executes the template with the specified name and data and
writes the output to the specified Writer.
████████████████████████████████████████████████████████████████████████
370.Loading and Executing a Template
example:
main.go:
package main
import (
"fmt"
"html/template"
"os"
)
func main() {
t, err := template.ParseFiles("templates/template.html")
if (err == nil) {
t.Execute(os.Stdout, &Kayak)
fmt.Println()
} else {
Printfln("Error: %v", err.Error())
}
}
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
████████████████████████████████████████████████████████████████████████
371.Loading Multiple Templates
There are two approaches to working with multiple templates.
The first is to create a separate Template value
for each of them and execute them separately.
example:
Using Separate Templates:
main.go:
package main
import (
"fmt"
"html/template"
"os"
)
func main() {
t1, err1 := template.ParseFiles("templates/template.html")
t2, err2 := template.ParseFiles("templates/extras.html")
if (err1 == nil && err2 == nil) {
t1.Execute(os.Stdout, &Kayak)
os.Stdout.WriteString("\n")
t2.Execute(os.Stdout, &Kayak)
os.Stdout.WriteString("\n")
} else {
Printfln("Error: %v %v", err1.Error(), err2.Error())
}
}
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
<h1>Extras Template Value: {Kayak Watersports 279}</h1>
████████████████████████████████████████████████████████████████████████
372.Using a Combined Template
When multiple files are loaded with the ParseFiles,
the result is a Template value on which the
ExecuteTemplate method can be called to execute a specified template.
The filename is used as the template name,
which means that the templates in this example are named template.html and extras.html.
You can call the Execute method on the Template returned by the ParseFiles or ParseGlob
function, and the first template that was loaded will be selected
and used to produce the output.
Take care when using the ParseGlob function because
the first template loaded—and therefore the template that will be
executed—may not be the file you expect.
example:
main.go:
package main
import (
"html/template"
"os"
)
func main() {
allTemplates, err1 := template.ParseFiles("templates/template.html",
"templates/extras.html")
if (err1 == nil) {
allTemplates.ExecuteTemplate(os.Stdout, "template.html", &Kayak)
os.Stdout.WriteString("\n")
allTemplates.ExecuteTemplate(os.Stdout, "extras.html", &Kayak)
} else {
Printfln("Error: %v %v", err1.Error())
}
}
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
<h1>Extras Template Value: {Kayak Watersports 279}</h1>
████████████████████████████████████████████████████████████████████████
373.Enumerating Loaded Templates
It can be useful to enumerate the templates that have been loaded,
especially when using the ParseGlob function,
to make sure that all the expected files have been discovered.
example:
main.go:
package main
import (
"html/template"
)
func main() {
allTemplates, err := template.ParseGlob("templates/*.html")
if (err == nil) {
for _, t := range allTemplates.Templates() {
Printfln("Template name: %v", t.Name())
}
} else {
Printfln("Error: %v %v", err.Error())
}
}
=============================
Output:
Template name: extras.html
Template name: template.html
████████████████████████████████████████████████████████████████████████
374.Looking Up a Specific Template
An alternative to specifying a name is to use the Lookup method to select a template,
which is useful when
you want to pass a template as an argument to a function
example:
main.go:
package main
import (
"html/template"
"os"
)
func Exec(t *template.Template) error {
return t.Execute(os.Stdout, &Kayak)
}
func main() {
allTemplates, err := template.ParseGlob("templates/*.html")
if err == nil {
selectedTemplated := allTemplates.Lookup("template.html")
err = Exec(selectedTemplated)
}
if err != nil {
Printfln("Error: %v %v", err.Error())
}
}
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
████████████████████████████████████████████████████████████████████████
375.The Template Actions
Action Description
------------ -----------------------------------------
{{ value }} This action inserts a data value or the result of an expression into the
{{ expr }} template. A period is used to refer to the data value passed to the Execute or
ExecuteTemplate function. See the “Inserting Data Values” section for details.
{{ value.fieldname }} This action inserts the value of a struct field. See the “Inserting Data Values” section for details.
{{ value.method arg }} This action invokes a method and inserts the result into the template
output. Parentheses are not used, and arguments are separated by
spaces. See the “Inserting Data Values” section for details.
{{ func arg }} This action invokes a function and inserts the result into the output.
There are built-in functions for common tasks, such as formatting
data values, and custom functions can be defined, as described in the
“Defining Template Functions” section.
{{ expr | value.method }} Expressions can be chained together using a vertical bar so that the result
{{ expr | func of the first expression is used as the last argument in the second expression.
{{ range value }} This action iterates through the specified slice and adds the content
... between the range and end keyword for each element. The actions within
{{ end }} the nested content are executed, with the current element accessible
through the period. See the “Using Slices in Templates” section for details.
{{ range value }} This action is similar to the range/end combination but defines a section
... of nested content that is used if the slice contains no elements.
{{ else }}
...
{{ end }}
{{ if expr }} This action evaluates an expression and executes the nested template
... content if the result is true, as demonstrated in the “Conditionally
{{ end }} Executing Template Content” section. This action can be used with
optional else and else if clauses.
{{ with expr }} This action evaluates an expression and executes the nested template
... content if the result isn't nil or the empty string. This action can be used
{{ end }} with optional clauses.
{{ define "name" }} This action defines a template with the specified name
...
{{ end }}
{{ template "name" expr }} This action executes the template with the specified name and data and
inserts the result in the output.
{{ block "name" expr }} This action defines a template with the specified name and invokes it
... with the specified data. This is typically used to define a template that
{{ end }} can be replaced by one loaded from another file, as demonstrated in the
“Defining Template Blocks” section.
████████████████████████████████████████████████████████████████████████
376.The Template Expressions for Inserting Values into Templates
Inserting Data Values
Expression Description
------------ -----------------------------------------------
. This expression inserts the value passed to the Execute or ExecuteTemplate method into the
template output.
.Field This expression inserts the value of the specified field into the template output.
.Method This expression calls the specified method without arguments and inserts the result into the
template output.
.Method This expression calls the specified method with the specified argument and inserts the result
arg into the template output.
call This expression invokes a struct function field, using the specified arguments, which are
.Field arg separated by spaces. The result from the function is inserted into the template output.
████████████████████████████████████████████████████████████████████████
377.Inserting Data Values in the template.html
Unlike Go code, methods are not invoked with parentheses, and arguments
are simply specified after the name, separated by spaces.
It is the responsibility of the developer to ensure
that arguments are of a type that can be used by the method or function.
example:
templates/template.html:
<h1>Template Value: {{ . }}</h1>
<h1>Name: {{ .Name }}</h1>
<h1>Category: {{ .Category }}</h1>
<h1>Price: {{ .Price }}</h1>
<h1>Tax: {{ .AddTax }}</h1>
<h1>Discount Price: {{ .ApplyDiscount 10 }}</h1>
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
<h1>Name: Kayak</h1>
<h1>Category: Watersports</h1>
<h1>Price: 279</h1>
<h1>Tax: 334.8</h1>
<h1>Discount Price: 269</h1>
████████████████████████████████████████████████████████████████████████
378.Understanding Contextual Escaping
Values are automatically escaped to make them safe for inclusion in HTML, CSS, and JavaScript code,
with the appropriate escaping rules applied based on context. For example, a string value such as
"It was a <big> boat" used as the text content of an HTML element would be inserted into the
template as "It was a <big> boat" but as "It was a \u003cbig\u003e boat" when used
as a string literal value in JavaScript code. Full details of how values are escaped can be found at
https://golang.org/pkg/html/template.
████████████████████████████████████████████████████████████████████████
379.The Built-in Templates Functions for Formatting Data
Name Description
------- --------------------------------
print This is an alias to the fmt.Sprint function.
printf This is an alias to the fmt.Sprintf function.
println This is an alias to the fmt.Sprintln function.
html This function encodes a value for safe inclusion in an HTML document.
js This function encodes a value for safe inclusion in a JavaScript document.
urlquery This function encodes a value for use in a URL query string.
example:
template.html:
<h1>Template Value: {{ . }}</h1>
<h1>Name: {{ .Name }}</h1>
<h1>Category: {{ .Category }}</h1>
<h1>Price: {{ printf "$%.3f" .Price }}</h1>
<h1>Tax: {{ printf "$%.2f" .AddTax }}</h1>
<h1>Discount Price: {{ .ApplyDiscount 10 }}</h1>
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
<h1>Name: Kayak</h1>
<h1>Category: Watersports</h1>
<h1>Price: $279.000</h1>
<h1>Tax: $334.80</h1>
<h1>Discount Price: 269</h1>
████████████████████████████████████████████████████████████████████████
380.Chaining Expressions
Chaining expressions creates a pipeline for values,
which allows the output from one method or function
to be used as the input for another.
example:
template.html:
<h1>Template Value: {{ . }}</h1>
<h1>Name: {{ .Name }}</h1>
<h1>Category: {{ .Category }}</h1>
<h1>Price: {{ printf "$%.2f" .Price }}</h1>
<h1>Tax: {{ printf "$%.2f" .AddTax }}</h1>
<h1>Discount Price: {{ .ApplyDiscount 10 | printf "$%.2f" }}</h1>
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
<h1>Name: Kayak</h1>
<h1>Category: Watersports</h1>
<h1>Price: $279.00</h1>
<h1>Tax: $334.80</h1>
<h1>Discount Price: $269.00</h1>
████████████████████████████████████████████████████████████████████████
381.Using Parentheses in html
Chaining can be used only for the last argument provided to a function.
An alternative approach—and
one that can be used to set other function arguments is to use parentheses
example:
template.html:
<h1>Template Value: {{ . }}</h1>
<h1>Name: {{ .Name }}</h1>
<h1>Category: {{ .Category }}</h1>
<h1>Price: {{ printf "$%.2f" .Price }}</h1>
<h1>Tax: {{ printf "$%.2f" .AddTax }}</h1>
<h1>Discount Price: {{ printf "$%.2f" (.ApplyDiscount 10) }}</h1>
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
<h1>Name: Kayak</h1>
<h1>Category: Watersports</h1>
<h1>Price: $279.00</h1>
<h1>Tax: $334.80</h1>
<h1>Discount Price: $269.00</h1>
████████████████████████████████████████████████████████████████████████
382.Trimming Whitespace
HTML isn't sensitive to the whitespace between elements,
but whitespace can still cause problems for text content and attribute values,
especially when you want to structure the content
of a template to make it easy to read.
example:
template.html
<h1>
Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{ printf "$%.2f" .Price }}
</h1>
=============================
Output:
<h1>
Name: Kayak, Category: Watersports, Price,
$279.00
</h1>
████████████████████████████████████████████████████████████████████████
383.The minus sign must
The effect is to remove all of the whitespace to before or after the action.
The minus sign can be used to trim whitespace,
applied immediately after or before the braces
that open or close an action.
example:
template.html:
<h1>
Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{ printf "$%.2f" .Price }}
</h1>
<h1>
Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- printf "$%.2f" .Price -}}
</h1>
=============================
Output:
<h1>
Name: Kayak, Category: Watersports, Price,
$279.00
</h1>
<h1>
Name: Kayak, Category: Watersports, Price,$279.00</h1>
████████████████████████████████████████████████████████████████████████
384.Trimming Additional Whitespace
The whitespace around the final action has been removed,
but there is still a newline character after
the opening h1 tag because the whitespace trimming applies only to actions.
example:
template.html:
<h1>
{{- "" -}} Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- printf "$%.2f" .Price -}}
</h1>
=============================
Output:
<h1>Name: Kayak, Category: Watersports, Price,$279.00</h1>
████████████████████████████████████████████████████████████████████████
385.Slices in Templates
Template actions can be used to generate content for slices
example:
Processing a Slice in the template.html
{{ range . -}}
<h1>Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- printf "$%.2f" .Price }}</h1>
{{ end }}
main.go:
package main
import (
"html/template"
"os"
)
func Exec(t *template.Template) error {
return t.Execute(os.Stdout, Products)
}
func main() {
allTemplates, err := template.ParseGlob("templates/*.html")
if err == nil {
selectedTemplated := allTemplates.Lookup("template.html")
err = Exec(selectedTemplated)
}
if err != nil {
Printfln("Error: %v %v", err.Error())
}
}
=============================
Output:
<h1>Name: Kayak, Category: Watersports, Price,$279.00</h1>
<h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1>
<h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1>
<h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1>
<h1>Name: Stadium, Category: Soccer, Price,$79500.00</h1>
<h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1>
<h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1>
<h1>Name: Bling-Bling King, Category: Chess, Price,$1200.00</h1>
████████████████████████████████████████████████████████████████████████
386.The Built-in Template Functions for Slices
Go text templates support the built-in functions
Name Description
------ --------------
slice This function creates a new slice. Its arguments are the original slice, the start index, and the end index.
index This function returns the element at the specified index.
len This function returns the length of the specified slice.
example:
Built-in Functions in the template.html:
<h1>There are {{ len . }} products in the source data.</h1>
<h1>First product: {{ index . 0 }}</h1>
{{ range slice . 3 6 -}}
<h1>Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- printf "$%.2f" .Price }}</h1>
{{ end }}
=============================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>First product: {Kayak Watersports 279}</h1>
<h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1>
<h1>Name: Stadium, Category: Soccer, Price,$79500.00</h1>
<h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1>
████████████████████████████████████████████████████████████████████████
387.Conditionally Executing Template Content
Actions can be used to conditionally insert content into the output based on the evaluation of their
expressions.
The Template Conditional Functions:
Function Description
------------ -----------------------------------------------
eq arg1 arg2 This function returns true if arg1 == arg2.
ne arg1 arg2 This function returns true if arg1 != arg2.
lt arg1 arg2 This function returns true if arg1 < arg2.
le arg1 arg2 This function returns true if arg1 <= arg2.
gt arg1 arg2 This function returns true if arg1 > arg2.
ge arg1 arg2 This function returns true if arg1 >= arg2.
and arg1 arg2 This function returns true if both arg1 and arg2 are true.
not arg1 This function returns true if arg1 is false, and false if it is true.
example:
a Conditional Action in the template.html:
<h1>There are {{ len . }} products in the source data.</h1>
<h1>First product: {{ index . 0 }}</h1>
{{ range . -}}
{{ if lt .Price 100.00 -}}
<h1>Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- printf "$%.2f" .Price }}</h1>
{{ end -}}
{{ end }}
=============================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>First product: {Kayak Watersports 279}</h1>
<h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1>
<h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1>
<h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1>
<h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1>
<h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1>
Despite the use of the minus sign to trim whitespace,
the output is oddly formatted because of the
way I chose to structure the template.
████████████████████████████████████████████████████████████████████████
388.Using the Optional Conditional Actions
The if action can be used with optional else and else if keywords
example:
template.html:
<h1>There are {{ len . }} products in the source data.</h1>
<h1>First product: {{ index . 0 }}</h1>
{{ range . -}}
{{ if lt .Price 100.00 -}}
<h1>Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- printf "$%.2f" .Price }}</h1>
{{ else if gt .Price 1500.00 -}}
<h1>Expensive Product {{ .Name }} ({{ printf "$%.2f" .Price}})</h1>
{{ else -}}
<h1>Midrange Product: {{ .Name }} ({{ printf "$%.2f" .Price}})</h1>
{{ end -}}
{{ end }}
main.go:
package main
import (
"html/template"
"os"
)
func Exec(t *template.Template) error {
return t.Execute(os.Stdout, Products)
}
func main() {
allTemplates, err := template.ParseGlob("templates/*.html")
if err == nil {
selectedTemplated := allTemplates.Lookup("template.html")
err = Exec(selectedTemplated)
}
if err != nil {
Printfln("Error: %v %v", err.Error())
}
}
=============================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>First product: {Kayak Watersports 279}</h1>
<h1>Midrange Product: Kayak ($279.00)</h1>
<h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1>
<h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1>
<h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1>
<h1>Expensive Product Stadium ($79500.00)</h1>
<h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1>
<h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1>
<h1>Midrange Product: Bling-Bling King ($1200.00)</h1>
████████████████████████████████████████████████████████████████████████
389.Creating Named Nested Templates
The define action is used to create a nested template that can be executed by name,
which allows content to
be defined once and used repeatedly with the template action
example:
template.html:
{{ define "currency" }}{{ printf "$%.2f" . }}{{ end }}
{{ define "basicProduct" -}}
Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- template "currency" .Price }}
{{- end }}
{{ define "expensiveProduct" -}}
Expensive Product {{ .Name }} ({{ template "currency" .Price }})
{{- end }}
<h1>There are {{ len . }} products in the source data.</h1>
<h1>First product: {{ index . 0 }}</h1>
{{ range . -}}
{{ if lt .Price 100.00 -}}
<h1>{{ template "basicProduct" . }}</h1>
{{ else if gt .Price 1500.00 -}}
<h1>{{ template "expensiveProduct" . }}</h1>
{{ else -}}
<h1>Midrange Product: {{ .Name }} ({{ printf "$%.2f" .Price}})</h1>
{{ end -}}
{{ end }}
=============================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>First product: {Kayak Watersports 279}</h1>
<h1>Midrange Product: Kayak ($279.00)</h1>
<h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1>
<h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1>
<h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1>
<h1>Expensive Product Stadium ($79500.00)</h1>
<h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1>
<h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1>
<h1>Midrange Product: Bling-Bling King ($1200.00)</h1>
The define keyword is followed by the template name in quotes,
and the template is terminated by the end keyword.
The template keyword is used to execute a named template,
specifying the template name and a data value:
...
{{- template "currency" .Price }}
...
████████████████████████████████████████████████████████████████████████
390.Selecting a Named Template in the main.go
Using the define and end keywords for the main template content excludes the whitespace used to
separate the other named templates.
Adding a Named Template in the template.html:
{{ define "currency" }}{{ printf "$%.2f" . }}{{ end }}
{{ define "basicProduct" -}}
Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- template "currency" .Price }}
{{- end }}
{{ define "expensiveProduct" -}}
Expensive Product {{ .Name }} ({{ template "currency" .Price }})
{{- end }}
{{ define "mainTemplate" -}}
<h1>There are {{ len . }} products in the source data.</h1>
<h1>First product: {{ index . 0 }}</h1>
{{ range . -}}
{{ if lt .Price 100.00 -}}
<h1>{{ template "basicProduct" . }}</h1>
{{ else if gt .Price 1500.00 -}}
<h1>{{ template "expensiveProduct" . }}</h1>
{{ else -}}
<h1>Midrange Product: {{ .Name }} ({{ printf "$%.2f" .Price}})</h1>
{{ end -}}
{{ end }}
{{- end}}
=============================
main.go:
package main
import (
"html/template"
"os"
)
func Exec(t *template.Template) error {
return t.Execute(os.Stdout, Products)
}
func main() {
allTemplates, err := template.ParseGlob("templates/*.html")
if (err == nil) {
selectedTemplated := allTemplates.Lookup("mainTemplate")
err = Exec(selectedTemplated)
}
if (err != nil) {
Printfln("Error: %v %v", err.Error())
}
}
=============================
Any of the named templates can be executed directly, but I have selected the mainTemplate
Output:
<h1>There are 8 products in the source data.</h1>
<h1>First product: {Kayak Watersports 279}</h1>
<h1>Midrange Product: Kayak ($279.00)</h1>
<h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1>
<h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1>
<h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1>
<h1>Expensive Product Stadium ($79500.00)</h1>
<h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1>
<h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1>
<h1>Midrange Product: Bling-Bling King ($1200.00)</h1>
████████████████████████████████████████████████████████████████████████
391.Defining Template Blocks
Template blocks are used to define a template with default content that can be overridden in another
template file, which requires multiple templates to be loaded and executed together.
This is often used to common content, such as a layout.
The templates must be loaded so that the file that contains the block action is loaded before the file that
contains the define action that redefines the template.
When the templates are loaded, the template defined
in the list.html file redefines the template named body so that the content in the list.html file replaces
the content in the template.html file.
example:
template.html File in the templates Folder
{{ define "mainTemplate" -}}
<h1>This is the layout header</h1>
{{ block "body" . }}
<h2>There are {{ len . }} products in the source data.</h2>
{{ end }}
<h1>This is the layout footer</h1>
{{ end }}
Output:
<h1>This is the layout header</h1>
<h2>There are 8 products in the source data.</h2>
<h1>This is the layout footer</h1>
When used alone, the output from the template file includes the content in the block.
But this content can be redefined by another template file.
example:
list.html:
{{ define "body" }}
{{ range . }}
<h2>Product: {{ .Name }} ({{ printf "$%.2f" .Price}})</h2>
{{ end -}}
{{ end }}
========================
main.go:
package main
import (
"html/template"
"os"
)
func Exec(t *template.Template) error {
return t.Execute(os.Stdout, Products)
}
func main() {
allTemplates, err := template.ParseFiles("templates/template.html","templates/list.html")
if err == nil {
selectedTemplated := allTemplates.Lookup("mainTemplate")
err = Exec(selectedTemplated)
}
if err != nil {
Printfln("Error: %v %v", err.Error())
}
}
=============================
Output:
<h1>This is the layout header</h1>
<h2>Product: Kayak ($279.00)</h2>
<h2>Product: Lifejacket ($49.95)</h2>
<h2>Product: Soccer Ball ($19.50)</h2>
<h2>Product: Corner Flags ($34.95)</h2>
<h2>Product: Stadium ($79500.00)</h2>
<h2>Product: Thinking Cap ($16.00)</h2>
<h2>Product: Unsteady Chair ($75.00)</h2>
<h2>Product: Bling-Bling King ($1200.00)</h2>
<h1>This is the layout footer</h1>
████████████████████████████████████████████████████████████████████████
392.Defining Template Functions
example:
main.go File in the htmltext Folder:
package main
import (
"html/template"
"os"
)
func GetCategories(products []Product) (categories []string) {
catMap := map[string]string {}
for _, p := range products {
if (catMap[p.Category] == "") {
catMap[p.Category] = p.Category
categories = append(categories, p.Category)
}
}
return
}
func Exec(t *template.Template) error {
return t.Execute(os.Stdout, Products)
}
func main() {
allTemplates := template.New("allTemplates")
allTemplates.Funcs(map[string]interface{} {
"getCats": GetCategories,
})
allTemplates, err := allTemplates.ParseGlob("templates/*.html")
if (err == nil) {
selectedTemplated := allTemplates.Lookup("mainTemplate")
err = Exec(selectedTemplated)
}
if (err != nil) {
Printfln("Error: %v %v", err.Error())
}
}
=============================
Output:
<h1>This is the layout header</h1>
<h2>There are 8 products in the source data.</h2>
<h1>This is the layout footer</h1>
████████████████████████████████████████████████████████████████████████
393.Using a Custom Function in the template.html
example:
template.html:
{{ define "mainTemplate" -}}
<h1>There are {{ len . }} products in the source data.</h1>
{{ range getCats . -}}
<h1>Category: {{ . }}</h1>
{{ end }}
{{- end }}
=============================================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>Category: Watersports</h1>
<h1>Category: Soccer</h1>
<h1>Category: Chess</h1>
████████████████████████████████████████████████████████████████████████
394.Creating an HTML Fragment in the main.go
example:
main.go:
...
func GetCategories(products []Product) (categories []string) {
catMap := map[string]string {}
for _, p := range products {
if (catMap[p.Category] == "") {
catMap[p.Category] = p.Category
categories = append(categories, "<b>p.Category</b>")
}
}
return
}
...
===============================================================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>Category: <b>p.Category</b></h1>
<h1>Category: <b>p.Category</b></h1>
<h1>Category: <b>p.Category</b></h1>
████████████████████████████████████████████████████████████████████████
395.The Types Aliases Used to Denote Content Types
Name Description
-------- -----------
CSS This type denotes CSS content.
HTML This type denotes a fragment of HTML.
HTMLAttr This type denotes a value that will be used as the value for an HTML attribute.
JS This type denotes a fragment of JavaScript code.
JSStr This type denotes a value that is intended to appear between quotes in a JavaScript expression.
Srcset This type denotes a value that can be used in the srcset attribute of an img element.
URL This type denotes a URL.
████████████████████████████████████████████████████████████████████████
396.Returning HTML Content in the main.go
example:
main.go:
...
func GetCategories(products []Product) (categories []template.HTML) {
catMap := map[string]string {}
for _, p := range products {
if (catMap[p.Category] == "") {
catMap[p.Category] = p.Category
categories = append(categories, "<b>p.Category</b>")
}
}
return
}
...
=======================================================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>Category: <b>p.Category</b></h1>
<h1>Category: <b>p.Category</b></h1>
<h1>Category: <b>p.Category</b></h1>
████████████████████████████████████████████████████████████████████████
397.Providing Access to Standard Library Functions
Adding a Function Mapping in the main.go
example:
main.go:
package main
import (
"html/template"
"os"
"strings"
)
func GetCategories(products []Product) (categories []string) {
catMap := map[string]string{}
for _, p := range products {
if catMap[p.Category] == "" {
catMap[p.Category] = p.Category
categories = append(categories, p.Category)
}
}
return
}
func Exec(t *template.Template) error {
return t.Execute(os.Stdout, Products)
}
func main() {
allTemplates := template.New("allTemplates")
allTemplates.Funcs(map[string]interface{}{
"getCats": GetCategories,
"lower": strings.ToLower,
})
allTemplates, err := allTemplates.ParseGlob("templates/*.html")
if err == nil {
selectedTemplated := allTemplates.Lookup("mainTemplate")
err = Exec(selectedTemplated)
}
if err != nil {
Printfln("Error: %v %v", err.Error())
}
}
=============================================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>Category: Watersports</h1>
<h1>Category: Soccer</h1>
<h1>Category: Chess</h1>
████████████████████████████████████████████████████████████████████████
398.Using a Template Function in the template.html
example:
template.html:
{{ define "mainTemplate" -}}
<h1>There are {{ len . }} products in the source data.</h1>
{{ range getCats . -}}
<h1>Category: {{ lower . }}</h1>
{{ end }}
{{- end }}
=============================================================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>Category: watersports</h1>
<h1>Category: soccer</h1>
<h1>Category: chess</h1>
████████████████████████████████████████████████████████████████████████
399.Defining Template Variables
Defining and Using a Template Variable in the template.html
example:
template.html:
{{ define "mainTemplate" -}}
{{ $length := len . }}
<h1>There are {{ $length }} products in the source data.</h1>
{{ range getCats . -}}
<h1>Category: {{ lower . }}</h1>
{{ end }}
{{- end }}
=============================================================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>Category: watersports</h1>
<h1>Category: soccer</h1>
<h1>Category: chess</h1>
████████████████████████████████████████████████████████████████████████
400.Defining and Using a Template Variable in the template.html
example:
template.html:
{{ define "mainTemplate" -}}
<h1>There are {{ len . }} products in the source data.</h1>
{{- range getCats . -}}
{{ if ne ($char := slice (lower .) 0 1) "s" }}
<h1>{{$char}}: {{.}}</h1>
{{- end }}
{{- end }}
{{- end }}
==============================================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>w: Watersports</h1>
<h1>c: Chess</h1>
████████████████████████████████████████████████████████████████████████
401.Using Template Variables in Range Actions
Enumerating a Map in the template.html
example:
main.go:
...
func Exec(t *template.Template) error {
productMap := map[string]Product {}
for _, p := range Products {
productMap[p.Name] = p
}
return t.Execute(os.Stdout, &productMap)
}
...
template.html:
{{ define "mainTemplate" -}}
{{ range $key, $value := . -}}
<h1>{{ $key }}: {{ printf "$%.2f" $value.Price }}</h1>
{{ end }}
{{- end }}
Output:
<h1>Bling-Bling King: $1200.00</h1>
<h1>Corner Flags: $34.95</h1>
<h1>Kayak: $279.00</h1>
<h1>Lifejacket: $49.95</h1>
<h1>Soccer Ball: $19.50</h1>
<h1>Stadium: $79500.00</h1>
<h1>Thinking Cap: $16.00</h1>
<h1>Unsteady Chair: $75.00</h1>
████████████████████████████████████████████████████████████████████████
402.Creating Text Templates
Loading and Executing a Text Template in the main.go
The Contents of the template.txt
example:
templates/template.txt:
{{ define "mainTemplate" -}}
{{ range $key, $value := . -}}
{{ $key }}: {{ printf "$%.2f" $value.Price }}
{{ end }}
{{- end }}
---------------------------------------
main.go:
package main
import (
"text/template"
"os"
"strings"
)
func GetCategories(products []Product) (categories []string) {
catMap := map[string]string {}
for _, p := range products {
if (catMap[p.Category] == "") {
catMap[p.Category] = p.Category
categories = append(categories, p.Category)
}
}
return
}
func Exec(t *template.Template) error {
productMap := map[string]Product {}
for _, p := range Products {
productMap[p.Name] = p
}
return t.Execute(os.Stdout, &productMap)
}
func main() {
allTemplates := template.New("allTemplates")
allTemplates.Funcs(map[string]interface{} {
"getCats": GetCategories,
"lower": strings.ToLower,
})
allTemplates, err := allTemplates.ParseGlob("templates/*.txt")
if (err == nil) {
selectedTemplated := allTemplates.Lookup("mainTemplate")
err = Exec(selectedTemplated)
}
if (err != nil) {
Printfln("Error: %v %v", err.Error())
}
}
Output:
Bling-Bling King: $1200.00
Corner Flags: $34.95
Kayak: $279.00
Lifejacket: $49.95
Soccer Ball: $19.50
Stadium: $79500.00
Thinking Cap: $16.00
Unsteady Chair: $75.00
████████████████████████████████████████████████████████████████████████`,
}
View Source
var OriginalHTTPClients = DataBase{
Alldatafield: `
436.Creating HTTP Clients
Putting HTTP Clients in Context
What are they?
HTTP requests are used to retrieve data from HTTP servers
Why are they useful?
HTTP is one of the most widely used protocols and is commonly used to provide
access to content that can be presented to the user as well as data that is consumed
programmatically.
How is it used?
The features of the net/http package are used to create and send requests and
process responses.
Are there any pitfalls or limitations?
These features are well-designed and easy to use, although some features require a
specific sequence to use.
Are there any alternatives?
The standard library includes support for other network protocols and also for
opening and using lower-level network connections.
See the
https://pkg.go.dev/net@go1.17.1
https://pkg.go.dev/net
for details of the net package and its subpackages, such as net/smtp,
for example, which implements the SMTP protocol.
Chapter Summary:
Problem Solution
-------- ------------
Send HTTP requests Use the convenience methods for specific HTTP methods
Configure HTTP requests Use the fields and methods defined by the Client struct
Create a preconfigured request Use the NewRequest convenience functions
Use cookies in a request Use a cookie jar
Configure how redirections are processed Use the CheckRedirect field to register a function that is
invoked to deal with a redirection
Send multipart forms Use the mime/multipart package
Preparing for This Chapter
1- go mod init httpclient
2- printer.go
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
3- httpclient folder -> file named product.go
package main
type Product struct {
Name, Category string
Price float64
}
var Products = []Product {
{ "Kayak", "Watersports", 279 },
{ "Lifejacket", "Watersports", 49.95 },
{ "Soccer Ball", "Soccer", 19.50 },
{ "Corner Flags", "Soccer", 34.95 },
{ "Stadium", "Soccer", 79500 },
{ "Thinking Cap", "Chess", 16 },
{ "Unsteady Chair", "Chess", 75 },
{ "Bling-Bling King", "Chess", 1200 },
}
4- The Contents of the index.html File in the httpclient Folder
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
</head>
<body>
<h1>Hello, World</div>
</body>
</html>
5- The Contents of the server.go File in the httpclient Folder
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
func init() {
http.HandleFunc("/html",
func(writer http.ResponseWriter, request *http.Request) {
http.ServeFile(writer, request, "./index.html")
})
http.HandleFunc("/json",
func(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Content-Type", "application/json")
json.NewEncoder(writer).Encode(Products)
})
http.HandleFunc("/echo",
func(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(writer, "Method: %v\n", request.Method)
for header, vals := range request.Header {
fmt.Fprintf(writer, "Header: %v: %v\n", header, vals)
}
fmt.Fprintln(writer, "----")
data, err := io.ReadAll(request.Body)
if err == nil {
if len(data) == 0 {
fmt.Fprintln(writer, "No body")
} else {
writer.Write(data)
}
} else {
fmt.Fprintf(os.Stdout, "Error reading body: %v\n", err.Error())
}
})
}
6- The Contents of the main.go File in the httpclient Folder
package main
import (
"net/http"
)
func main() {
Printfln("Starting HTTP Server")
http.ListenAndServe(":5000", nil)
}
=======================================================================================
Output:
The code in httpclient folder will be compiled and executed.
Use a web browser to request http://localhost:5000/html and http://localhost:5000/json,
To see the echo result, request http://localhost:5000/echo
████████████████████████████████████████████████████████████████████████
437.Sending Simple HTTP Requests
The net/http package provides a set of convenience functions that make basic HTTP requests.
The functions are named after the HTTP method of the request they created.
The Convenience Methods for HTTP Requests
Name Description
----------------- -----------------------------
Get(url) This function sends a GET request to the specified HTTP or HTTPS URL. The
results are a Response and an error that reports problems with the request.
Head(url) This function sends a HEAD request to the specified HTTP or HTTPS URL.
A HEAD request returns the headers that would be returned for a GET request.
The results are a Response and an error that reports problems with the request.
Post(url, contentType, reader) This function sends a POST request to the specified HTTP or HTTPS URL, with
the specified Content-Type header value. The content for the form is provided
by the specified Reader. The results are a Response and an error that reports
problems with the request.
PostForm(url, data) This function sends a POST request to the specified HTTP or HTTPS URL, with
the Content-Type header set to application/x-www-form-urlencoded. The
content for the form is provided by a map[string][]string. The results are a
Response and an error that reports problems with the request.
████████████████████████████████████████████████████████████████████████
438.Sending a GET Request in the main.go
main.go:
package main
import (
"net/http"
"os"
"time"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
response, err := http.Get("http://localhost:5000/html")
if (err == nil) {
response.Write(os.Stdout)
} else {
Printfln("Error: %v", err.Error())
}
}
===================================================================================================
The argument to the Get function is a string that contains the URL to request.
The results are a Response value and an error that reports any problems sending the request.
Output:
HTTP/1.1 200 OK
Content-Length: 171
Accept-Ranges: bytes
Content-Type: text/html; charset=utf-8
Date: Thu, 12 Oct 2023 16:17:10 GMT
Last-Modified: Wed, 11 Oct 2023 12:44:50 GMT
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
</head>
<body>
<h1>Hello, World</div>
</body>
</html>
========================================================================================================
example-2:
main.go:
package main
import (
"net/http"
"os"
// "time"
)
func main() {
// go http.ListenAndServe(":5000", nil)
// time.Sleep(time.Second)
response, err := http.Get("https://www.google.com")
if (err == nil) {
response.Write(os.Stdout)
} else {
Printfln("Error: %v", err.Error())
}
}
Output:
HTTP/2.0 403 Forbidden
Content-Length: 1579
Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
Content-Type: text/html; charset=UTF-8
Date: Thu, 12 Oct 2023 16:15:23 GMT
Referrer-Policy: no-referrer
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
<title>Error 403 (Forbidden)!!1</title>
<style>
*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
</style>
<a href=//www.google.com/><span id=logo aria-label=Google></span></a>
<p><b>403.</b> <ins>That's an error.</ins>
<p>Your client does not have permission to get URL <code>/</code> from this server. <ins>That's all we know.</ins>
████████████████████████████████████████████████████████████████████████
439.The Fields and Methods Defined by the Response Struct
Name Description
---------- -------------------------------------
StatusCode This field returns the response status code, expressed as an int.
Status This field returns a string containing the status description.
Proto This field returns a string containing the response HTTP protocol.
Header This field returns a map[string][]string that contains the response headers.
Body This field returns a ReadCloser, which is a Reader that defines a Close method and
which provides access to the response body.
Trailer This field returns a map[string][]string that contains the response trailers.
ContentLength This field returns the value of the Content-Length header, parsed into an int64 value.
TransferEncoding This field returns the set of Transfer-Encoding header values.
Close This bool field returns true if the response contains a Connection header set to close,
which indicates that the HTTP connection should be closed.
Uncompressed This field returns true if the server sent a compressed response that was
decompressed by the net/http package.
Request This field returns the Request that was used to obtain the response. The Request struct
is described in Chapter 24.
TLS This field provides details of the HTTPS connection.
Cookies() This method returns a []*Cookie, which contains the Set-Cookie headers in the
response. The Cookie struct is described in Chapter 24.
Location() This method returns the URL from the response Location header and an error that
indicates when the response does not contain this header.
Write(writer) This method writes a summary of the response to the specified Writer.
████████████████████████████████████████████████████████████████████████
440.Reading the Response Body in the main.go
main.go:
package main
import (
"net/http"
"os"
"time"
"io"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
response, err := http.Get("http://localhost:5000/html")
if (err == nil && response.StatusCode == http.StatusOK) {
data, err := io.ReadAll(response.Body)
if (err == nil) {
defer response.Body.Close()
os.Stdout.Write(data)
}
} else {
Printfln("Error: %v, Status Code: %v", err.Error(), response.StatusCode)
}
}
====================================================================
Output: in Terminal
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
</head>
<body>
<h1>Hello, World</div>
</body>
</html>
████████████████████████████████████████████████████████████████████████
441.Reading and Parsing Data in the main.go
main.go:
package main
import (
"net/http"
//"os"
"time"
//"io"
"encoding/json"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
response, err := http.Get("http://localhost:5000/json")
if (err == nil && response.StatusCode == http.StatusOK) {
defer response.Body.Close()
data := []Product {}
err = json.NewDecoder(response.Body).Decode(&data)
if (err == nil) {
for _, p := range data {
Printfln("Name: %v, Price: $%.2f", p.Name, p.Price)
}
} else {
Printfln("Decode error: %v", err.Error())
}
} else {
Printfln("Error: %v, Status Code: %v", err.Error(), response.StatusCode)
}
}
====================================================================
Output: in Terminal
Name: Kayak,Price: $279.00
Name: Lifejacket,Price: $49.95
Name: Soccer Ball,Price: $19.50
Name: Corner Flags,Price: $34.95
Name: Stadium,Price: $79500.00
Name: Thinking Cap,Price: $16.00
Name: Unsteady Chair,Price: $75.00
Name: Bling-Bling King,Price: $1200.00
████████████████████████████████████████████████████████████████████████
442.Sending POST Requests
The Post and PostForm functions are used to send POST requests.
The PostForm function encodes a map of values as form data.
Sending a Form in the main.go
main.go:
package main
import (
"net/http"
"os"
"time"
"io"
//"encoding/json"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
formData := map[string][]string {
"name": { "Kayak "},
"category": { "Watersports"},
"price": { "279"},
}
response, err := http.PostForm("http://localhost:5000/echo", formData)
if (err == nil && response.StatusCode == http.StatusOK) {
io.Copy(os.Stdout, response.Body)
defer response.Body.Close()
} else {
Printfln("Error: %v, Status Code: %v", err.Error(), response.StatusCode)
}
}
====================================================================
Output:
Method: POST
Header: Accept-Encoding: [gzip]
Header: User-Agent: [Go-http-client/1.1]
Header: Content-Length: [42]
Header: Content-Type: [application/x-www-form-urlencoded]
----
category=Watersports&name=Kayak+&price=279
████████████████████████████████████████████████████████████████████████
443.Posting a Form Using a Reader
The Post function sends a POST request
to the server and creates the request body by reading content from a Reader.
Posting from a Reader in the main.go:
package main
import (
"net/http"
"os"
"time"
"io"
"encoding/json"
"strings"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
var builder strings.Builder
err := json.NewEncoder(&builder).Encode(Products[0])
if (err == nil) {
response, err := http.Post("http://localhost:5000/echo","application/json",strings.NewReader(builder.String()))
if (err == nil && response.StatusCode == http.StatusOK) {
io.Copy(os.Stdout, response.Body)
defer response.Body.Close()
} else {
Printfln("Error: %v", err.Error())
}
} else {
Printfln("Error: %v", err.Error())
}
}
====================================================================
Output:
Method: POST
Header: User-Agent: [Go-http-client/1.1]
Header: Content-Length: [54]
Header: Content-Type: [application/json]
Header: Accept-Encoding: [gzip]
----
{"Name":"Kayak","Category":"Watersports","Price":279}
████████████████████████████████████████████████████████████████████████
444.Understanting The Content-Length Header
This header is set automatically but is included in requests only when it is
possible to determine how much data will be included in the body in advance.
This is done by inspecting the Reader to determine the dynamic type.
When the data is stored in memory using the strings.
Reader, bytes.Reader, or bytes.Buffer type,
the built-in len function is used to determine the amount of data,
and the result is used to set the Content-Length header.
For all other types, the Content-Type head is not set,
and chunked encoding is used instead, which means that
the body is written in blocks of data whose size
is declared as part of the request body.
This approach allows requests to be sent
without needing to read all the data from the Reader just to work
out how many bytes there are.
Chunked encoding is described at
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding
████████████████████████████████████████████████████████████████████████
445.Configuring HTTP Client Requests
The Client struct is used when control is required over an HTTP request
and defines the fields and methods described:
The Client Fields and Methods
Name Description
----------------- -------------------------
Transport This field is used to select the transport that will be used to send the HTTP
request. The net/http package provides a default transport.
CheckRedirect This field is used to specify a custom policy for dealing with repeated
redirections, as described in the “Managing Redirections” section.
Jar This field returns a CookieJar, which is used to manage cookies, as
described in the “Working with Cookies” section.
Timeout This field is used to set a timeout for the request, specified as a time.Duration
Do(request) This method sends the specified Request, returning a Response and an
error that indicates problems sending the request.
CloseIdleConnections() This method closes any idle HTTP requests that are currently open and unused.
Get(url) This method is called by the Get function described
Head(url) This method is called by the Head function described
Post(url, contentType,reader) This method is called by the Post function described
PostForm(url, data) This method is called by the PostForm function described
████████████████████████████████████████████████████████████████████████
446.Useful Request Fields and Methods
Name Description
------ ------------------------------
Method This string field specifies the HTTP method that will be used for the request. The net/
http package defines constants for HTTP methods, such as MethodGet and MethodPost.
URL This URL field specifies the URL to which the request will be sent. The URL struct is
Header This field is used to specify the headers for the request. The headers are specified in a
map[string][]string, and the field will be nil when a Request value is created using the literal struct syntax.
ContentLength This field is used to set the Content-Length header using an int64 value.
TransferEncoding This field is used to set the Transfer-Encoding header using a slice of strings.
Body This ReadCloser field specifies the source for the request body. If you have a Reader
that doesn't define a Close method, then the io.NopCloser function can be used to
create a ReadCloser whose Close method does nothing.
████████████████████████████████████████████████████████████████████████
447.The Function for Parsing URL Values
Name Description
--------- ---------------------
Parse(string) This method parses a string into a URL. The results are the URL value and an error that
indicates problems parsing the string.
████████████████████████████████████████████████████████████████████████
448.Sending a Request in the main.go
main.go:
package main
import (
"net/http"
"os"
"time"
"io"
"encoding/json"
"strings"
"net/url"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
var builder strings.Builder
err := json.NewEncoder(&builder).Encode(Products[0])
if (err == nil) {
reqURL, err := url.Parse("http://localhost:5000/echo")
if (err == nil) {
req := http.Request {
Method: http.MethodPost,
URL: reqURL,
Header: map[string][]string {
"Content-Type": { "application.json" },
},
Body: io.NopCloser(strings.NewReader(builder.String())),
}
response, err := http.DefaultClient.Do(&req)
if (err == nil && response.StatusCode == http.StatusOK) {
io.Copy(os.Stdout, response.Body)
defer response.Body.Close()
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Parse Error: %v", err.Error())
}
} else {
Printfln("Encoder Error: %v", err.Error())
}
}
====================================================================
Output:
Method: POST
Header: User-Agent: [Go-http-client/1.1]
Header: Content-Type: [application.json]
Header: Accept-Encoding: [gzip]
----
{"Name":"Kayak","Category":"Watersports","Price":279}
████████████████████████████████████████████████████████████████████████
449.Using the Convenience Functions to Create a Request
The net/http Convenience Functions for Creating Requests
Name Description
------------------------- ----------------------------
NewRequest(method, url,reader) This function creates a new Reader, configured with the specified method,
URL, and body. The function also returns an error that indicates problems
creating the value, including parsing the URL, which is expressed as a string.
NewRequestWithContext(context, method, url, reader) This function creates a new Reader that will be sent in the specified context.
████████████████████████████████████████████████████████████████████████
450.Using the Convenience Function in the main.go
example:
main.go:
package main
import (
"encoding/json"
"io"
"net/http"
"os"
"strings"
"time"
//"net/url"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
var builder strings.Builder
err := json.NewEncoder(&builder).Encode(Products[0])
if err == nil {
req, err := http.NewRequest(http.MethodPost, "http://localhost:5000/echo",
io.NopCloser(strings.NewReader(builder.String())))
if err == nil {
req.Header["Content-Type"] = []string{"application/json"}
response, err := http.DefaultClient.Do(req)
if err == nil && response.StatusCode == http.StatusOK {
io.Copy(os.Stdout, response.Body)
defer response.Body.Close()
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Request Init Error: %v", err.Error())
}
} else {
Printfln("Encoder Error: %v", err.Error())
}
}
====================================================================
Output:
Method: POST
Header: User-Agent: [Go-http-client/1.1]
Header: Content-Type: [application/json]
Header: Accept-Encoding: [gzip]
----
{"Name":"Kayak","Category":"Watersports","Price":279}
████████████████████████████████████████████████████████████████████████
451.Working with Cookies
The Client keeps track of the cookies it receives from the server and automatically includes them in
subsequent requests.
example:
The Contents of the server_cookie.go:
package main
import (
"fmt"
"net/http"
"strconv"
)
func init() {
http.HandleFunc("/cookie",
func(writer http.ResponseWriter, request *http.Request) {
counterVal := 1
counterCookie, err := request.Cookie("counter")
if err == nil {
counterVal, _ = strconv.Atoi(counterCookie.Value)
counterVal++
}
http.SetCookie(writer, &http.Cookie{
Name: "counter", Value: strconv.Itoa(counterVal),
})
if len(request.Cookies()) > 0 {
for _, c := range request.Cookies() {
fmt.Fprintf(writer, "Cookie Name: %v, Value: %v\n",
c.Name, c.Value)
}
} else {
fmt.Fprintln(writer, "Request contains no cookies")
}
})
}
-----------------------------------
Changing URL in the main.go:
package main
import (
"io"
"net/http"
"os"
"time"
// "encoding/json"
// "strings"
//"net/url"
"net/http/cookiejar"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
jar, err := cookiejar.New(nil)
if err == nil {
http.DefaultClient.Jar = jar
}
for i := 0; i < 3; i++ {
req, err := http.NewRequest(http.MethodGet,
"http://localhost:5000/cookie", nil)
if err == nil {
response, err := http.DefaultClient.Do(req)
if err == nil && response.StatusCode == http.StatusOK {
io.Copy(os.Stdout, response.Body)
defer response.Body.Close()
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Request Init Error: %v", err.Error())
}
}
}
====================================================================
Output: in Terminal
Request contains no cookies
Cookie Name: counter, Value: 1
Cookie Name: counter, Value: 2
████████████████████████████████████████████████████████████████████████
452.The Methods Defined by the CookieJar Interface
Name Description
------------------------ ---------------------------------
SetCookies(url, cookies) This method stores a *Cookie slice for the specified URL.
Cookes(url) This method returns a *Cookie slice containing the cookies that
should be included in a request for the specified URL.
The net/http/cookiejar package contains an implementation of the CookieJar interface that stores
cookies in memory. Cookie jars are created with a constructor function
████████████████████████████████████████████████████████████████████████
453.The Cookie Jar Constructor Function in the net/http/cookiejar Package
Name Description
------------ -------------------------
New(options) This function creates a new CookieJar, configured with an Options struct, described
next. The function also returns an error that reports problems creating the jar.
The New function accepts a net/http/cookiejar/Options struct, which is used to configure the
cookie jar. There is only one Options field, PublicSuffixList, which is used to specify an implementation
of the interface with the same name, which provides support for preventing cookies from being set too
widely, which can cause privacy violations. The standard library doesn't contain an implementation of the
PublicSuffixList interface, but there is an implementation available at
https://pkg.go.dev/golang.org/x/net/publicsuffix
████████████████████████████████████████████████████████████████████████
454.Creating Separate Clients and Cookie Jars
A consequence of using the DefaultClient is that all requests share the same cookies,
which can be useful, especially since the cookie jar will ensure that each request
only includes the cookies that are required for each URL.
████████████████████████████████████████████████████████████████████████
455.Creating Separate Clients in the main.go File
example:
main.go:
package main
import (
"net/http"
"os"
"time"
"io"
//"encoding/json"
//"strings"
//"net/url"
"net/http/cookiejar"
"fmt"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
clients := make([]http.Client, 3)
for index, client := range clients {
jar, err := cookiejar.New(nil)
if (err == nil) {
client.Jar = jar
}
for i := 0; i < 3; i++ {
req, err := http.NewRequest(http.MethodGet,
"http://localhost:5000/cookie", nil)
if (err == nil) {
response, err := client.Do(req)
if (err == nil && response.StatusCode == http.StatusOK) {
fmt.Fprintf(os.Stdout, "Client %v: ", index)
io.Copy(os.Stdout, response.Body)
defer response.Body.Close()
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Request Init Error: %v", err.Error())
}
}
}
}
====================================================================
Output:
Client 0: Request contains no cookies
Client 0: Cookie Name: counter, Value: 1
Client 0: Cookie Name: counter, Value: 2
Client 1: Request contains no cookies
Client 1: Cookie Name: counter, Value: 1
Client 1: Cookie Name: counter, Value: 2
Client 2: Request contains no cookies
Client 2: Cookie Name: counter, Value: 1
Client 2: Cookie Name: counter, Value: 2
████████████████████████████████████████████████████████████████████████
456.Sharing a CookieJar in the main.go
example:
main.go:
package main
import (
"io"
"net/http"
"os"
"time"
//"encoding/json"
//"strings"
//"net/url"
"fmt"
"net/http/cookiejar"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
jar, err := cookiejar.New(nil)
clients := make([]http.Client, 3)
for index, client := range clients {
//jar, err := cookiejar.New(nil)
if err == nil {
client.Jar = jar
}
for i := 0; i < 3; i++ {
req, err := http.NewRequest(http.MethodGet,
"http://localhost:5000/cookie", nil)
if err == nil {
response, err := client.Do(req)
if err == nil && response.StatusCode == http.StatusOK {
fmt.Fprintf(os.Stdout, "Client %v: ", index)
io.Copy(os.Stdout, response.Body)
defer response.Body.Close()
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Request Init Error: %v", err.Error())
}
}
}
}
====================================================================
Output:
Client 0: Request contains no cookies
Client 0: Cookie Name: counter, Value: 1
Client 0: Cookie Name: counter, Value: 2
Client 1: Cookie Name: counter, Value: 3
Client 1: Cookie Name: counter, Value: 4
Client 1: Cookie Name: counter, Value: 5
Client 2: Cookie Name: counter, Value: 6
Client 2: Cookie Name: counter, Value: 7
Client 2: Cookie Name: counter, Value: 8
████████████████████████████████████████████████████████████████████████
457.Managing Redirections
By default, a Client will stop following redirections after ten requests,
but this can be changed by specifying a custom policy.
example:
The Contents of the server_redirects.go File in the httpclient Folder:
package main
import "net/http"
func init() {
http.HandleFunc("/redirect1",
func(writer http.ResponseWriter, request *http.Request) {
http.Redirect(writer, request, "/redirect2",
http.StatusTemporaryRedirect)
})
http.HandleFunc("/redirect2",
func(writer http.ResponseWriter, request *http.Request) {
http.Redirect(writer, request, "/redirect1",
http.StatusTemporaryRedirect)
})
}
====================================================================
Sending a Request in the main.go File in the httpclient Folder:
package main
import (
"net/http"
"os"
"io"
"time"
//"encoding/json"
//"strings"
//"net/url"
//"net/http/cookiejar"
//"fmt"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
req, err := http.NewRequest(http.MethodGet,
"http://localhost:5000/redirect1", nil)
if (err == nil) {
var response *http.Response
response, err = http.DefaultClient.Do(req)
if (err == nil) {
io.Copy(os.Stdout, response.Body)
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Error: %v", err.Error())
}
}
====================================================================
Output:
Request Error: Get "/redirect1": stopped after 10 redirects
████████████████████████████████████████████████████████████████████████
458.Defining a Custom Redirection Policy in the main.go
example:
main.go:
package main
import (
"net/http"
"os"
"io"
"time"
//"encoding/json"
//"strings"
"net/url"
//"net/http/cookiejar"
//"fmt"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
http.DefaultClient.CheckRedirect = func(req *http.Request,
previous []*http.Request) error {
if len(previous) == 3 {
url, _ := url.Parse("http://localhost:5000/html")
req.URL = url
}
return nil
}
req, err := http.NewRequest(http.MethodGet,
"http://localhost:5000/redirect1", nil)
if (err == nil) {
var response *http.Response
response, err = http.DefaultClient.Do(req)
if (err == nil) {
io.Copy(os.Stdout, response.Body)
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Error: %v", err.Error())
}
}
====================================================================
Output:
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
</head>
<body>
<h1>Hello, World</div>
</body>
</html>
████████████████████████████████████████████████████████████████████████
459.Creating Multipart Forms:
The mime/multipart package can be used to create a request body encoded as multipart/form-data, which
allows a form to safely contain binary data, such as the contents of a file.
████████████████████████████████████████████████████████████████████████
460.The Contents of the server_forms.go File in the httpclient Folder
example:
server_forms.go:
package main
import (
"net/http"
"fmt"
"io"
)
func init() {
http.HandleFunc("/form",
func (writer http.ResponseWriter, request *http.Request) {
err := request.ParseMultipartForm(10000000)
if (err == nil) {
for name, vals := range request.MultipartForm.Value {
fmt.Fprintf(writer, "Field %v: %v\n", name, vals)
}
for name, files := range request.MultipartForm.File {
for _, file := range files {
fmt.Fprintf(writer, "File %v: %v\n", name, file.Filename)
if f, err := file.Open(); err == nil {
defer f.Close()
io.Copy(writer, f)
}
}
}
} else {
fmt.Fprintf(writer, "Cannot parse form %v", err.Error())
}
})
}
====================================================================
Output:
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
</head>
<body>
<h1>Hello, World</div>
</body>
</html>
████████████████████████████████████████████████████████████████████████
461.The multipart.Writer Constructor Function
Name Description
--------------- ----------------------------
NewWriter(writer) This function creates a new multipart.Writer that writes form data to the specified io.Writer.
████████████████████████████████████████████████████████████████████████
462.The multipart.Writer Methods
Name Description
------------------------ ---------------------------------------
CreateFormField(fieldname) This method creates a new form field with the specified name. The results
are an io.Writer that is used to write the field data and an error that
reports problems creating the field.
CreateFormFile(fieldname, filename) This method creates a new file field with the specified field name and file
name. The results are an io.Writer that is used to write the field data and
an error that reports problems creating the field.
FormDataContentType() This method returns a string that is used to set the Content-Type request
header and includes the string that denotes the boundaries between the parts of the form.
Close() This function finalizes the form and writes the terminating boundary that
denotes the end of the form data.
████████████████████████████████████████████████████████████████████████
463.Creating and Sending a Multipart Form in the main.go
example:
main.go:
package main
import (
"io"
"net/http"
"os"
"time"
//"encoding/json"
//"strings"
//"net/url"
//"net/http/cookiejar"
//"fmt"
"bytes"
"mime/multipart"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
var buffer bytes.Buffer
formWriter := multipart.NewWriter(&buffer)
fieldWriter, err := formWriter.CreateFormField("name")
if err == nil {
io.WriteString(fieldWriter, "Alice")
}
fieldWriter, err = formWriter.CreateFormField("city")
if err == nil {
io.WriteString(fieldWriter, "New York")
}
fileWriter, err := formWriter.CreateFormFile("codeFile", "printer.go")
if err == nil {
fileData, err := os.ReadFile("./printer.go")
if err == nil {
fileWriter.Write(fileData)
}
}
formWriter.Close()
req, err := http.NewRequest(http.MethodPost,
"http://localhost:5000/form", &buffer)
req.Header["Content-Type"] = []string{formWriter.FormDataContentType()}
if err == nil {
var response *http.Response
response, err = http.DefaultClient.Do(req)
if err == nil {
io.Copy(os.Stdout, response.Body)
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Error: %v", err.Error())
}
}
====================================================================
Output:
Field name: [Alice]
Field city: [New York]
File codeFile: printer.go
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
Caution Don't use the defer keyword on the call to the Close method; otherwise, the final boundary string
won't be added to the form until after the request will be sent, producing a form that not all servers will process.
It is important to call the Close method before sending the request.
████████████████████████████████████████████████████████████████████████
`,
}
View Source
var OriginalHTTPServers = DataBase{
Alldatafield: `
403.Creating HTTP Servers
What are they?
The features described in this chapter make it easy for Go applications to create HTTP servers.
Why are they useful?
HTTP is one of the most widely used protocols and is useful for both user-facing
applications and web services.
How is it used?
The features of the net/http package are used to create a server and handle requests.
Are there any pitfalls or limitations?
These features are well-designed and easy to use.
Are there any alternatives?
The standard library includes support for other network protocols and also for
opening and using lower-level network connections.
See https://pkg.go.dev/net@go1.17.1 for details of the net package and its subpackages,
such as net/smtp, for example, which implements the SMTP protocol.
Problem Solution
-------- -------------
Create an HTTP or HTTPS server Use the ListenAndServe or ListenAndServeTLS functions
Inspect an HTTP request Use the features of the Request struct
Produce a response Use the ResponseWriter interface or the
convenience functions
Handle requests to specific URLs Use the integrated router
Serve static content Use the FileServer and StripPrefix function
Use a template to produce a response Write the content to the ResponseWriter
or produce a JSON response
Handle form data Use the Request methods
Set or read cookies Use the Cookie, Cookies, and SetCookie methods
Preparing for This Chapter:
1- go mod init httpserver
2- printer.go:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
3- product.go:
package main
type Product struct {
Name, Category string
Price float64
var Products = []Product{
{"Kayak", "Watersports", 279},
{"Lifejacket", "Watersports", 49.95},
{"Soccer Ball", "Soccer", 19.50},
{"Corner Flags", "Soccer", 34.95},
{"Stadium", "Soccer", 79500},
{"Thinking Cap", "Chess", 16},
{"Unsteady Chair", "Chess", 75},
{"Bling-Bling King", "Chess", 1200},
}
4- main.go:
package main
func main() {
for _, p := range Products {
Printfln("Product: %v, Category: %v, Price: $%.2f",
p.Name, p.Category, p.Price)
}
}
=================================
Output:
Product: Kayak, Category: Watersports, Price: $279.00
Product: Lifejacket, Category: Watersports, Price: $49.95
Product: Soccer Ball, Category: Soccer, Price: $19.50
Product: Corner Flags, Category: Soccer, Price: $34.95
Product: Stadium, Category: Soccer, Price: $79500.00
Product: Thinking Cap, Category: Chess, Price: $16.00
Product: Unsteady Chair, Category: Chess, Price: $75.00
Product: Bling-Bling King, Category: Chess, Price: $1200.00
████████████████████████████████████████████████████████████████████████
404.Creating a Simple HTTP Server
example:
main.go:
package main
import (
"net/http"
"io"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter,
request *http.Request) {
io.WriteString(writer, sh.message)
}
func main() {
err := http.ListenAndServe(":5000", StringHandler{ message: "Hello, World"})
if (err != nil) {
Printfln("Error: %v", err.Error())
}
}
=================================
Output: go run .
in Web Browser, search localhost:5000/
Hello, World
████████████████████████████████████████████████████████████████████████
405.Creating the HTTP Listener and Handler
The net/http Convenience Functions
The ListenAndServe function starts listening for HTTP requests on a specified network address.
The ListenAndServeTLS function does the same for HTTP requests.
The addresses accepted by the functions can be used to restrict the HTTP server so that it
only accepts requests on a specific interface or to listen for requests on any interface.
Name Description
--------- ----------------------------------------
ListenAndServe(addr, handler) This function starts listening for HTTP requests on a specified
address and passes requests onto the specified handler.
ListenAndServeTLS(addr, cert, key, handler) This function starts listening for HTTPS requests. The arguments are the address
████████████████████████████████████████████████████████████████████████
406.The Method Defined by the Handler Interface
Name Description
ServeHTTP(writer, request) This method is invoked to process a HTTP request. The request is described by a
Request value, and the response is written using a ResponseWriter, both of which
are received as parameters.
████████████████████████████████████████████████████████████████████████
407.Inspecting the Request
The Basic Fields Defined by the Request Struct
Name Description
------- ------------------------
Method This field provides the HTTP method (GET, POST, etc.) as a string. The net/http package defines
constants for the HTTP methods, such as MethodGet and MethodPost.
URL This field returns the requested URL, expressed as a URL value.
Proto This field returns a string that indicates the version of HTTP used for the request.
Host This field returns a string containing the requested hos.
Header This field returns a Header value, which is an alias to map[string][]string and contains the
request headers. The map keys are the names of the headers, and the values are string slices
containing the header values.
Trailer This field returns a map[string]string that contains any additional headers that are included in
the request after the body.
Body This filed returns a ReadCloser, which is an interface that combines the Read method of the
Reader interface with the Close method of the Closer interface
████████████████████████████████████████████████████████████████████████
408.Writing Request Fields in the main.go
example:
main.go:
package main
import (
"io"
"net/http"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
Printfln("Method: %v", request.Method)
Printfln("URL: %v", request.URL)
Printfln("HTTP Version: %v", request.Proto)
Printfln("Host: %v", request.Host)
for name, val := range request.Header {
Printfln("Header: %v, Value: %v", name, val)
}
Printfln("---")
io.WriteString(writer, sh.message)
}
func main() {
err := http.ListenAndServe(":5000", StringHandler{message: "Hello, World"})
if err != nil {
Printfln("Error: %v", err.Error())
}
}
===================================================================================
Output: Compile and execute the project and request http://localhost:5000
Method: GET
URL: /
HTTP Version: HTTP/1.1
Host: localhost:5000
Header: Accept, Value: [text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8]
Header: Accept-Language, Value: [en-US,en;q=0.5]
Header: Accept-Encoding, Value: [gzip, deflate, br]
Header: Sec-Fetch-Mode, Value: [navigate]
Header: Sec-Fetch-Site, Value: [none]
Header: User-Agent, Value: [Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0]
Header: Connection, Value: [keep-alive]
Header: Upgrade-Insecure-Requests, Value: [1]
Header: Sec-Fetch-Dest, Value: [document]
Header: Sec-Fetch-User, Value: [?1]
---
Method: GET
URL: /favicon.ico
HTTP Version: HTTP/1.1
Host: localhost:5000
Header: Accept-Encoding, Value: [gzip, deflate, br]
Header: Connection, Value: [keep-alive]
Header: Sec-Fetch-Dest, Value: [image]
Header: Sec-Fetch-Mode, Value: [no-cors]
Header: Sec-Fetch-Site, Value: [same-origin]
Header: User-Agent, Value: [Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0]
Header: Accept, Value: [image/avif,image/webp,*/*]
Header: Accept-Language, Value: [en-US,en;q=0.5]
Header: Referer, Value: [http://localhost:5000/]
---
████████████████████████████████████████████████████████████████████████
409.Save All Logs in a txt file:
Server.go:
package server
import (
"io"
"log"
"net/http"
"os"
)
type StringHandler struct {
Message string
}
func MyServer() {
logFile, err := os.OpenFile("Server/LogFile/logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer logFile.Close()
log.SetOutput(logFile)
err = http.ListenAndServe(":5000", StringHandler{Message: "Hello, World"})
if err != nil {
log.Printf("Error: %v", err.Error())
}
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
log.Printf("Method: %v", request.Method)
log.Printf("URL: %v", request.URL)
log.Printf("HTTP Version: %v", request.Proto)
log.Printf("Host: %v", request.Host)
for name, val := range request.Header {
log.Printf("Header: %v, Value: %v", name, val)
}
io.WriteString(writer, sh.Message)
log.Printf("============================================◉🧭🧭🧭🧭🧭🧭🧭◉==========================================")
}
Output: Create a TXT file in Server/LogFile/logs.txt and save appendation in to it.
████████████████████████████████████████████████████████████████████████
410.Filtering Requests and Generating Responses
Useful Fields and Methods Defined by the URL Struct
The ResponseWriter interface defines the methods that are available when creating a response.
Name Description
------- -----------------------------
Scheme This field returns the scheme component of the URL.
Host This field returns the host component of the URL, which may include the port.
RawQuery This field returns the query string from the URL. Use the Query method to process the query
string into a map.
Path This field returns the path component of the URL.
Fragment This field returns the fragment component of the URL, without the # character.
Hostname() This method returns the hostname component of the URL as a string.
Port()T his method returns the port component of the URL as a string.
Query() This method returns a map[string][]string (a map with string keys and string slice
values), containing the query string fields.
User() This method returns the user information associated with the request.
String() This method returns a string representation of the URL.
████████████████████████████████████████████████████████████████████████
411.The ResponseWriter Methods
Name Description
Header() This method returns a Header, which is an alias to map[string][]string, that can be
used to set the response headers.
WriteHeader(code) This method sets the status code for the response, specified as an int. The net/http
package defines constants for most status codes.
Write(data) This method writes data to the response body and implements the Writer interface.
████████████████████████████████████████████████████████████████████████
412.Producing Difference Responses in the main.go
example:
main.go:
package main
import (
"net/http"
"io"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter,
request *http.Request) {
if (request.URL.Path == "/favicon.ico") {
Printfln("Request for icon detected - returning 404")
writer.WriteHeader(http.StatusNotFound)
return
}
Printfln("Request for %v", request.URL.Path)
io.WriteString(writer, sh.message)
}
func main() {
err := http.ListenAndServe(":5000", StringHandler{ message: "Hello, World"})
if (err != nil) {
Printfln("Error: %v", err.Error())
}
}
Output: in Terminal
Request for /
Request for icon detected - returning 404
████████████████████████████████████████████████████████████████████████
413.Get Logs and Handle request if URL not Exist:
example:
main.go:
package main
import (
"io"
"log"
"net/http"
"os"
)
type StringHandler struct {
Message string
}
func main() {
logFile, err := os.OpenFile("LogFile/logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer logFile.Close()
log.SetOutput(logFile)
err = http.ListenAndServe(":5000", StringHandler{Message: "Hello, World"})
if err != nil {
log.Printf("Error: %v", err.Error())
}
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
if (request.URL.Path != "/") {
Printfln("Request for icon detected - returning 404")
writer.WriteHeader(http.StatusNotFound)
return
}
Printfln("Request for %v", request.URL.Path)
io.WriteString(writer, sh.Message)
log.Printf("Method: %v", request.Method)
log.Printf("URL: %v", request.URL)
log.Printf("HTTP Version: %v", request.Proto)
log.Printf("Host: %v", request.Host)
for name, val := range request.Header {
log.Printf("Header: %v, Value: %v", name, val)
}
io.WriteString(writer, sh.Message)
log.Printf("============================================◉🧭🧭🧭🧭🧭🧭🧭◉==========================================")
}
Output: so we have to logs, one of that is logs about user request,
another that is about path URL in Terminal
████████████████████████████████████████████████████████████████████████
414.Using the Response Convenience Functions
Name Description
----------------------- ----------------------------
Error(writer, message, code) This function sets the header to the specified code, sets the Content-Type header
to text/plain, and writes the error message to the response. The X-Content-
Type-Options header is also set to stop browsers from interpreting the response as
anything other than text.
NotFound(writer, request) This function calls Error and specifies a 404 error code.
Redirect(writer, request, url, code) This function sends a redirection response to the specified URL and with the
specified status code.
ServeFile(writer, request, fileName) This function sends a response containing the contents of the specified file. The
Content-Type header is set based on the file name but can be overridden by
explicitly setting the header before calling the function. See the “Creating a Static
HTTP Server” section for an example that serves files.
████████████████████████████████████████████████████████████████████████
415.Convenience Functions in the main.go
example:
main.go:
package main
import (
"io"
"log"
"net/http"
"os"
)
type StringHandler struct {
Message string
}
func main() {
logFile, err := os.OpenFile("LogFile/logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer logFile.Close()
log.SetOutput(logFile)
err = http.ListenAndServe(":5000", StringHandler{Message: "Hello, World"})
if err != nil {
log.Printf("Error: %v", err.Error())
}
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
Printfln("Request for %v", request.URL.Path)
switch request.URL.Path {
case "/favicon.ico":
http.NotFound(writer, request)
case "/message":
io.WriteString(writer, sh.Message)
default:
http.Redirect(writer, request, "/message", http.StatusTemporaryRedirect)
}
log.Printf("Method: %v", request.Method)
log.Printf("URL: %v", request.URL)
log.Printf("HTTP Version: %v", request.Proto)
log.Printf("Host: %v", request.Host)
for name, val := range request.Header {
log.Printf("Header: %v, Value: %v", name, val)
}
io.WriteString(writer, sh.Message)
log.Printf("============================================◉🧭🧭🧭🧭🧭🧭🧭◉==========================================")
}
Output: Will write in Terminal and File for feture analyzing.
████████████████████████████████████████████████████████████████████████
416.Using the Convenience Functions in the main.go
example:
main.go:
package main
import (
"io"
"net/http"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
Printfln("Request for %v", request.URL.Path)
switch request.URL.Path {
case "/favicon.ico":
http.NotFound(writer, request)
case "/message":
io.WriteString(writer, sh.message)
default:
http.Redirect(writer, request, "/message", http.StatusTemporaryRedirect)
}
}
func main() {
err := http.ListenAndServe(":5000", StringHandler{message: "Hello, World"})
if err != nil {
Printfln("Error: %v", err.Error())
}
}
Output:
every wrong links redirect to specific link.
uses a switch statement to decide how to respond to a request.
████████████████████████████████████████████████████████████████████████
417.Using the Convenience Routing Handler
The process of inspecting the URL and selecting a response can produce complex code that is difficult to
read and maintain.
To simplify the process, the net/http package provides a Handler implementation that
allows matching the URL to be separated from producing a request.
example:
main.go:
package main
import (
"io"
"net/http"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
Printfln("Request for %v", request.URL.Path)
io.WriteString(writer, sh.message)
}
func main() {
http.Handle("/message", StringHandler{"Hello, World"})
http.Handle("/favicon.ico", http.NotFoundHandler())
http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect))
err := http.ListenAndServe(":5000", nil)
if err != nil {
Printfln("Error: %v", err.Error())
}
}
Output:
don't show wrong search.
████████████████████████████████████████████████████████████████████████
418.The net/http Functions for Creating Routing Rules
Name Description
----------------- ---------------------------------
Handle(pattern, handler) This function creates a rule that invokes the specified ServeHTTP method of the
specified Hander for requests that match the pattern.
HandleFunc(pattern, handlerFunc) This function creates a rule that invokes the specified function for requests that match
the pattern. The function is invoked with ResponseWriter and Request arguments.
████████████████████████████████████████████████████████████████████████
419.he net/http Functions for Creating Request Handlers
Name Description
FileServer(root) This function creates a Handler that produces responses using the ServeFile
function. See the “Creating a Static HTTP Server” section for an example that
serves files.
NotFoundHandler() This function creates a Handler that produces responses using the NotFound function.
RedirectHandler(url, code) This function creates a Handler that produces responses using the Redirect function.
StripPrefix(prefix, handler) This function creates a Handler that removes the specified prefix from the
request URL and passes on the request to the specified Handler. See the
“Creating a Static HTTP Server” section for details.
TimeoutHandler(handler, duration, message) This function passes on the request to the specified Handler but generates an error
response if the response hasn't been produced within the specified duration.
████████████████████████████████████████████████████████████████████████
420.Supporting HTTPS Requests
The net/http package provides integrated support for HTTPS.
To prepare for HTTPS, you will need to add
two files to the httpserver folder:
a certificate file and a private key file.
████████████████████████████████████████████████████████████████████████
421.Getting Certificates for HTTPS
A good way to get started with HTTPS is with a self-signed certificate,
which can be used for development and testing.
If you don't already have a self-signed certificate,
then you can create one online using sites such as
https://getacert.com
or
https://www.selfsignedcertificate.com
both of which will let you create a self-signed certificate easily
and without charge.
Two files are required to use HTTPS, regardless of whether your certificate
is self-signed or not. The first is the certificate file,
which usually has a cer or cert file extension.
The second is the private key file, which usually has a key file extension.
after ready to deploy:
https://letsencrypt.org
The ListenAndServeTLS function is used to enable HTTPS,
where the additional arguments specify the
certificate and private key files,
which are named certificate.cer and certificate.key in my project
████████████████████████████████████████████████████████████████████████
422.Enabling HTTPS in the main.go
example:
main.go:
package main
import (
"io"
"net/http"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
Printfln("Request for %v", request.URL.Path)
io.WriteString(writer, sh.message)
}
func main() {
http.Handle("/message", StringHandler{"Hello, World"})
http.Handle("/favicon.ico", http.NotFoundHandler())
http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect))
go func() {
err := http.ListenAndServeTLS(":5500", "certificate.cer",
"certificate.key", nil)
if err != nil {
Printfln("HTTPS Error: %v", err.Error())
}
}()
err := http.ListenAndServe(":5000", nil)
if err != nil {
Printfln("Error: %v", err.Error())
}
}
Output:
HTTPS Error: open certificate.cer: no such file or directory
Request for /message
████████████████████████████████████████████████████████████████████████
423.Redirecting HTTP Requests to HTTPS
A common requirement when creating web servers is to redirect HTTP requests to the HTTPS port.
This can be done by creating a custom handler
example:
Redirecting to HTTPS in the main.go:
package main
import (
"net/http"
"io"
"strings"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter,
request *http.Request) {
Printfln("Request for %v", request.URL.Path)
io.WriteString(writer, sh.message)
}
func HTTPSRedirect(writer http.ResponseWriter,
request *http.Request) {
host := strings.Split(request.Host, ":")[0]
target := "https://" + host + ":5500" + request.URL.Path
if len(request.URL.RawQuery) > 0 {
target += "?" + request.URL.RawQuery
}
http.Redirect(writer, request, target, http.StatusTemporaryRedirect)
}
func main() {
http.Handle("/message", StringHandler{ "Hello, World"})
http.Handle("/favicon.ico", http.NotFoundHandler())
http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect))
go func () {
err := http.ListenAndServeTLS(":5520", "certificate.cer",
"certificate.key", nil)
if (err != nil) {
Printfln("HTTPS Error: %v", err.Error())
}
}()
err := http.ListenAndServe(":5000", http.HandlerFunc(HTTPSRedirect))
if (err != nil) {
Printfln("Error: %v", err.Error())
}
}
Output:
Not work for my Browser but you have to try, maybe work it for you.
████████████████████████████████████████████████████████████████████████
424.Creating a Static HTTP Server
The net/http package includes built-in support for responding to requests with the contents of files.
To prepare for the static HTTP server,
create the httpserver/static folder and add to it a file named index.html
example:
static/index.html:
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
<link href="bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="m-1 p-2 bg-primary text-white h2">
Hello, World
</div>
</body>
</html>
store.html:
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
<link href="bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="m-1 p-2 bg-primary text-white h2 text-center">
Products
</div>
<table class="table table-sm table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>Category</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Kayak</td>
<td>Watersports</td>
<td>$279.00</td>
</tr>
<tr>
<td>Lifejacket</td>
<td>Watersports</td>
<td>$49.95</td>
</tr>
</tbody>
</table>
</body>
</html>
The HTML files depend on the Bootstrap CSS package to style the HTML content. Run the command
shown in here in the httpserver folder to download the Bootstrap CSS file into the static folder.
(You may have to install the curl command.):
curl https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css --output static/bootstrap.min.css
Output:
ootstrap.min.css
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 152k 0 152k 0 0 163k 0 --:--:-- --:--:-- --:--:-- 163k
████████████████████████████████████████████████████████████████████████
425.Creating the Static File Route
example:
Defining a Route in the main.go:
package main
import (
"net/http"
"io"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter,
request *http.Request) {
Printfln("Request for %v", request.URL.Path)
io.WriteString(writer, sh.message)
}
func main() {
http.Handle("/message", StringHandler{ "Hello, World"})
http.Handle("/favicon.ico", http.NotFoundHandler())
http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect))
fsHandler := http.FileServer(http.Dir("./static"))
http.Handle("/files/", http.StripPrefix("/files", fsHandler))
err := http.ListenAndServe(":5000", nil)
if (err != nil) {
Printfln("Error: %v", err.Error())
}
}
Output:
redirect from => https://localhost:5500/files/store.html
to => static/store.html
████████████████████████████████████████████████████████████████████████
426.Using Templates to Generate Responses
example:
templates/products.html:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Pro Go</title>
<link rel="stylesheet" href="/files/bootstrap.min.css">
</head>
<body>
<h3 class="bg-primary text-white text-center p-2 m-2">Products</h3>
<div class="p-2">
<table class="table table-sm table-striped table-bordered">
<thead>
<tr>
<th>Index</th>
<th>Name</th>
<th>Category</th>
<th class="text-end">Price</th>
</tr>
</thead>
<tbody>
{{ range $index, $product := .Data }}
<tr>
<td>{{ $index }}</td>
<td>{{ $product.Name }}</td>
<td>{{ $product.Category }}</td>
<td class="text-end">
{{ printf "$%.2f" $product.Price }}
</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</body>
</html>
dynamic.go:
package main
import (
"html/template"
"net/http"
"strconv"
)
type Context struct {
Request *http.Request
Data []Product
}
var htmlTemplates *template.Template
func HandleTemplateRequest(writer http.ResponseWriter, request *http.Request) {
path := request.URL.Path
if (path == "") {
path = "products.html"
}
t := htmlTemplates.Lookup(path)
if (t == nil) {
http.NotFound(writer, request)
} else {
err := t.Execute(writer, Context{ request, Products})
if (err != nil) {
http.Error(writer, err.Error(), http.StatusInternalServerError)
}
}
}
func init() {
var err error
htmlTemplates = template.New("all")
htmlTemplates.Funcs(map[string]interface{} {
"intVal": strconv.Atoi,
})
htmlTemplates, err = htmlTemplates.ParseGlob("templates/*.html")
if (err == nil) {
http.Handle("/templates/", http.StripPrefix("/templates/",
http.HandlerFunc(HandleTemplateRequest)))
} else {
panic(err)
}
}
Output:
The initialization function loads all the templates with the html extension in the templates folder and
sets up a route so that requests that start with /templates/ are processed by the HandleTemplateRequest
function. This function looks up the template, falling back to the products.html file if no file path is
specified, executes the template, and writes the response.
████████████████████████████████████████████████████████████████████████
427.Understanding Content Type Sniffing
which implements the MIME Sniffing algorithm defined by:
https://mimesniff.spec.whatwg.org
The sniffing process can't detect every content type,
but it does well with standard web types, such as HTML, CSS, and JavaScript.
The DetectContentType function returns a MIME type,
which is used as the value for the Content-Type header.
████████████████████████████████████████████████████████████████████████
428.Responding with JSON Data
JSON responses are widely used in web services,
which provide access to an application's data for clients
that don't want to receive HTML, such as Angular or React JavaScript clients.
example:
json.go:
package main
import (
"net/http"
"encoding/json"
)
func HandleJsonRequest(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Content-Type", "application/json")
json.NewEncoder(writer).Encode(Products)
}
func init() {
http.HandleFunc("/json", HandleJsonRequest)
}
=========================================================================
main.go:
package main
import (
"net/http"
"io"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter,
request *http.Request) {
Printfln("Request for %v", request.URL.Path)
io.WriteString(writer, sh.message)
}
func main() {
http.Handle("/message", StringHandler{ "Hello, World"})
http.Handle("/favicon.ico", http.NotFoundHandler())
http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect))
fsHandler := http.FileServer(http.Dir("./static"))
http.Handle("/files/", http.StripPrefix("/files", fsHandler))
err := http.ListenAndServe(":5000", nil)
if (err != nil) {
Printfln("Error: %v", err.Error())
}
}
===================================================================
Output:
The initialization function creates a route,
which means that requests for /json will be processed by the
HandleJsonRequest function.
████████████████████████████████████████████████████████████████████████
429.Handling Form Data
The net/http package provides support for easily receiving and processing form data.
This template makes use of template variables, expressions,
and functions to get the query string from
the request and select the first index value,
which is converted to an int and used to retrieve a Product value
from the data provided to the template.
example:
edit.html:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Pro Go</title>
<link rel="stylesheet" href="/files/bootstrap.min.css">
</head>
<body>
{{ $index := intVal (index (index .Request.URL.Query "index") 0) }}
{{ if lt $index (len .Data)}}
{{ with index .Data $index}}
<h3 class="bg-primary text-white text-center p-2 m-2">Product</h3>
<form method="POST" action="/forms/edit" class="m-2">
<div class="form-group">
<label>Index</label>
<input name="index" value="{{$index}}" class="form-control" disabled />
<input name="index" value="{{$index}}" type="hidden" />
</div>
<div class="form-group">
<label>Name</label>
<input name="name" value="{{.Name}}" class="form-control" />
</div>
<div class="form-group">
<label>Category</label>
<input name="category" value="{{.Category}}" class="form-control" />
</div>
<div class="form-group">
<label>Price</label>
<input name="price" value="{{.Price}}" class="form-control" />
</div>
<div class="mt-2">
<button type="submit" class="btn btn-primary">Save</button>
<a href="/templates/" class="btn btn-secondary">Cancel</a>
</div>
</form>
{{ end }}
{{ else }}
<h3 class="bg-danger text-white text-center p-2">
No Product At Specified Index
</h3>
{{end }}
</body>
</html>
████████████████████████████████████████████████████████████████████████
430.Reading Form Data from Requests
The Request Form Data Fields and Methods
Name Description
-------- ------------------------------
Form This field returns a map[string][]string containing the parsed form data and the
query string parameters. The ParseForm method must be called before this field is read.
PostForm This field is similar to Form but excludes the query string parameters so that only
data from the request body is contained in the map. The ParseForm method must
be called before this field is read.
MultipartForm This field returns a multipart form represented using the Form struct defined in the
mime/multipart package. The ParseMultipartForm method must be called before
this field is read.
FormValue(key) This method returns the first value for the specified form key and returns the
empty string if there is no value. The source of data for this method is the Form
field, and calling the FormValue method automatically calls ParseForm or
ParseMultipartForm to parse the form.
PostFormValue(key) This method returns the first value for the specified form key and returns the
empty string if there is no value. The source of data for this method is the PostForm
field, and calling the PostFormValue method automatically calls ParseForm or
ParseMultipartForm to parse the form.
FormFile(key) This method provides access to the first file with the specified key in the form.
The results are a File and FileHeader, both of which are defined in the mime/
multipart package, and an error. Calling this function causes the ParseForm or
ParseMultipartForm functions to be invoked to parse the form.
ParseForm() This method parses a form and populates the Form and PostForm fields. The result
is an error that describes any parsing problems.
ParseMultipart This method parses a MIME multipart form and populates the MultipartForm field.
Form(max) The argument specifies the maximum number of bytes to allocate to the form data,
and the result is an error that describes any problems processing the form.
The init function sets up a new route so that the ProcessFormData function handles requests whose
path is /forms/edit. Within the ProcessFormData function, the request method is checked, and the form
data in the request is used to create a Product struct and replace the existing data value. In a real project,
validating the data submitted in the form is essential, but for this chapter I trust that the form contains
valid data.
Processing form data:
https://localhost:5500/templates/edit.html?index=2
example:
The Contents of the forms.go File in the httpserver Folder:
package main
import (
"net/http"
"strconv"
)
func ProcessFormData(writer http.ResponseWriter, request *http.Request) {
if (request.Method == http.MethodPost) {
index, _ := strconv.Atoi(request.PostFormValue("index"))
p := Product {}
p.Name = request.PostFormValue("name")
p.Category = request.PostFormValue("category")
p.Price, _ = strconv.ParseFloat(request.PostFormValue("price"), 64)
Products[index] = p
}
http.Redirect(writer, request, "/templates", http.StatusTemporaryRedirect)
}
func init() {
http.HandleFunc("/forms/edit", ProcessFormData)
}
Output:
without view of upload you can add data to templates URL.
████████████████████████████████████████████████████████████████████████
431.Reading Multipart Forms
example:
upload.html:
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
<link href="bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="m-1 p-2 bg-primary text-white h2 text-center">
Upload File
</div>
<form method="POST" action="/forms/upload" class="p-2"
enctype="multipart/form-data">
<div class="form-group">
<label class="form-label">Name</label>
<input class="form-control" type="text" name="name">
</div>
<div class="form-group">
<label class="form-label">City</label>
<input class="form-control" type="text" name="city">
</div>
<div class="form-group">
<label class="form-label">Choose Files</label>
<input class="form-control" type="file" name="files" multiple>
</div>
<button type="submit" class="btn btn-primary mt-2">Upload</button>
</form>
</body>
</html>
====================================================================================
upload.go:
package main
import (
"fmt"
"io"
"net/http"
)
func HandleMultipartForm(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Name: %v, City: %v\n", request.FormValue("name"),
request.FormValue("city"))
fmt.Fprintln(writer, "------")
file, header, err := request.FormFile("files")
if err == nil {
defer file.Close()
fmt.Fprintf(writer, "Name: %v, Size: %v\n", header.Filename, header.Size)
for k, v := range header.Header {
fmt.Fprintf(writer, "Key: %v, Value: %v\n", k, v)
}
fmt.Fprintln(writer, "------")
io.Copy(writer, file)
} else {
http.Error(writer, err.Error(), http.StatusInternalServerError)
}
}
func init() {
http.HandleFunc("/forms/upload", HandleMultipartForm)
}
Output: Search in Browser: http://localhost:5000/files/upload.html
You Can Upload
████████████████████████████████████████████████████████████████████████
432.The FileHeader Fields and Method
Name Description
------- ------------------------------
Name This field returns a string containing the name of the file.
Size This field returns an int64 containing the size of the file.
Header This field returns a map[string][]string, which contains the headers for the MIME part that
contains the file.
Open() This method returns a File that can be used to read the content associated with the header, as
demonstrated in the next section.
████████████████████████████████████████████████████████████████████████
433.Reading and Setting Cookies
The net/http Function for Setting Cookies
Name Description
--------------- ------------------------------------
SetCookie(writer, cookie) This function adds a Set-Cookie header to the specified ResponseWriter. The
cookie is described using a pointer to a Cookie struct, which is described next.
Cookies can be complex, and care must be taken to configure them correctly.
The detail of how cookies work is beyond the scope of this book,
but there is a good description available at
https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
and a detailed breakdown of the cookie
fields at:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie.
████████████████████████████████████████████████████████████████████████
434.The Fields Defined by the Cookie Struct
Name Description
------- --------------------------------
Name This field represents the name of the cookie, expressed as a string.
Value This field represents the cookie value, expressed as a string.
Path This optional field specifies the cookie path.
Domain This optional field specifies the host/domain to which the cookie will be set.
Expires This field specifies the cookie expiry, expressed as a time.Time value.
MaxAge This field specifies the number of seconds until the cookie expires, expressed as an int.
Secure When this bool field is true, the client will only send the cookie over HTTPS connections.
HttpOnly When this bool field is true, the client will prevent JavaScript code from accessing the cookie.
SameSite This field specifies the cross-origin policy for the cookie using the SameSite constants, which
defines SameSiteDefaultMode, SameSiteLaxMode, SameSiteStrictMode, and SameSiteNoneMode.
████████████████████████████████████████████████████████████████████████
435.The Request Methods for Cookies
Name Description
----------- ----------------
Cookie(name) This method returns a pointer to the Cookie value with the specified name and an error
that indicates when there is no matching cookie.
Cookies() This method returns a slice of Cookie pointers.
example:
cookies.go:
package main
import (
"net/http"
"fmt"
"strconv"
)
func GetAndSetCookie(writer http.ResponseWriter, request *http.Request) {
counterVal := 1
counterCookie, err := request.Cookie("counter")
if (err == nil) {
counterVal, _ = strconv.Atoi(counterCookie.Value)
counterVal++
}
http.SetCookie(writer, &http.Cookie{
Name: "counter", Value: strconv.Itoa(counterVal),
})
if (len(request.Cookies()) > 0) {
for _, c := range request.Cookies() {
fmt.Fprintf(writer, "Cookie Name: %v, Value: %v", c.Name, c.Value)
}
} else {
fmt.Fprintln(writer, "Request contains no cookies")
}
}
func init() {
http.HandleFunc("/cookies", GetAndSetCookie)
}
======================================================================================
Compile and execute the project and use a browser to request http://localhost:5000/cookies
Output:
search-1:
Request contains no cookies
search-2:
Cookie Name: counter, Value: 1
search-3:
Cookie Name: counter, Value: 2
...
`,
}
View Source
var OriginalJSONData = DataBase{
Alldatafield: `
302.Working with JSON Data
Putting Working with JSON Data in Context
What is it?
JSON data is the de facto standard for exchanging data, especially in HTTP applications.
Why is it useful?
JSON is simple enough to be supported by any language but can represent
relatively complex data.
How is it used?
The encoding/json package provides support for encoding and decoding JSON data.
Are there any pitfalls or limitations?
Not all Go data types can be represented in JSON, which requires the developer
to be mindful of how Go data types will be expressed.
Are there any alternatives?
There are many other data encodings available, some of which are supported by
the Go standard library.
The safest approach is to define a map with string keys and empty interface values, which ensures that
all the key-value pairs in the JSON data can be decoded into the map
Problem Solution
----------- ------------------------
Encode JSON dataCreate an Encoder with a Writer and invoke the Encode method
Control struct encoding Use JSON struct tags or implement the Mashaler interface
Decode JSON data Create a Decoder with a Reader and invoke the Decode method
Control struct decoding Use JSON struct tags or implement the Unmarshaler interface
████████████████████████████████████████████████████████████████████████
303.The encoding/json Constructor Functions for JSON Data
Name Description
------------------- --------------------------------------------
NewEncoder(writer) This function returns an Encoder, which can be used to encode JSON data and
write it to the specified Writer.
NewDecoder(reader) This function returns a Decoder, which can be used to read JSON data from the
specified Reader and decode it.
████████████████████████████████████████████████████████████████████████
304.The Functions for Creating and Parsing JSON Data
Name Description
------------------------ -----------------------------------------------
Marshal(value) This function encodes the specified value as JSON. The results are the
JSON content expressed in a byte slice and an error, which indicates any
encoding problems.
Unmarshal(byteSlice, val) This function parses JSON data contained in the specified slice of bytes
and assigns the result to the specified value.
████████████████████████████████████████████████████████████████████████
305.The Encoder Methods
The NewEncoder constructor function is used to create an Encoder, which can be used to write JSON data to a Writer.
Name Description
------------------------- --------------------------------------------------
Encode(val) This method encodes the specified value as JSON and writes it to the Writer.
SetEscapeHTML(on) This method accepts a bool argument that, when true, encodes
characters that would be dangerous in HTML to be escaped. The default
behavior is to escape these characters.
SetIndent(prefix, indent) This method specifies a prefix and indentation that is applied to the name
of each field in the JSON output.
████████████████████████████████████████████████████████████████████████
306.Expressing the Basic Go Data Types in JSON
Data TypeDescription
---------------- ------------------------------------
bool Go bool values are expressed as JSON true or false.
string Go string values are expressed as JSON strings. By default, unsafe HTML
characters are escaped.
float32, float64 Go floating-point values are expressed as JSON numbers.
int, int<size> Go integer values are expressed as JSON numbers.
uint, uint<size> Go integer values are expressed as JSON numbers.
byte Go bytes are expressed as JSON numbers.
rune Go runes are expressed as JSON numbers.
nil The Go nil value is expressed as the JSON null value.
Pointers The JSON encoder follows pointers and encodes the value at the pointer's location.
████████████████████████████████████████████████████████████████████████
307.encoding/json
example:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
var b bool = true
var str string = "Hello"
var fval float64 = 99.99
var ival int = 200
var pointer *int = &ival
var writer strings.Builder
encoder := json.NewEncoder(&writer)
for _, val := range []interface{}{b, str, fval, ival, pointer} {
encoder.Encode(val)
}
fmt.Print(writer.String())
}
Output:
true
"Hello"
99.99
200
200
████████████████████████████████████████████████████████████████████████
308.Encoding Slices and Arrays
Example:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
names := []string{"Kayak", "Lifejacket", "Soccer Ball"}
numbers := [3]int{10, 20, 30}
var byteArray [5]byte
copy(byteArray[0:], []byte(names[0]))
byteSlice := []byte(names[0])
var writer strings.Builder
encoder := json.NewEncoder(&writer)
encoder.Encode(names)
encoder.Encode(numbers)
encoder.Encode(byteArray)
encoder.Encode(byteSlice)
fmt.Print(writer.String())
}
Output:
["Kayak","Lifejacket","Soccer Ball"]
[10,20,30]
[75,97,121,97,107]
"S2F5YWs="
████████████████████████████████████████████████████████████████████████
309.Encoding Maps
Go maps are encoded as JSON objects, with the map keys used as the object keys. The values contained in
the map are encoded based on their type.
Maps can also be useful for creating custom JSON representations of Go data.
encoder.Encode()
example:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
m := map[string]float64{
"Kayak": 279,
"Lifejacket": 49.95,
}
var writer strings.Builder
encoder := json.NewEncoder(&writer)
encoder.Encode(m)
fmt.Print(writer.String())
}
Output:
{"Kayak":279,"Lifejacket":49.95}
████████████████████████████████████████████████████████████████████████
310.Encoding Structs
The Encoder expresses struct values as JSON objects, using the exported struct field names as the object’s
keys and the field values as the object's values
example:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
var writer strings.Builder
encoder := json.NewEncoder(&writer)
encoder.Encode(Kayak)
fmt.Print(writer.String())
}
Output:
{"Name":"Kayak","Category":"Watersports","Price":279}
████████████████████████████████████████████████████████████████████████
311.Effect of Promotion in JSON in Encoding
When a struct defines an embedded field that is also a struct, the fields of the embedded struct are promoted
and encoded as though they are defined by the enclosing type.
discount.go:
package main
type DiscountedProduct struct {
*Product
Discount float64
}
main.go:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
var writer strings.Builder
encoder := json.NewEncoder(&writer)
dp := DiscountedProduct {
Product: &Kayak,
Discount: 10.50,
}
encoder.Encode(&dp)
fmt.Print(writer.String())
}
Output:
{"Name":"Kayak","Category":"Watersports","Price":279,"Discount":10.5}
████████████████████████████████████████████████████████████████████████
312.Customizing the JSON Encoding of Structs
How a struct is encoded can be customized using struct tags, which are string literals that follow fields. Struct
tags are part of the Go support for reflection,
that tags follow fields and can be used to alter two aspects of how a field is encoded in JSON.
example:
discount.go:
package main
type DiscountedProduct struct {
*Product ^json:"product"^ // -------------------------> Use the symbol ^ above the Tab button instead
Discount float64
}
Output:
{"product":{"Name":"Kayak","Category":"Watersports","Price":279},"Discount":10.5}
████████████████████████████████████████████████████████████████████████
313.Omitting a Field حذف یک فیلد
The Encoder skips fields decorated with a tag that specifies a hyphen (the - character) for the name
The new tag tells the Encoder to skip the Discount field when creating the JSON representation of a
DIscountedProduct value.
exampe:
discount.go:
package main
type DiscountedProduct struct {
*Product ^json:"product"^ // -------------------------> Use the symbol ^ above the Tab button instead
Discount float64 ^json:"-"^ // -------------------------> Use the symbol ^ above the Tab button instead
}
Output:
{"product":{"Name":"Kayak","Category":"Watersports","Price":279}}
████████████████████████████████████████████████████████████████████████
314.Omitting Unassigned Fields
By default, the JSON Encoder includes struct fields, even when they have not been assigned a value
example:
main.go:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
var writer strings.Builder
encoder := json.NewEncoder(&writer)
dp := DiscountedProduct{
Product: &Kayak,
Discount: 10.50,
}
encoder.Encode(&dp)
dp2 := DiscountedProduct { Discount: 10.50 }
encoder.Encode(&dp2)
fmt.Print(writer.String())
}
Output:
{"product":{"Name":"Kayak","Category":"Watersports","Price":279}}
{"product":null}
To omit a nil field, the omitempty keyword is added to the tag for the field
discount.go:
package main
type DiscountedProduct struct {
// *Product ^json:"product"^ // -------------------------> Use the symbol ^ above the Tab button instead
*Product ^json:"product,omitempty"^ // -------------------------> Use the symbol ^ above the Tab button instead
Discount float64 ^json:"-"^
}
Output:
{"product":{"Name":"Kayak","Category":"Watersports","Price":279}}
{}
To skip a nil field without changing the name or field promotion, specify the omitempty keyword without a name
discount.go:
package main
type DiscountedProduct struct {
// *Product ^json:"product"^
// *Product ^json:"product,omitempty"^
*Product ^json:",omitempty"^ // -------------------------> Use the symbol ^ above the Tab button instead
Discount float64 ^json:"-"^ // -------------------------> Use the symbol ^ above the Tab button instead
}
Output:
{"Name":"Kayak","Category":"Watersports","Price":279}
{}
████████████████████████████████████████████████████████████████████████
315.Forcing Fields to be Encoded as Strings
Struct tags can be used to force a field value to be encoded as a string, overriding the normal encoding for
the field type
example:
discount.go:
package main
type DiscountedProduct struct {
*Product ^json:",omitempty"^
// Discount float64 ^json:"-"^ // -------------------------> Use the symbol ^ above the Tab button
Discount float64 ^json:",string"^ // -------------------------> Use the symbol ^ above the Tab button
}
Output:
{"Name":"Kayak","Category":"Watersports","Price":279,"Discount":"10.5"}
{"Discount":"10.5"}
████████████████████████████████████████████████████████████████████████
316.Encoding Interfaces
The JSON encoder can be used on values assigned to interface variables, but it is the dynamic type that
is encoded.
No aspect(جنبه) of the interface is used to adapt the JSON, and all the exported fields of each value in the slice
are included in the JSON. This can be a useful feature, but care must be taken when decoding this kind of
JSON, because each value can have a different set of fields
example:
interface.go:
package main
type Named interface{ GetName() string }
type Person struct{ PersonName string }
func (p *Person) GetName() string { return p.PersonName }
func (p *DiscountedProduct) GetName() string { return p.Name }
main.go:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
var writer strings.Builder
encoder := json.NewEncoder(&writer)
dp := DiscountedProduct{
Product: &Kayak,
Discount: 10.50,
}
namedItems := []Named { &dp, &Person{ PersonName: "Alice"}}
encoder.Encode(namedItems)
fmt.Print(writer.String())
}
Output:
[{"Name":"Kayak","Category":"Watersports","Price":279,"Discount":"10.5"},{"PersonName":"Alice"}]
████████████████████████████████████████████████████████████████████████
317.The Marshaler Method
Creating Completely Custom JSON Encodings
The Encoder checks to see whether a struct implements the Marshaler interface, which denotes a type that
has a custom encoding and which defines the method.
Name Description
-------------- ------------------------------------------
MarshalJSON() This method is invoked to create a JSON representation of a value and returns a byte
slice containing the JSON and an error indicating encoding problems.
████████████████████████████████████████████████████████████████████████
318.json.Marshal()
The MarshalJSON method can generate JSON in any way that suits the project.
I define a map with string keys and use the
empty interface for the values. This allows me to build the JSON by adding key-value pairs to the map
and then pass the map to the Marshal function, which uses the built-in support
to encode each of the values contained in the map.
example:
discount.go:
package main
import "encoding/json"
type DiscountedProduct struct {
*Product ^json:",omitempty"^
Discount float64 ^json:",string"^
}
func (dp *DiscountedProduct) MarshalJSON() (jsn []byte, err error) {
if dp.Product != nil {
m := map[string]interface{}{
"product": dp.Name,
"cost": dp.Price - dp.Discount,
}
jsn, err = json.Marshal(m)
}
return
}
main.go:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
var writer strings.Builder
encoder := json.NewEncoder(&writer)
dp := DiscountedProduct{
Product: &Kayak,
Discount: 10.50,
}
namedItems := []Named { &dp, &Person{ PersonName: "Alice"}}
encoder.Encode(namedItems)
fmt.Print(writer.String())
}
Output:
[{"cost":268.5,"product":"Kayak"},{"PersonName":"Alice"}]
████████████████████████████████████████████████████████████████████████
319.Decoding JSON Data
The NewDecoder constructor function creates a Decoder, which can be used to decode JSON data obtained
from a Reader.
The Decoder Methods:
Name Description
--------------------- --------------------------------------------
Decode(value) This method reads and decodes data, which is used to create the specified
value. The method returns an error that indicates problems decoding the
data to the required type or EOF.
DisallowUnknownFields() By default, when decoding a struct type, the Decoder ignores any key in
the JSON data for which there is no corresponding struct field. Calling this
method causes the Decode to return an error, rather than ignoring the key.
UseNumber() By default, JSON number values are decoded into float64 values. Calling
this method uses the Number type instead, as described in the “Decoding
Number Values” section.
████████████████████████████████████████████████████████████████████████
320.Decoding Basic Data Types
example:
main.go:
package main
import (
"encoding/json"
"io"
"strings"
"asd/asd"
)
func main() {
reader := strings.NewReader(^true "Hello" 99.99 200^) // -------------------------> Use the symbol ^ above the Tab button
vals := []interface{}{}
decoder := json.NewDecoder(reader)
for {
var decodedVal interface{}
err := decoder.Decode(&decodedVal)
if err != nil {
if err != io.EOF {
asd.Printfln("Error: %v", err.Error())
}
break
}
vals = append(vals, decodedVal)
}
for _, val := range vals {
asd.Printfln("Decoded (%T): %v", val, val)
}
}
Output:
Decoded (bool): true
Decoded (string): Hello
Decoded (float64): 99.99
Decoded (float64): 200
████████████████████████████████████████████████████████████████████████
321.Decoding Number Values
The Methods Defined by the Number Type
Name Description
--------- ------------------------------------
Int64() This method returns the decoded value as a int64 and an error that indicates if the value
cannot be converted.
Float64() This method returns the decoded value as a float64 and an error that indicates if the
value cannot be converted.
String() This method returns the unconverted string from the JSON data.
Not all JSON number values can be expressed as
Go int64 values, so this is the method that is typically called first.
If attempting to convert to an integer
fails, then the Float64 method can be called.
If a number cannot be converted to either Go type, then the
String method can be used to get the unconverted string from the JSON data.
████████████████████████████████████████████████████████████████████████
322.Decoding Numbers
example:
main.go:
package main
import (
"encoding/json"
"io"
"strings"
)
func main() {
reader := strings.NewReader(^true "Hello" 99.99 200^) // -------------------------> Use the symbol ^ above the Tab button
vals := []interface{}{}
decoder := json.NewDecoder(reader)
for {
var decodedVal interface{}
err := decoder.Decode(&decodedVal)
if err != nil {
if err != io.EOF {
Printfln("Error: %v", err.Error())
}
break
}
vals = append(vals, decodedVal)
}
for _, val := range vals {
if num, ok := val.(json.Number); ok {
if ival, err := num.Int64(); err == nil {
Printfln("Decoded Integer: %v", ival)
} else if fpval, err := num.Float64(); err == nil {
Printfln("Decoded Floating Point: %v", fpval)
} else {
Printfln("Decoded String: %v", num.String())
}
} else {
Printfln("Decoded (%T): %v", val, val)
}
}
}
Output:
Decoded (bool): true
Decoded (string): Hello
Decoded (float64): 99.99
Decoded (float64): 200
████████████████████████████████████████████████████████████████████████
323.Specifying Types for Decoding
example:
main.go:
package main
import (
"encoding/json"
"strings"
)
func main() {
reader := strings.NewReader(^true "Hello" 99.99 200^) // -------------------------> Use the symbol ^ above the Tab button
var bval bool
var sval string
var fpval float64
var ival int
vals := []interface{}{&bval, &sval, &fpval, &ival}
decoder := json.NewDecoder(reader)
for i := 0; i < len(vals); i++ {
err := decoder.Decode(vals[i])
if err != nil {
Printfln("Error: %v", err.Error())
break
}
}
Printfln("Decoded (%T): %v", bval, bval)
Printfln("Decoded (%T): %v", sval, sval)
Printfln("Decoded (%T): %v", fpval, fpval)
Printfln("Decoded (%T): %v", ival, ival)
}
Output:
Decoded (bool): true
Decoded (string): Hello
Decoded (float64): 99.99
Decoded (int): 200
████████████████████████████████████████████████████████████████████████
324.Decoding Arrays
The Decoder processes arrays automatically, but care must be taken because JSON allows arrays to contain
values of different types, which conflicts with the strict type rules enforced by Go.
The source JSON data contains two arrays, one of which contains only numbers and one of which mixes
numbers and strings. The Decoder doesn’t try to figure out if a JSON array can be represented using a single
Go type and decodes every array into an empty interface slice:
example:
main.go:
package main
import (
"encoding/json"
"io"
"strings"
)
func main() {
reader := strings.NewReader(^[10,20,30]["Kayak","Lifejacket",279]^) // -------------------------> Use the symbol ^ above the Tab button
vals := []interface{}{}
decoder := json.NewDecoder(reader)
for {
var decodedVal interface{}
err := decoder.Decode(&decodedVal)
if err != nil {
if err != io.EOF {
Printfln("Error: %v", err.Error())
}
break
}
vals = append(vals, decodedVal)
}
for _, val := range vals {
Printfln("Decoded (%T): %v", val, val)
}
}
Output:
Decoded ([]interface {}): [10 20 30]
Decoded ([]interface {}): [Kayak Lifejacket 279]
████████████████████████████████████████████████████████████████████████
325.Specifying the Decoded Array Type
The second array contains a mix of values, which means that I have to specify
the empty interface as the target type. The literal slice syntax is awkward when using the empty interface
because two sets of braces are required:
...
mixed := []interface{} {}
example:
main.go:
package main
import (
"encoding/json"
"strings"
)
func main() {
reader := strings.NewReader(^[10,20,30]["Kayak","Lifejacket",279]^) // -------------------------> Use the symbol ^ above the Tab button
ints := []int {}
mixed := []interface{} {}
vals := []interface{} { &ints, &mixed}
decoder := json.NewDecoder(reader)
for i := 0; i < len(vals); i++ {
err := decoder.Decode(vals[i])
if err != nil {
Printfln("Error: %v", err.Error())
break
}
}
Printfln("Decoded (%T): %v", ints, ints)
Printfln("Decoded (%T): %v", mixed, mixed)
}
Output:
Decoded ([]int): [10 20 30]
Decoded ([]interface {}): [Kayak Lifejacket 279]
████████████████████████████████████████████████████████████████████████
326.Decoding Maps
The safest approach is to define a map with string keys and empty interface values, which ensures that
all the key-value pairs in the JSON data can be decoded into the map
JavaScript objects are expressed as key-value pairs, which makes it easy to decode them into Go maps
example:
main.go:
package main
import (
"encoding/json"
"strings"
)
func main() {
reader := strings.NewReader(^{"Kayak" : 279, "Lifejacket" : 49.95}^) // -------------------------> Use the symbol ^ above the Tab button
m := map[string]interface{}{}
decoder := json.NewDecoder(reader)
err := decoder.Decode(&m)
if err != nil {
Printfln("Error: %v", err.Error())
} else {
Printfln("Map: %T, %v", m, m)
for k, v := range m {
Printfln("Key: %v, Value: %v", k, v)
}
}
}
Output:
Map: map[string]interface {}, map[Kayak:279 Lifejacket:49.95]
Key: Kayak, Value: 279
Key: Lifejacket, Value: 49.95
████████████████████████████████████████████████████████████████████████
327.a Specific Value Type
A single JSON object can be used for multiple data types as values, but if you know in advance that you
will be decoding a JSON object that has a single value type, then you can be more specific when defining the
map into which the data will be decoded.
example:
main.go:
package main
import (
"encoding/json"
"strings"
)
func main() {
reader := strings.NewReader(^{"Kayak" : 279, "Lifejacket" : 49.95}^) // -------------------------> Use the symbol ^ above the Tab button
m := map[string]float64 {}
decoder := json.NewDecoder(reader)
err := decoder.Decode(&m)
if err != nil {
Printfln("Error: %v", err.Error())
} else {
Printfln("Map: %T, %v", m, m)
for k, v := range m {
Printfln("Key: %v, Value: %v", k, v)
}
}
}
Output:
Map: map[string]float64, map[Kayak:279 Lifejacket:49.95]
Key: Kayak, Value: 279
Key: Lifejacket, Value: 49.95
████████████████████████████████████████████████████████████████████████
328.Decoding Structs
The Decoder decodes the JSON object and uses the keys to set the values of the exported struct fields.
The capitalization of the fields and JSON keys don't have to match, and the Decoder will ignore any JSON
key for which there isn't a struct field and ignore any struct field for which there is no JSON key.
The JSON objectscontain different capitalization and have more or fewer keys than the Product struct
fields. The Decoder processes the data as best as it can.
example:
main.go:
package main
import (
"strings"
"encoding/json"
"io"
)
func main() {
reader := strings.NewReader(^ // -------------------------> Use the symbol ^ above the Tab button
{"Name":"Kayak","Category":"Watersports","Price":279}
{"Name":"Lifejacket","Category":"Watersports" }
{"name":"Canoe","category":"Watersports", "price": 100, "inStock": true }
^) // -------------------------> Use the symbol ^ above the Tab button
decoder := json.NewDecoder(reader)
for {
var val Product
err := decoder.Decode(&val)
if err != nil {
if err != io.EOF {
Printfln("Error: %v", err.Error())
}
break
} else {
Printfln("Name: %v, Category: %v, Price: %v",
val.Name, val.Category, val.Price)
}
}
}
Output:
Name: Kayak, Category: Watersports, Price: 279
Name: Lifejacket, Category: Watersports, Price: 0
Name: Canoe, Category: Watersports, Price: 100
████████████████████████████████████████████████████████████████████████
329.Decoding to Interface Types
As I explained earlier in the chapter, the JSON encoder deals with interfaces by encoding the value
using the exported fields of the dynamic type. This is because JSON deals with key-value pairs and
has no way to express methods. As a consequence, you cannot decode directly to an interface variable
from JSON. Instead, you must decode to a struct or map and then assign the value that is created to an
interface variable.
████████████████████████████████████████████████████████████████████████
330.Disallowing Unused Keys غیر مجاز کردن
By default, the Decoder will ignore JSON keys for which there is no corresponding struct field. This behavior
can be changed by calling the DisallowUnknownFields method
example:
Disallowing Unused Keys in the main.go
...
decoder := json.NewDecoder(reader)
decoder.DisallowUnknownFields()
...
output:
Name: Kayak, Category: Watersports, Price: 279
Name: Lifejacket, Category: Watersports, Price: 0
Error: json: unknown field "inStock"
████████████████████████████████████████████████████████████████████████
331.Struct Tags
The tag applied to the Discount field tells the Decoder that the value for this field should be obtained
from the JSON key named offer and that the value will be parsed from a string, instead of the JSON
number that would usually be expected for a Go float64 value.
example:
discount.go:
package main
import "encoding/json"
type DiscountedProduct struct {
*Product ^json:",omitempty"^ // -------------------------> Use the symbol ^ above the Tab button
Discount float64 ^json:"offer,string"^ // -------------------------> Use the symbol ^ above the Tab button
}
func (dp *DiscountedProduct) MarshalJSON() (jsn []byte, err error) {
if (dp.Product != nil) {
m := map[string]interface{} {
"product": dp.Name,
"cost": dp.Price - dp.Discount,
}
jsn, err = json.Marshal(m)
}
return
}
main.go:
package main
import (
"strings"
"encoding/json"
"io"
)
func main() {
reader := strings.NewReader(^
{"Name":"Kayak","Category":"Watersports","Price":279, "Offer": "10"}^) // -------------------------> Use the symbol ^ above the Tab button
decoder := json.NewDecoder(reader)
for {
var val DiscountedProduct
err := decoder.Decode(&val)
if err != nil {
if err != io.EOF {
Printfln("Error: %v", err.Error())
}
break
} else {
Printfln("Name: %v, Category: %v, Price: %v, Discount: %v",
val.Name, val.Category, val.Price, val.Discount)
}
}
}
Output:
Name: Kayak, Category: Watersports, Price: 279, Discount: 10
████████████████████████████████████████████████████████████████████████
332.Creating Completely Custom JSON Decoders
The Unmarshaler Method
Name Description
------------------------ ------------------------------------------
UnmarshalJSON(byteSlice) This method is invoked to decode JSON data contained in the specified
byte slice. The result is an error indicating encoding problems.
████████████████████████████████████████████████████████████████████████
333.Defining a Custom Decoder
This implementation of the UnmarshalJSON method uses the Unmarshal method to decode the JSON
data into a map and then checks the type of each value required for the DiscountedProduct struct.
example:
discount.go:
package main
import (
"encoding/json"
"strconv"
)
type DiscountedProduct struct {
*Product ^json:",omitempty"^ // -------------------------> Use the symbol ^ above the Tab button
Discount float64 ^json:"offer,string"^ // -------------------------> Use the symbol ^ above the Tab button
}
func (dp *DiscountedProduct) MarshalJSON() (jsn []byte, err error) {
if dp.Product != nil {
m := map[string]interface{}{
"product": dp.Name,
"cost": dp.Price - dp.Discount,
}
jsn, err = json.Marshal(m)
}
return
}
func (dp *DiscountedProduct) UnmarshalJSON(data []byte) (err error) {
mdata := map[string]interface{}{}
err = json.Unmarshal(data, &mdata)
if dp.Product == nil {
dp.Product = &Product{}
}
if err == nil {
if name, ok := mdata["Name"].(string); ok {
dp.Name = name
}
if category, ok := mdata["Category"].(string); ok {
dp.Category = category
}
if price, ok := mdata["Price"].(float64); ok {
dp.Price = price
}
if discount, ok := mdata["Offer"].(string); ok {
fpval, fperr := strconv.ParseFloat(discount, 64)
if fperr == nil {
dp.Discount = fpval
}
}
}
return
}
Output:
Name: Kayak, Category: Watersports, Price: 279, Discount: 10
`,
}
View Source
var OriginalReadingandWriting = DataBase{
Alldatafield: `
273.Reading and Writing Data
These interfaces are used wherever data is read or written, which means that any
source or destination for data can be treated in much the same way so that writing data to a file, for example,
is just the same as writing data to a network connection.
What are they?
These interfaces define the basic methods required to read and write data.
Why are they useful?
This approach means that just about any data source can be used
in the same way, while still allowing specialized features to be
defined using the composition features.
How is it used?
The io package defines these interfaces, but the implementations
are available from a range of other packages
Are there any pitfalls or limitations?
These interfaces don't entirely hide the detail of sources or
destinations for data and additional methods are often required,
provided by interfaces that build on Reader and Writer.
Are there any alternatives?
The use of these interfaces is optional, but they are hard to avoid
because they are used throughout the standard library.
The Reader and Writer interfaces are defined by the io package and
provide abstract ways to read and write data,
without being tied to where the data is coming from or going to.
Preparing for This Chapter:
product.go:
package main
type Product struct {
Name, Category string
Price float64
}
var Kayak = Product{
Name: "Kayak",
Category: "Watersports",
Price: 279,
}
var Products = []Product{
{"Kayak", "Watersports", 279},
{"Lifejacket", "Watersports", 49.95},
{"Soccer Ball", "Soccer", 19.50},
{"Corner Flags", "Soccer", 34.95},
{"Stadium", "Soccer", 79500},
{"Thinking Cap", "Chess", 16},
{"Unsteady Chair", "Chess", 75},
{"Bling-Bling King", "Chess", 1200},
}
printer.go:
package main
import (
"fmt"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
main.go:
package main
func main() {
Printfln("Product: %v, Price : %v", Kayak.Name, Kayak.Price)
}
████████████████████████████████████████████████████████████████████████
274.The Reader interface
The Reader interface doesn't include any detail about where data comes from or how it is obtained—it
just defines the Read method. The details are left to the types that implement the interface, and there are
reader implementations in the standard library for different data sources.
defines a single method:
Name Description
-------------- -------------------------
Read(byteSlice) This method reads data into the specified []byte. The method returns the number of
bytes that were read, expressed as an int, and an error.
████████████████████████████████████████████████████████████████████████
275.io.Reader package
example:
package main
import (
"io"
"strings"
)
func processData(reader io.Reader) {
b := make([]byte, 2)
for {
count, err := reader.Read(b)
if count > 0 {
Printfln("Read %v bytes: %v", count, string(b[0:count]))
}
if err == io.EOF {
break
}
}
}
func main() {
r := strings.NewReader("Kayak")
processData(r)
}
Output:
Read 2 bytes: Ka
Read 2 bytes: ya
Read 1 bytes: k
████████████████████████████████████████████████████████████████████████
276.Writer interface
Write(byteSlice)
This method writes the data from the specified byte slice. The method returns the
number of bytes that were written and an error. The error will be non-nil if the
number of bytes written is less than the length of the slice.
The Writer interface doesn't include any details of how the written data is stored, transmitted, or
processed, all of which is left to the types that implement the interface.
████████████████████████████████████████████████████████████████████████
277.io.Writer
example:
package main
import(
"io"
"strings"
"asd/asd"
)
func processData(reader io.Reader, writer io.Writer) {
b := make([]byte, 2)
for {
count, err := reader.Read(b)
if count > 0 {
writer.Write(b[0:count])
asd.Printfln("Read %v bytes: %v", count, string(b[0:count]))
}
if err == io.EOF {
break
}
}
}
func main() {
r := strings.NewReader("Kayak")
var builder strings.Builder
processData(r, &builder)
asd.Printfln("String builder contents: %s", builder.String())
}
Output:
Read 2 bytes: Ka
Read 2 bytes: ya
Read 1 bytes: k
String builder contents: Kayak
████████████████████████████████████████████████████████████████████████
278.io.EOF
The io package defines a special error named EOF,
which is used to signal when the Reader reaches the end of the data.
If the error result from the Read function is equal to the EOF error,
then I break out of the for
loop that has been reading data from the Reader:
...
if err == io.EOF {
break
}
...
████████████████████████████████████████████████████████████████████████
279.the Utility Functions for Readers and Writers
توابع مفید برای خوانندگان و نویسندگان
Functions in the io Package for Readng and Writing Data
Name Description
------------------------ -------------------------------------------------------
Copy(w, r) This function copies data from a Reader to a Writer until EOF is returned or
another error is encountered. The results are the number of bytes copies and an
error used to describe any problems.
CopyBuffer(w, r, buffer) This function performs the same task as Copy but reads the data into the
specified buffer before it is passed to the Writer.
CopyN(w, r, count) This function copies count bytes from the Reader to the Writer. The results are
the number of bytes copies and an error used to describe any problems.
ReadAll(r) This function reads data from the specified Reader until EOF is reached. The
results are a byte slice containing the read data and an error, which is used to
describe any problems.
ReadAtLeast(r, byteSlice, min) This function reads at least the specified number of bytes from the reader,
placing them into the byte slice. An error is reported if fewer bytes than specified
are read.
ReadFull(r, byteSlice) This function fills the specified byte slice with data. The result is the number
of bytes read and an error. An error will be reported if EOF was encountered
before enough bytes to fill the slice were read.
WriteString(w, str) This function writes the specified string to a writer.
████████████████████████████████████████████████████████████████████████
280.io.Copy(writer, reader)
example:
package main
import(
"io"
"asd/asd"
"strings"
)
func processData(reader io.Reader, writer io.Writer) {
count, err := io.Copy(writer, reader)
if (err == nil) {
asd.Printfln("Read %v bytes", count)
} else {
asd.Printfln("Error: %v", err.Error())
}
}
func main() {
r := strings.NewReader("Kayak .")
var builder strings.Builder
processData(r, &builder)
asd.Printfln("String builder contents: %s", builder.String())
}
Output:
Read 7 bytes
String builder contents: Kayak .
████████████████████████████████████████████████████████████████████████
281.The io Package Functions for Specialized Readers and Writers
Name Description
----------- ----------------------------------------
Pipe() This function returns a PipeReader and a PipeWriter, which can be used to connect
functions that require a Reader and a Writer, as described in the “Using Pipes” section.
MultiReader(...readers) This function defines a variadic parameter that allows an arbitrary number of Reader
values to be specified. The result is a Reader that passes on the content from each of
its parameters in the sequence they are defined, as described in the “Concatenating
Multiple Readers” section.
MultiWriter(...writers) This function defines a variadic parameter that allows an arbitrary number of Writer
values to be specified. The result is a Writer that sends the same data to all the
specified writers, as described in the “Combining Multiple Writers” section.
LimitReader(r, limit) This function creates a Reader that will EOF after the specified number of bytes, as
described in the “Limiting Read Data” section.
████████████████████████████████████████████████████████████████████████
282.Pipe
Pipes are used to connect code that consumes data through a Reader
and code that produces code through a Writer.
The GenerateData function defines a Writer parameter, which it uses to write bytes from a string.
example:
data.go:
package main
import (
"io"
"asd/asd"
)
func GenerateData(writer io.Writer) {
data := []byte("Kayak, Lifejacket")
writeSize := 4
for i := 0; i < len(data); i += writeSize {
end := i + writeSize;
if (end > len(data)) {
end = len(data)
}
count, err := writer.Write(data[i: end])
asd.Printfln("Wrote %v byte(s): %v", count, string(data[i: end]))
if (err != nil) {
asd.Printfln("Error: %v", err.Error())
}
}
}
func ConsumeData(reader io.Reader) {
data := make([]byte, 0, 10)
slice := make([]byte, 2)
for {
count, err := reader.Read(slice)
if (count > 0) {
asd.Printfln("Read data: %v", string(slice[0:count]))
data = append(data, slice[0:count]...)
}
if (err == io.EOF) {
break
}
}
asd.Printfln("Read data: %v", string(data))
}
Notice the parentheses at the end of this statement. These are required when creating a goroutine for an
anonymous function, but it is easy to forget them.
main.go:
package main
import (
"io"
)
func main() {
pipeReader, pipeWriter := io.Pipe()
go func() {
GenerateData(pipeWriter)
pipeWriter.Close()
}()
ConsumeData(pipeReader)
}
The output highlights the fact that pipes are synchronous. The GenerateData function calls the writer’s
Write method and then blocks until the data is read. This is why the first message in the output is from the
reader: the reader is consuming the data two bytes at a time, which means that two read operations are
required before the initial call to the Write method, which is used to send four bytes, completes, and the
message from the GenerateData function is displayed.
Output:
Read data: Ka
Read data: ya
Wrote 4 byte(s): Kaya
Read data: k,
Read data: L
Wrote 4 byte(s): k, L
Read data: if
Read data: ej
Wrote 4 byte(s): ifej
Read data: ac
Read data: ke
Wrote 4 byte(s): acke
Read data: t
Wrote 1 byte(s): t
Read data: Kayak, Lifejacket
████████████████████████████████████████████████████████████████████████
283.PipeReader and a PipeWriter
The io.Pipe function returns a PipeReader and a PipeWriter. The PipeReader and PipeWriter structs
implement the Closer interface
Name Description
------- -----------------------
Close() This method closes the reader or writer. The details are implementation specific, but, in
general, any subsequent reads from a closed Reader will return zero bytes and the EOF error,
while any subsequent writes to a closed Writer will return an error.
The PipeReader struct implements the Reader interface, which means I can use it as the argument to
the ConsumeData function. The ConsumeData function is executed in the main goroutine, which means that
the application won't exit until the function completes.
The effect is that data is written into the pipe using the PipeWriter and read from the pipe using the
PipeReader. When the GenerateData function is complete, the Close method is called on the PipeWriter,
which causes the next read by the PipeReader to produce EOF.
████████████████████████████████████████████████████████████████████████
284.io.MultiReader()
example:
main.go:
package main
import (
"io"
"strings"
)
func main() {
r1 := strings.NewReader("Kayak")
r2 := strings.NewReader("Lifejacket")
r3 := strings.NewReader("Canoe")
concatReader := io.MultiReader(r1, r2, r3)
ConsumeData(concatReader)
}
Output:
Read data: Ka
Read data: ya
Read data: k
Read data: Li
Read data: fe
Read data: ja
Read data: ck
Read data: et
Read data: Ca
Read data: no
Read data: e
Read data: KayakLifejacketCanoe
████████████████████████████████████████████████████████████████████████
285.io.MultiWriter()
example:
package main
import (
"io"
"strings"
"asd/asd"
)
func main() {
var w1 strings.Builder
var w2 strings.Builder
var w3 strings.Builder
combinedWriter := io.MultiWriter(&w1, &w2, &w3)
GenerateData(combinedWriter)
asd.Printfln("Writer #1: %v", w1.String())
asd.Printfln("Writer #2: %v", w2.String())
asd.Printfln("Writer #3: %v", w3.String())
}
Output:
Wrote 4 byte(s): Kaya
Wrote 4 byte(s): k, L
Wrote 4 byte(s): ifej
Wrote 4 byte(s): acke
Wrote 1 byte(s): t
Writer #1: Kayak, Lifejacket
Writer #2: Kayak, Lifejacket
Writer #3: Kayak, Lifejacket
████████████████████████████████████████████████████████████████████████
286.io.TeeReader(concatReader, &writer)
Echoing Reads to a Writer
The TeeReader function returns a Reader that echoes the data that it receives to a Writer.
example:
package main
import (
"asd/asd"
"io"
"strings"
)
func main() {
r1 := strings.NewReader("Kayak")
r2 := strings.NewReader("Lifejacket")
r3 := strings.NewReader("Canoe")
concatReader := io.MultiReader(r1, r2, r3)
var writer strings.Builder
teeReader := io.TeeReader(concatReader, &writer)
ConsumeData(teeReader)
asd.Printfln("Echo data: %v", writer.String())
}
Output:
Read data: Ka
Read data: ya
Read data: k
Read data: Li
Read data: fe
Read data: ja
Read data: ck
Read data: et
Read data: Ca
Read data: no
Read data: e
Read data: KayakLifejacketCanoe
Echo data: KayakLifejacketCanoe
████████████████████████████████████████████████████████████████████████
287.io.LimitReader(concatReader, 5)
The LimitReader function is used to restrict the amount of data that can be obtained from a Reader
example:
package main
import (
"io"
"strings"
)
func main() {
r1 := strings.NewReader("Kayak")
r2 := strings.NewReader("Lifejacket")
r3 := strings.NewReader("Canoe")
concatReader := io.MultiReader(r1, r2, r3)
limited := io.LimitReader(concatReader, 5)
ConsumeData(limited)
}
Output:
Read data: Ka
Read data: ya
Read data: k
Read data: Kayak
████████████████████████████████████████████████████████████████████████
288.bufio
The bufio package provides support for adding buffers to readers and writers.
example:
This code defined a struct type named CustomReader that acts as a wrapper around a Reader.
The implementation of the Read method generates output that reports how much data is read and
how many read operations are performed overall.
custom.go:
package main
import (
"io"
"asd/asd"
)
type CustomReader struct {
reader io.Reader
readCount int
}
func NewCustomReader(reader io.Reader) *CustomReader {
return &CustomReader{reader, 0}
}
func (cr *CustomReader) Read(slice []byte) (count int, err error) {
count, err = cr.reader.Read(slice)
cr.readCount++
asd.Printfln("Custom Reader: %v bytes", count)
if err == io.EOF {
asd.Printfln("Total Reads: %v", cr.readCount)
}
return
}
main.go:
package main
import (
"asd/asd"
"io"
"strings"
)
func main() {
text := "It was a boat. A small boat."
var reader io.Reader = NewCustomReader(strings.NewReader(text))
var writer strings.Builder
slice := make([]byte, 5)
for {
count, err := reader.Read(slice)
if count > 0 {
writer.Write(slice[0:count])
}
if err != nil {
break
}
}
asd.Printfln("Read data: %v", writer.String())
}
The NewCustomreader function is used to create a CustomReader that reads from a string and uses a for
loop to consume the data using a byte slice.
Output:
Custom Reader: 5 bytes
Custom Reader: 5 bytes
Custom Reader: 5 bytes
Custom Reader: 5 bytes
Custom Reader: 5 bytes
Custom Reader: 3 bytes
Custom Reader: 0 bytes
Total Reads: 7
Read data: It was a boat. A small boat.
████████████████████████████████████████████████████████████████████████
289.bufio Functions for Creating Buffered Readers
Name Description
---------------------- ---------------------------------
NewReader(r) This function returns a buffered Reader with the default buffer size (which is
4,096 bytes at the time of writing).
NewReaderSize(r, size) This function returns a buffered Reader with the specified buffer size.
████████████████████████████████████████████████████████████████████████
290.bufio.NewReader(reader)
The default buffer size is 4,096 bytes, which means that the buffered reader was able to read all the data
in a single read operation, plus an additional read to produce the EOF result. Introducing the buffer reduces
the overhead associated with the read operations, albeit at the cost of the memory used to buffer the data.
example:
package main
import (
"io"
"strings"
"bufio"
)
func main() {
text := "It was a boat. A small boat."
var reader io.Reader = NewCustomReader(strings.NewReader(text))
var writer strings.Builder
slice := make([]byte, 5)
reader = bufio.NewReader(reader)
for {
count, err := reader.Read(slice)
if (count > 0) {
writer.Write(slice[0:count])
}
if (err != nil) {
break
}
}
Printfln("Read data: %v", writer.String())
}
Output:
Custom Reader: 28 bytes
Custom Reader: 0 bytes
Total Reads: 2
Read data: It was a boat. A small boat.
████████████████████████████████████████████████████████████████████████
291.Additional Buffered Reader Methods
The NewReader and NewReaderSize functions return bufio.Reader values, which implement the io.
Reader interface and which can be used as drop-in wrappers for other types of Reader methods, seamlessly
introducing a read buffer.
████████████████████████████████████████████████████████████████████████
292.The Methods Defined by the Buffered Reader
Name Description
-------------- ------------------------------------------
Buffered() This method returns an int that indicates the number of bytes that can be read from the buffer.
Discard(count) This method discards the specified number of bytes.
Peek(count) This method returns the specified number of bytes without removing them from the
buffer, meaning they will be returned by subsequent calls to the Read method.
Reset(reader) This method discards the data in the buffer and performs subsequent reads from the
specified Reader.
Size() This method returns the size of the buffer, expressed int.
████████████████████████████████████████████████████████████████████████
293.buffered.Read(slice)
example:
package main
import (
"io"
"strings"
"bufio"
)
func main() {
text := "It was a boat. A small boat."
var reader io.Reader = NewCustomReader(strings.NewReader(text))
var writer strings.Builder
slice := make([]byte, 5)
buffered := bufio.NewReader(reader)
for {
count, err := buffered.Read(slice)
if (count > 0) {
Printfln("Buffer size: %v, buffered: %v",
buffered.Size(), buffered.Buffered())
writer.Write(slice[0:count])
}
if (err != nil) {
break
}
}
Printfln("Read data: %v", writer.String())
}
Output:
Custom Reader: 28 bytes
Buffer size: 4096, buffered: 23
Buffer size: 4096, buffered: 18
Buffer size: 4096, buffered: 13
Buffer size: 4096, buffered: 8
Buffer size: 4096, buffered: 3
Buffer size: 4096, buffered: 0
Custom Reader: 0 bytes
Total Reads: 2
Read data: It was a boat. A small boat.
████████████████████████████████████████████████████████████████████████
294.bufio Functions for Creating Buffered Writers
Name Description
----------------------- --------------------------------------
NewWriter(w) This function returns a buffered Writer with the default buffer size (which is
4,096 bytes at the time of writing).
NewWriterSize(w, size) This function returns a buffered Writer with the specified buffer size.
████████████████████████████████████████████████████████████████████████
295.Methods Defined by the bufio.Writer Struct
Name Description
-------------- -----------------------------------
Available() This method returns the number of available bytes in the buffer.
Buffered() This method returns the number of bytes that have been written to the buffer.
Flush() This method writes the contents of the buffer to the underlying Writer.
Reset(writer) This method discards the data in the buffer and performs subsequent writes to the
specified Writer.
Size() This method returns the capacity of the buffer in bytes.
████████████████████████████████████████████████████████████████████████
296.Defining a Custom Writer example
The NewCustomWriter constructor wraps a Writer with a CustomWriter struct, which reports on its
write operations.
custom.go:
package main
import (
"asd/asd"
"io"
)
type CustomReader struct {
reader io.Reader
readCount int
}
func NewCustomReader(reader io.Reader) *CustomReader {
return &CustomReader{reader, 0}
}
func (cr *CustomReader) Read(slice []byte) (count int, err error) {
count, err = cr.reader.Read(slice)
cr.readCount++
asd.Printfln("Custom Reader: %v bytes", count)
if err == io.EOF {
asd.Printfln("Total Reads: %v", cr.readCount)
}
return
}
type CustomWriter struct {
writer io.Writer
writeCount int
}
func NewCustomWriter(writer io.Writer) *CustomWriter {
return &CustomWriter{writer, 0}
}
func (cw *CustomWriter) Write(slice []byte) (count int, err error) {
count, err = cw.writer.Write(slice)
cw.writeCount++
asd.Printfln("Custom Writer: %v bytes", count)
return
}
func (cw *CustomWriter) Close() (err error) {
if closer, ok := cw.writer.(io.Closer); ok {
closer.Close()
}
asd.Printfln("Total Writes: %v", cw.writeCount)
return
}
main.go:
package main
import (
"strings"
"asd/asd"
)
func main() {
text := "It was a boat. A small boat."
var builder strings.Builder
var writer = NewCustomWriter(&builder)
for i := 0; true; {
end := i + 5
if end >= len(text) {
writer.Write([]byte(text[i:]))
break
}
writer.Write([]byte(text[i:end]))
i = end
}
asd.Printfln("Written data: %v", builder.String())
}
Output:
Custom Writer: 5 bytes
Custom Writer: 5 bytes
Custom Writer: 5 bytes
Custom Writer: 5 bytes
Custom Writer: 5 bytes
Custom Writer: 3 bytes
Written data: It was a boat. A small boat.
████████████████████████████████████████████████████████████████████████
297.Using a Buffered Writer in the main.go example
example:
main.go:
package main
import (
"strings"
"asd/asd"
"bufio"
)
func main() {
text := "It was a boat. A small boat."
var builder strings.Builder
var writer = bufio.NewWriterSize(NewCustomWriter(&builder), 20)
for i := 0; true; {
end := i + 5
if end >= len(text) {
writer.Write([]byte(text[i:]))
writer.Flush()
break
}
writer.Write([]byte(text[i:end]))
i = end
}
asd.Printfln("Written data: %v", builder.String())
}
Output:
Custom Writer: 20 bytes
Custom Writer: 8 bytes
Written data: It was a boat. A small boat.
████████████████████████████████████████████████████████████████████████
298.Scanning from a Reader
example:
main.go
package main
import (
"io"
"strings"
"asd/asd"
"fmt"
)
func scanFromReader(reader io.Reader, template string,
vals ...interface{}) (int, error) {
return fmt.Fscanf(reader, template, vals...)
}
func main() {
reader := strings.NewReader("Kayak Watersports $279.00")
var name, category string
var price float64
scanTemplate := "%s %s $%f"
_, err := scanFromReader(reader, scanTemplate, &name, &category, &price)
if err != nil {
asd.Printfln("Error: %v", err.Error())
} else {
asd.Printfln("Name: %v", name)
asd.Printfln("Category: %v", category)
asd.Printfln("Price: %.2f", price)
}
}
Output:
Name: Kayak
Category: Watersports
Price: 279.00
████████████████████████████████████████████████████████████████████████
299.Scanning Gradually اسکن به تدریج
example:
main.go
package main
import (
"io"
"strings"
"asd/asd"
"fmt"
)
func scanSingle(reader io.Reader, val interface{}) (int, error) {
return fmt.Fscan(reader, val)
}
func main() {
reader := strings.NewReader("Kayak Watersports $279.00")
for {
var str string
_, err := scanSingle(reader, &str)
if err != nil {
if err != io.EOF {
asd.Printfln("Error: %v", err.Error())
}
break
}
asd.Printfln("Value: %v", str)
}
}
Output:
Value: Kayak
Value: Watersports
Value: $279.00
████████████████████████████████████████████████████████████████████████
300.Writing Formatted Strings to a Writer
The fmt package also provides functions for writing formatted strings to a Writer
The writeFormatted function uses the fmt.Fprintf function to write a string formatted with a template
to a Writer.
example:
main.go:
package main
import (
"io"
"strings"
"fmt"
)
func writeFormatted(writer io.Writer, template string, vals ...interface{}) {
fmt.Fprintf(writer, template, vals...)
}
func main() {
var writer strings.Builder
template := "Name: %s, Category: %s, Price: $%.2f"
writeFormatted(&writer, template, "Kayak", "Watersports", float64(279))
fmt.Println(writer.String())
}
Output:
Name: Kayak, Category: Watersports, Price: $279.00
████████████████████████████████████████████████████████████████████████
301.strings.Replacer struct
The strings.Replacer struct can be used to perform replacements on a string and output the modified
result to a Writer.
example:
package main
import (
"fmt"
"io"
"strings"
)
func writeReplaced(writer io.Writer, str string, subs ...string) {
replacer := strings.NewReplacer(subs...)
replacer.WriteString(writer, str)
}
func main() {
text := "It was a boat. A small boat."
subs := []string{"boat", "kayak", "small", "huge"}
var writer strings.Builder
writeReplaced(&writer, text, subs...)
fmt.Println(writer.String())
}
Output:
It was a kayak. A huge kayak.
`,
}
View Source
var OriginalSingleDefExamples = SingleDefinitionExamples{ MapSingleDefEx: map[string]string{ "for": `56.for ================= example-1 ------------ main.go package main import ( "fmt" ) func main() { counter := "EXAMPLE" // Enumerating Sequences: for index, character := range counter { fmt.Println("Index:", index, "Character:", string(character)) } } =========================== in Terminal: go run . =========================== Output: Index: 0 Character: E Index: 1 Character: X Index: 2 Character: A Index: 3 Character: M Index: 4 Character: P Index: 5 Character: L Index: 6 Character: E`, "http servers": `403.Creating HTTP Servers ================= example-1 ------------ 1- go mod init httpserver 2- printer.go: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } 3- product.go: package main type Product struct { Name, Category string Price float64 var Products = []Product{ {"Kayak", "Watersports", 279}, {"Lifejacket", "Watersports", 49.95}, {"Soccer Ball", "Soccer", 19.50}, {"Corner Flags", "Soccer", 34.95}, {"Stadium", "Soccer", 79500}, {"Thinking Cap", "Chess", 16}, {"Unsteady Chair", "Chess", 75}, {"Bling-Bling King", "Chess", 1200}, } 4- in -> 404.Creating a Simple HTTP Server main.go: package main import ( "net/http" "io" ) type StringHandler struct { message string } func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { io.WriteString(writer, sh.message) } func main() { err := http.ListenAndServe(":5000", StringHandler{ message: "Hello, World"}) if (err != nil) { Printfln("Error: %v", err.Error()) } } ================================= Output: go run . in Web Browser, search localhost:5000/ Hello, World `, "simple server": `403.Creating HTTP Servers ================= example-1 ------------ 1- go mod init httpserver 2- printer.go: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } 3- product.go: package main type Product struct { Name, Category string Price float64 var Products = []Product{ {"Kayak", "Watersports", 279}, {"Lifejacket", "Watersports", 49.95}, {"Soccer Ball", "Soccer", 19.50}, {"Corner Flags", "Soccer", 34.95}, {"Stadium", "Soccer", 79500}, {"Thinking Cap", "Chess", 16}, {"Unsteady Chair", "Chess", 75}, {"Bling-Bling King", "Chess", 1200}, } 4- in -> 404.Creating a Simple HTTP Server main.go: package main import ( "net/http" "io" ) type StringHandler struct { message string } func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { io.WriteString(writer, sh.message) } func main() { err := http.ListenAndServe(":5000", StringHandler{ message: "Hello, World"}) if (err != nil) { Printfln("Error: %v", err.Error()) } } ================================= Output: go run . in Web Browser, search localhost:5000/ Hello, World `, "web application": ` main.go: package main import ( "fmt" "net/http" ) func handlerFunction(w http.ResponseWriter, r *http.Request){ fmt.Fprint(w, "<h1>Welcome to AcronProject Site</h1>") } func main() { http.HandleFunc("/", handlerFunction) fmt.Println("Starting the Web Server on http://locanhost:3000") http.ListenAndServe(":3000", nil) } ======================================== Output: go run main.go in Web Browser, search localhost:3000 Welcome to AcronProject Site `, "simple web application": `main.go: package main import ( "fmt" "net/http" ) func handlerFunction(w http.ResponseWriter, r *http.Request){ fmt.Fprint(w, "<h1>Welcome to AcronProject Site</h1>") } func main() { http.HandleFunc("/", handlerFunction) fmt.Println("Starting the Web Server on http://locanhost:3000") http.ListenAndServe(":3000", nil) } ======================================== Output: go run main.go in Web Browser, search localhost:3000 Welcome to AcronProject Site `, "simple web app": `main.go: package main import ( "fmt" "net/http" ) func handlerFunction(w http.ResponseWriter, r *http.Request){ fmt.Fprint(w, "<h1>Welcome to AcronProject Site</h1>") } func main() { http.HandleFunc("/", handlerFunction) fmt.Println("Starting the Web Server on http://locanhost:3000") http.ListenAndServe(":3000", nil) } ======================================== Output: go run main.go in Web Browser, search localhost:3000 Welcome to AcronProject Site `, "bool": `Boolean var closed bool // boolean variable 'closed' implicitly initialized with 'false' speeding := true // boolean variable 'speeding' initialized with 'true' hasError := false // boolean variable 'hasError' initialized with 'false' `, "boolean": `Boolean var closed bool // boolean variable 'closed' implicitly initialized with 'false' speeding := true // boolean variable 'speeding' initialized with 'true' hasError := false // boolean variable 'hasError' initialized with 'false' `, "booleans": `Boolean var closed bool // boolean variable 'closed' implicitly initialized with 'false' speeding := true // boolean variable 'speeding' initialized with 'true' hasError := false // boolean variable 'hasError' initialized with 'false' `, "package comments": `kelvin.go // Package kelvin provides tools to convert // temperatures to and from Kelvin. package kelvin `, "package comment": `kelvin.go // Package kelvin provides tools to convert // temperatures to and from Kelvin. package kelvin`, "function comment": `example: // CelsiusFreezingTemp returns an integer value equal to the temperature at which water freezes in degrees Celsius. func CelsiusFreezingTemp() int { return 0 }`, "function comments": `example: // CelsiusFreezingTemp returns an integer value equal to the temperature at which water freezes in degrees Celsius. func CelsiusFreezingTemp() int { return 0 }`, "operator": `Operator Example -------- ------------- + 4 + 6 == 10 - 15 - 10 == 5 * 2 * 3 == 6 / 13 / 3 == 4 % 13 % 3 == 1`, "operators": `Operator Example -------- ------------- + 4 + 6 == 10 - 15 - 10 == 5 * 2 * 3 == 6 / 13 / 3 == 4 % 13 % 3 == 1`, "arithmetic operators": `Operator Example -------- ------------- + 4 + 6 == 10 - 15 - 10 == 5 * 2 * 3 == 6 / 13 / 3 == 4 % 13 % 3 == 1`, "converting between types": `var x int = 42 // x has type int f := float64(x) // f has type float64 (ie. 42.0) var y float64 = 11.9 // y has type float64 i := int(y) // i has type int (ie. 11)`, "arithmetic operations on different types": `var x int = 42 // this line produces an error value := float32(2.0) * x // invalid operation: mismatched types float32 and int // you must convert int type to float32 before performing arithmetic operation value := float32(2.0) * float32(x)`, "arithmetic operations on different type": `var x int = 42 // this line produces an error value := float32(2.0) * x // invalid operation: mismatched types float32 and int // you must convert int type to float32 before performing arithmetic operation value := float32(2.0) * float32(x)`, "godoc": `An Example Program With Godoc Comments The code below adheres to the Go way, in this case using single-line comments. =================================== `, "string": `A string literal is defined between double quotes: const name = "Jane" ================================================== Strings can be concatenated via the + operator: "Jane" + " " + "Austen" // => "Jane Austen" ================================================== `, "beego": `Quick Start Create hello directory, cd hello directory mkdir hello cd hello Init module go mod init Download and install go get github.com/beego/beego Create file hello.go package main import "github.com/beego/beego" func main(){ beego.Run() } Build and run go build hello.go ./hello `, "beego1": `beego-2 this is first example, you can write => beggo or beego1 or beego2 and more for another examples: Quick Start Create hello directory, cd hello directory mkdir hello cd hello Init module go mod init Download and install go get github.com/beego/beego Create file hello.go package main import "github.com/beego/beego" func main(){ beego.Run() } Build and run go build hello.go ./hello `, "beego framework": `Quick Start Create hello directory, cd hello directory mkdir hello cd hello Init module go mod init Download and install go get github.com/beego/beego Create file hello.go package main import "github.com/beego/beego" func main(){ beego.Run() } Build and run go build hello.go ./hello `, "generics": `main.go: package main import "fmt" type CustomMap[T comparable, V int | string] map[T]V func main() { Printfln("Hello, Data") obj := make(CustomMap[int, string]) obj[3] = "3" obj[1] = "1" fmt.Println(obj) }`, "generic": `main.go: package main import "fmt" type CustomMap[T comparable, V int | string] map[T]V func main() { Printfln("Hello, Data") obj := make(CustomMap[int, string]) obj[3] = "3" obj[1] = "1" fmt.Println(obj) }`, "reflection": `example: package main func printDetails(values ...interface{}) { for _, elem := range values { switch val := elem.(type) { case Product: Printfln("Product: Name: %v, Category: %v, Price: %v", val.Name, val.Category, val.Price) case Customer: Printfln("Customer: Name: %v, City: %v", val.Name, val.City) } } } func main() { product := Product { Name: "Kayak", Category: "Watersports", Price: 279, } customer := Customer { Name: "Alice", City: "New York" } printDetails(product, customer) } ==================================================================== Output: Product: Name: Kayak, Category: Watersports, Price: 279 Customer: Name: Alice, City: New York`, "reflection ": `example: package main func printDetails(values ...interface{}) { for _, elem := range values { switch val := elem.(type) { case Product: Printfln("Product: Name: %v, Category: %v, Price: %v", val.Name, val.Category, val.Price) case Customer: Printfln("Customer: Name: %v, City: %v", val.Name, val.City) } } } func main() { product := Product { Name: "Kayak", Category: "Watersports", Price: 279, } customer := Customer { Name: "Alice", City: "New York" } printDetails(product, customer) } ==================================================================== Output: Product: Name: Kayak, Category: Watersports, Price: 279 Customer: Name: Alice, City: New York`, "reflection 1": `example: package main func printDetails(values ...interface{}) { for _, elem := range values { switch val := elem.(type) { case Product: Printfln("Product: Name: %v, Category: %v, Price: %v", val.Name, val.Category, val.Price) case Customer: Printfln("Customer: Name: %v, City: %v", val.Name, val.City) } } } func main() { product := Product { Name: "Kayak", Category: "Watersports", Price: 279, } customer := Customer { Name: "Alice", City: "New York" } printDetails(product, customer) } ==================================================================== Output: Product: Name: Kayak, Category: Watersports, Price: 279 Customer: Name: Alice, City: New York`, "reflection1": `example: package main func printDetails(values ...interface{}) { for _, elem := range values { switch val := elem.(type) { case Product: Printfln("Product: Name: %v, Category: %v, Price: %v", val.Name, val.Category, val.Price) case Customer: Printfln("Customer: Name: %v, City: %v", val.Name, val.City) } } } func main() { product := Product { Name: "Kayak", Category: "Watersports", Price: 279, } customer := Customer { Name: "Alice", City: "New York" } printDetails(product, customer) } ==================================================================== Output: Product: Name: Kayak, Category: Watersports, Price: 279 Customer: Name: Alice, City: New York`, "server": `server1 = you can use server2 example, server3 or more main.go: package main import ( "net/http" "os" ) func main() { dir, _ := os.Getwd() http.ListenAndServe(":3000", http.FileServer(http.Dir(dir))) } `, "server1": `server1 = you can use server2 example, server3 or more main.go: package main import ( "net/http" "os" ) func main() { dir, _ := os.Getwd() http.ListenAndServe(":3000", http.FileServer(http.Dir(dir))) } `, "beego2": `beego-2 this is first example, you can write => beggo or beego1 or beego2 and more for another examples: ========================================================================= main.go: package main import ( "github.com/beego/beego/v2/server/web" ) func main() { web.Run() } ======================================================================== go.mod: module github.com/sinalalebakhsh/WebApplication1 go 1.21.3 require github.com/beego/beego/v2 v2.1.3 require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/kr/text v0.2.0 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.16.0 // indirect github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 // indirect golang.org/x/crypto v0.10.0 // indirect golang.org/x/net v0.10.0 // indirect golang.org/x/sys v0.9.0 // indirect golang.org/x/text v0.10.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) ========================================================================= go.sum: github.com/beego/beego/v2 v2.1.3 h1:x436yz6jrSasYBzfOP39S097kvq5/5fBTFfEvVA456M= github.com/beego/beego/v2 v2.1.3/go.mod h1:0J0RQVIpepnRUfu6ax+kLVVB1FcdYryHK9lpRl5wvbY= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw= github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 h1:DAYUYH5869yV94zvCES9F51oYtN5oGlwjxJJz7ZCnik= github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= golang.org/x/crypto v0.10.0 h1:LKqV2xt9+kDzSTfOhx4FrkEBcMrAgHSYgzywV9zcGmM= golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.9.0 h1:KS/R3tvhPqvJvwcKfnBHJwwthS11LRhmM5D59eEXa0s= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/text v0.10.0 h1:UpjohKhiEgNc0CSauXmwYftY1+LlaC75SJwh0SgCX58= golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= ========================================================================= Output: in broswer search: localhost:8080 + Enter the key You will see that ========================================================================== /* 1- Write in Terminal: go mod tidy 2- Output: go: finding module for package github.com/beego/beego/v2/server/web go: downloading github.com/beego/beego/v2 v2.1.3 go: found github.com/beego/beego/v2/server/web in github.com/beego/beego/v2 v2.1.3 go: downloading github.com/prometheus/client_golang v1.16.0 go: downloading golang.org/x/crypto v0.10.0 go: downloading google.golang.org/protobuf v1.30.0 go: downloading github.com/elazarl/go-bindata-assetfs v1.0.1 go: downloading github.com/stretchr/testify v1.8.1 go: downloading github.com/pkg/errors v0.9.1 go: downloading github.com/mitchellh/mapstructure v1.5.0 go: downloading github.com/shiena/ansicolor v0.0.0-20200904210342-c7312218db18 go: downloading github.com/prometheus/client_model v0.3.0 go: downloading github.com/prometheus/common v0.42.0 go: downloading github.com/cespare/xxhash/v2 v2.2.0 go: downloading github.com/prometheus/procfs v0.10.1 go: downloading golang.org/x/net v0.10.0 go: downloading github.com/golang/protobuf v1.5.3 go: downloading github.com/matttproud/golang_protobuf_extensions v1.0.4 go: downloading golang.org/x/text v0.10.0 go: downloading gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c go: downloading github.com/kr/pretty v0.3.1 go: downloading github.com/rogpeppe/go-internal v1.10.0 go: finding module for package github.com/kr/text go: downloading github.com/kr/text v0.2.0 go: found github.com/kr/text in github.com/kr/text v0.2.0 */ /* 1- After that, write: go run . 2- Output in Terminal is: 2023/11/11 08:10:26.835 [D] init global config instance failed. If you do not use this, just ignore it. open conf/app.conf: no such file or directory 2023/11/11 08:10:26.839 [I] http server Running on http://:8080 3- write in browser: localhost:8080 4- Output: NOT Found blue text with another information. */ `, "goal or none sense game": `game-1 Goal Or None Sense Game main.go: package main import ( "fmt" "math/rand" "time" ) func main() { rand.Seed(time.Now().UnixNano()) var cups, chances int fmt.Print("How many cups? ") fmt.Scan(&cups) fmt.Print("How many chances? ") fmt.Scan(&chances) aiGoal := rand.Intn(cups) + 1 fmt.Println("----------") var userGuess int for i := 0; i < chances; i++ { fmt.Printf("%d chances left.\n", chances-i) fmt.Print("Guess [1-", cups, "]: ") fmt.Scan(&userGuess) if userGuess == aiGoal { fmt.Println("You guessed right.") break } else { fmt.Println("🥶 🫨 🥶 🫨") fmt.Println("🥶 🫥 🥶 🫨 🫨") fmt.Println("----------") } } if userGuess == aiGoal { fmt.Println("==============") fmt.Println("💓 😍 💓 😍 💓 😍 💓 😍💗💗💗") } else { fmt.Printf("The right answer is %d.\n", aiGoal) fmt.Println("You lost. Sorry!") } } `, "game": `game-1 Goal Or None Sense Game main.go: package main import ( "fmt" "math/rand" "time" ) func main() { rand.Seed(time.Now().UnixNano()) var cups, chances int fmt.Print("How many cups? ") fmt.Scan(&cups) fmt.Print("How many chances? ") fmt.Scan(&chances) aiGoal := rand.Intn(cups) + 1 fmt.Println("----------") var userGuess int for i := 0; i < chances; i++ { fmt.Printf("%d chances left.\n", chances-i) fmt.Print("Guess [1-", cups, "]: ") fmt.Scan(&userGuess) if userGuess == aiGoal { fmt.Println("You guessed right.") break } else { fmt.Println("🥶 🫨 🥶 🫨") fmt.Println("🥶 🫥 🥶 🫨 🫨") fmt.Println("----------") } } if userGuess == aiGoal { fmt.Println("==============") fmt.Println("💓 😍 💓 😍 💓 😍 💓 😍💗💗💗") } else { fmt.Printf("The right answer is %d.\n", aiGoal) fmt.Println("You lost. Sorry!") } } `, "game1": `game-1 Goal Or None Sense Game main.go: package main import ( "fmt" "math/rand" "time" ) func main() { rand.Seed(time.Now().UnixNano()) var cups, chances int fmt.Print("How many cups? ") fmt.Scan(&cups) fmt.Print("How many chances? ") fmt.Scan(&chances) aiGoal := rand.Intn(cups) + 1 fmt.Println("----------") var userGuess int for i := 0; i < chances; i++ { fmt.Printf("%d chances left.\n", chances-i) fmt.Print("Guess [1-", cups, "]: ") fmt.Scan(&userGuess) if userGuess == aiGoal { fmt.Println("You guessed right.") break } else { fmt.Println("🥶 🫨 🥶 🫨") fmt.Println("🥶 🫥 🥶 🫨 🫨") fmt.Println("----------") } } if userGuess == aiGoal { fmt.Println("==============") fmt.Println("💓 😍 💓 😍 💓 😍 💓 😍💗💗💗") } else { fmt.Printf("The right answer is %d.\n", aiGoal) fmt.Println("You lost. Sorry!") } } `, "game goal or none sense": `game-1 Goal Or None Sense Game main.go: package main import ( "fmt" "math/rand" "time" ) func main() { rand.Seed(time.Now().UnixNano()) var cups, chances int fmt.Print("How many cups? ") fmt.Scan(&cups) fmt.Print("How many chances? ") fmt.Scan(&chances) aiGoal := rand.Intn(cups) + 1 fmt.Println("----------") var userGuess int for i := 0; i < chances; i++ { fmt.Printf("%d chances left.\n", chances-i) fmt.Print("Guess [1-", cups, "]: ") fmt.Scan(&userGuess) if userGuess == aiGoal { fmt.Println("You guessed right.") break } else { fmt.Println("🥶 🫨 🥶 🫨") fmt.Println("🥶 🫥 🥶 🫨 🫨") fmt.Println("----------") } } if userGuess == aiGoal { fmt.Println("==============") fmt.Println("💓 😍 💓 😍 💓 😍 💓 😍💗💗💗") } else { fmt.Printf("The right answer is %d.\n", aiGoal) fmt.Println("You lost. Sorry!") } } `, "goal or none sense": `game-1 Goal Or None Sense Game main.go: package main import ( "fmt" "math/rand" "time" ) func main() { rand.Seed(time.Now().UnixNano()) var cups, chances int fmt.Print("How many cups? ") fmt.Scan(&cups) fmt.Print("How many chances? ") fmt.Scan(&chances) aiGoal := rand.Intn(cups) + 1 fmt.Println("----------") var userGuess int for i := 0; i < chances; i++ { fmt.Printf("%d chances left.\n", chances-i) fmt.Print("Guess [1-", cups, "]: ") fmt.Scan(&userGuess) if userGuess == aiGoal { fmt.Println("You guessed right.") break } else { fmt.Println("🥶 🫨 🥶 🫨") fmt.Println("🥶 🫥 🥶 🫨 🫨") fmt.Println("----------") } } if userGuess == aiGoal { fmt.Println("==============") fmt.Println("💓 😍 💓 😍 💓 😍 💓 😍💗💗💗") } else { fmt.Printf("The right answer is %d.\n", aiGoal) fmt.Println("You lost. Sorry!") } } `, "encryption": `encryption1 simple encryption main.go: package main import "fmt" func main() { userInput := "sina" var Encryption string for _, char := range userInput { Encryption += fmt.Sprintf("%d ", char) } fmt.Println(Encryption) } `, "encryption1": `encryption1 simple encryption main.go: package main import "fmt" func main() { userInput := "sina" var Encryption string for _, char := range userInput { Encryption += fmt.Sprintf("%d ", char) } fmt.Println(Encryption) } `, "simple encryption": `encryption1 simple encryption main.go: package main import "fmt" func main() { userInput := "sina" var Encryption string for _, char := range userInput { Encryption += fmt.Sprintf("%d ", char) } fmt.Println(Encryption) } `, "first encryption": `encryption1 simple encryption main.go: package main import "fmt" func main() { userInput := "sina" var Encryption string for _, char := range userInput { Encryption += fmt.Sprintf("%d ", char) } fmt.Println(Encryption) } `, "go tool dist list": ` 488🚀 This will provide you a list of operating systems and architectures separated by / characters: Output: aix/ppc64 android/386 android/amd64 android/arm android/arm64 darwin/amd64 darwin/arm64 dragonfly/amd64 freebsd/386 freebsd/amd64 freebsd/arm freebsd/arm64 freebsd/riscv64 illumos/amd64 ios/amd64 ios/arm64 js/wasm linux/386 linux/amd64 linux/arm linux/arm64 linux/loong64 linux/mips linux/mips64 linux/mips64le linux/mipsle linux/ppc64 linux/ppc64le linux/riscv64 linux/s390x netbsd/386 netbsd/amd64 netbsd/arm netbsd/arm64 openbsd/386 openbsd/amd64 openbsd/arm openbsd/arm64 plan9/386 plan9/amd64 plan9/arm solaris/amd64 wasip1/wasm windows/386 windows/amd64 windows/arm windows/arm64`, "tool dist list": ` 488🚀 This will provide you a list of operating systems and architectures separated by / characters: Output: aix/ppc64 android/386 android/amd64 android/arm android/arm64 darwin/amd64 darwin/arm64 dragonfly/amd64 freebsd/386 freebsd/amd64 freebsd/arm freebsd/arm64 freebsd/riscv64 illumos/amd64 ios/amd64 ios/arm64 js/wasm linux/386 linux/amd64 linux/arm linux/arm64 linux/loong64 linux/mips linux/mips64 linux/mips64le linux/mipsle linux/ppc64 linux/ppc64le linux/riscv64 linux/s390x netbsd/386 netbsd/amd64 netbsd/arm netbsd/arm64 openbsd/386 openbsd/amd64 openbsd/arm openbsd/arm64 plan9/386 plan9/amd64 plan9/arm solaris/amd64 wasip1/wasm windows/386 windows/amd64 windows/arm windows/arm64`, "dist list": ` 488🚀 This will provide you a list of operating systems and architectures separated by / characters: Output: aix/ppc64 android/386 android/amd64 android/arm android/arm64 darwin/amd64 darwin/arm64 dragonfly/amd64 freebsd/386 freebsd/amd64 freebsd/arm freebsd/arm64 freebsd/riscv64 illumos/amd64 ios/amd64 ios/arm64 js/wasm linux/386 linux/amd64 linux/arm linux/arm64 linux/loong64 linux/mips linux/mips64 linux/mips64le linux/mipsle linux/ppc64 linux/ppc64le linux/riscv64 linux/s390x netbsd/386 netbsd/amd64 netbsd/arm netbsd/arm64 openbsd/386 openbsd/amd64 openbsd/arm openbsd/arm64 plan9/386 plan9/amd64 plan9/arm solaris/amd64 wasip1/wasm windows/386 windows/amd64 windows/arm windows/arm64`, }, }
View Source
var OriginalSingleDefFunctions = FunctionsDefinitions{ MapSingleDefFuncs: map[string]string{ "atoi()": "47🚀 Atoi(str) 🔔 This function parses a string into a base 10 int and is equivalent to calling ParseInt(str, 10, 0)", "atoi(str)": "47🚀 Atoi(str) 🔔 This function parses a string into a base 10 int and is equivalent to calling ParseInt(str, 10, 0)", "atoi(string)": "47🚀 Atoi(str) 🔔 This function parses a string into a base 10 int and is equivalent to calling ParseInt(str, 10, 0)", "formatint()": "49🚀 FormatInt(value, base) 🔔 This function returns a string representation of the specified int64 value, expressed in the specified base.", "formatint(value, base)": "49🚀 FormatInt(value, base) 🔔 This function returns a string representation of the specified int64 value, expressed in the specified base.", "formatint(val, base)": "49🚀 FormatInt(value, base) 🔔 This function returns a string representation of the specified int64 value, expressed in the specified base.", "formatuint()": "50🚀 FormatUint(val, base) 🔔 This function returns a string representation of the specified uint64 value, expressed in the specified base.", "formatuint(val, base)": "50🚀 FormatUint(val, base) 🔔 This function returns a string representation of the specified uint64 value, expressed in the specified base.", "formatfloat()": "51🚀 FormatFloat(val, format, precision, size) 🔔 This function returns a string representation of the specified float64 value, expressed using the specified format, precision, and size.", "formatfloat(1,2,3,4)": "51🚀 FormatFloat(val, format, precision, size) 🔔This function returns a string representation of the specified float64 value, expressed using the specified format, precision, and size.", "formatfloat(val, format, precision, size)": "51🚀 FormatFloat(val, format, precision, size) 🔔This function returns a string representation of the specified float64 value, expressed using the specified format, precision, and size.", "itoa()": "52🚀 Itoa(val) 🔔 This function returns a string representation of the specified int value, expressed using base 10.", "itoa(val)": "52🚀 Itoa(val) 🔔 This function returns a string representation of the specified int value, expressed using base 10.", "itoa(value)": "52🚀 Itoa(val) 🔔 This function returns a string representation of the specified int value, expressed using base 10.", "islower(rune)": "165🚀 IsLower(rune) 🔔 This function returns true if the specified rune is lowercase.", "islower()": "165🚀 IsLower(rune) 🔔 This function returns true if the specified rune is lowercase.", "islower(r)": "165🚀 IsLower(rune) 🔔 This function returns true if the specified rune is lowercase.", "islower(runes)": "165🚀 IsLower(rune) 🔔 This function returns true if the specified rune is lowercase.", "tolower(rune)": "165🚀 ToLower(rune) 🔔 This function returns the lowercase rune associated with the specified rune.", "tolower()": "165🚀 ToLower(rune) 🔔 This function returns the lowercase rune associated with the specified rune.", "tolower(r)": "165🚀 ToLower(rune) 🔔 This function returns the lowercase rune associated with the specified rune.", "tolower(runes)": "165🚀 ToLower(rune) 🔔 This function returns the lowercase rune associated with the specified rune.", "isupper(rune)": "165🚀 IsUpper(rune) 🔔 This function returns true if the specified rune is uppercase.", "isupper(r)": "165🚀 IsUpper(rune) 🔔 This function returns true if the specified rune is uppercase.", "isupper(runes)": "165🚀 IsUpper(rune) 🔔 This function returns true if the specified rune is uppercase.", "isupper()": "165🚀 IsUpper(rune) 🔔 This function returns true if the specified rune is uppercase.", "toupper(rune)": "165🚀 ToUpper(rune) 🔔 This function returns the upper rune associated with the specified rune.", "toupper()": "165🚀 ToUpper(rune) 🔔 This function returns the upper rune associated with the specified rune.", "toupper(r)": "165🚀 ToUpper(rune) 🔔 This function returns the upper rune associated with the specified rune.", "toupper(runes)": "165🚀 ToUpper(rune) 🔔 This function returns the upper rune associated with the specified rune.", "istitle(rune)": "165🚀 IsTitle(rune) 🔔 This function returns true if the specified rune is title case.", "istitle()": "165🚀 IsTitle(rune) 🔔 This function returns true if the specified rune is title case.", "istitle(r)": "165🚀 IsTitle(rune) 🔔 This function returns true if the specified rune is title case.", "istitle(runes)": "165🚀 IsTitle(rune) 🔔 This function returns true if the specified rune is title case.", "totitle(rune)": "165🚀 ToTitle(rune) 🔔 This function returns the title case rune associated with the specified rune.", "totitle()": "165🚀 ToTitle(rune) 🔔 This function returns the title case rune associated with the specified rune.", "totitle(r)": "165🚀 ToTitle(rune) 🔔 This function returns the title case rune associated with the specified rune.", "totitle(runes)": "165🚀 ToTitle(rune) 🔔 This function returns the title case rune associated with the specified rune.", "count(s, sub)": "167🚀 Count(s, sub) 🔔 This function returns an int that reports how many times the specified substring is found in the string s.", "count()": "167🚀 Count(s, sub) 🔔 This function returns an int that reports how many times the specified substring is found in the string s.", "count(string, sub)": "167🚀 Count(s, sub) 🔔 This function returns an int that reports how many times the specified substring is found in the string s.", "index(s, sub)": "167🚀 Index(s, sub) 🔔 These functions return the index of the first or last occurrence of a specified", "index()": "167🚀 Index(s, sub) 🔔 These functions return the index of the first or last occurrence of a specified", "index(string, sub)": "167🚀 Index(s, sub) 🔔 These functions return the index of the first or last occurrence of a specified", "indexany(s, chars)": "167🚀 IndexAny(s, chars) 🔔 These functions return the first or last occurrence of any character in the...", "indexany()": "167🚀 IndexAny(s, chars) 🔔 These functions return the first or last occurrence of any character in the...", "indexany(string, characters)": "167🚀 IndexAny(s, chars) 🔔 These functions return the first or last occurrence of any character in the...", "lastindexAny(s, chars)": "167🚀 LastIndexAny(s, chars) 🔔 specified string within the string s, or -1 if there is no occurrence.", "lastindexAny()": "167🚀 LastIndexAny(s, chars) 🔔 specified string within the string s, or -1 if there is no occurrence.", "lastindexAny(string, characters)": "167🚀 LastIndexAny(s, chars) 🔔 specified string within the string s, or -1 if there is no occurrence.", "indexbyte(s, b)": "167🚀 IndexByte(s, b) 🔔 These functions return the index of the first or last occurrence of a specified", "indexbyte()": "167🚀 IndexByte(s, b) 🔔 These functions return the index of the first or last occurrence of a specified", "indexbyte(string, byte)": "167🚀 IndexByte(s, b) 🔔 These functions return the index of the first or last occurrence of a specified", "lastindexByte(s, b)": "167🚀 LastIndexByte(s, b) 🔔 byte within the string s, or -1 if there is no occurrence.", "lastindexByte()": "167🚀 LastIndexByte(s, b) 🔔 byte within the string s, or -1 if there is no occurrence.", "lastindexByte(string, byte)": "167🚀 LastIndexByte(s, b) 🔔 byte within the string s, or -1 if there is no occurrence.", "indexfunc(s, func)": "167🚀 IndexFunc(s, func) 🔔 These functions return the index of the first or last occurrence of the...", "indexfunc()": "167🚀 IndexFunc(s, func) 🔔 These functions return the index of the first or last occurrence of the...", "indexfunc(string, function)": "167🚀 IndexFunc(s, func) 🔔 These functions return the index of the first or last occurrence of the...", "lastindexFunc(s, func)": "167🚀 LastIndexFunc(s, func) 🔔 character in the string s for which the specified function returns true, as described in the “Inspecting Strings with Custom Functions” section.", "lastindexFunc()": "167🚀 LastIndexFunc(s, func) 🔔 character in the string s for which the specified function returns true, as described in the “Inspecting Strings with Custom Functions” section.", "lastindexFunc(string, function)": "167🚀 LastIndexFunc(s, func) 🔔 character in the string s for which the specified function returns true, as described in the “Inspecting Strings with Custom Functions” section.", "contains(s, substr)": "161🚀 Contains(s, substr) 🔔 This function returns true if the string s contains substr and false if it does not.", "contains()": "161🚀 Contains(s, substr) 🔔 This function returns true if the string s contains substr and false if it does not.", "contains(string, substring)": "161🚀 Contains(s, substr) 🔔 This function returns true if the string s contains substr and false if it does not.", "containsany(s, substr)": "161🚀 ContainsAny(s, substr) 🔔 This function returns true if the string s contains any of the characters contained in the string substr.", "containsany()": "161🚀 ContainsAny(s, substr) 🔔 This function returns true if the string s contains any of the characters contained in the string substr.", "containsany(string, substring)": "161🚀 ContainsAny(s, substr) 🔔 This function returns true if the string s contains any of the characters contained in the string substr.", "containsrune(s, rune)": "161🚀 ContainsRune(s, rune) 🔔 This function returns true if the string s contains a specific rune.", "containsrune()": "161🚀 ContainsRune(s, rune) 🔔 This function returns true if the string s contains a specific rune.", "containsrune(string, rune)": "161🚀 ContainsRune(s, rune) 🔔 This function returns true if the string s contains a specific rune.", "containsrune(string, r)": "161🚀 ContainsRune(s, rune) 🔔 This function returns true if the string s contains a specific rune.", "containsrune(string, R)": "161🚀 ContainsRune(s, rune) 🔔 This function returns true if the string s contains a specific rune.", "containsrune(s, R)": "161🚀 ContainsRune(s, rune) 🔔 This function returns true if the string s contains a specific rune.", "containsrune(S, R)": "161🚀 ContainsRune(s, rune) 🔔 This function returns true if the string s contains a specific rune.", "equalfold(s1, s2)": "161🚀 EqualFold(s1, s2) 🔔 This function performs a case-insensitive comparison and returns true of strings s1 and s2 are the same.", "equalfold()": "161🚀 EqualFold(s1, s2) 🔔 This function performs a case-insensitive comparison and returns true of strings s1 and s2 are the same.", "equalfold(string, string)": "161🚀 EqualFold(s1, s2) 🔔 This function performs a case-insensitive comparison and returns true of strings s1 and s2 are the same.", "equalfold(string1, string2)": "161🚀 EqualFold(s1, s2) 🔔 This function performs a case-insensitive comparison and returns true of strings s1 and s2 are the same.", "hasprefix(s, prefix)": "161🚀 HasPrefix(s, prefix) 🔔 This function returns true if the string s begins with the string prefix.", "hasprefix(s, p)": "161🚀 HasPrefix(s, prefix) 🔔 This function returns true if the string s begins with the string prefix.", "hasprefix(string, p)": "161🚀 HasPrefix(s, prefix) 🔔 This function returns true if the string s begins with the string prefix.", "hasprefix(string, prefix)": "161🚀 HasPrefix(s, prefix) 🔔 This function returns true if the string s begins with the string prefix.", "hasprefix()": "161🚀 HasPrefix(s, prefix) 🔔 This function returns true if the string s begins with the string prefix.", "hassuffix(s, suffix)": "161🚀 HasSuffix(s, suffix) 🔔 This function returns true if the string ends with the string suffix.", "hassuffix(s, suf)": "161🚀 HasSuffix(s, suffix) 🔔 This function returns true if the string ends with the string suffix.", "hassuffix(string, suf)": "161🚀 HasSuffix(s, suffix) 🔔 This function returns true if the string ends with the string suffix.", "hassuffix(string, sufix)": "161🚀 HasSuffix(s, suffix) 🔔 This function returns true if the string ends with the string suffix.", "hassuffix()": "161🚀 HasSuffix(s, suffix) 🔔 This function returns true if the string ends with the string suffix.", "hassuffix(string, string)": "161🚀 HasSuffix(s, suffix) 🔔 This function returns true if the string ends with the string suffix.", "fields(s)": "169🚀 Fields(s) 🔔 This function splits a string on whitespace characters and returns a slice containing the nonwhitespace sections of the string s.", "fields()": "169🚀 Fields(s) 🔔 This function splits a string on whitespace characters and returns a slice containing the nonwhitespace sections of the string s.", "fields(string)": "169🚀 Fields(s) 🔔 This function splits a string on whitespace characters and returns a slice containing the nonwhitespace sections of the string s.", "fieldsfunc(s, func)": "169🚀 FieldsFunc(s, func) 🔔 This function splits the string s on the characters for which a custom function returns true and returns a slice containing the remaining sections of the string.", "fieldsfunc()": "169🚀 FieldsFunc(s, func) 🔔 This function splits the string s on the characters for which a custom function returns true and returns a slice containing the remaining sections of the string.", "fieldsfunc(string, function)": "169🚀 FieldsFunc(s, func) 🔔 This function splits the string s on the characters for which a custom function returns true and returns a slice containing the remaining sections of the string.", "split(s, sub)": "169🚀 Split(s, sub) 🔔 This function splits the string s on every occurrence of the specified substring, returning a string slice. If the separator is the empty string, then the slice will contain strings for each character.", "split()": "169🚀 Split(s, sub) 🔔 This function splits the string s on every occurrence of the specified substring, returning a string slice. If the separator is the empty string, then the slice will contain strings for each character.", "split(string, sub)": "169🚀 Split(s, sub) 🔔 This function splits the string s on every occurrence of the specified substring, returning a string slice. If the separator is the empty string, then the slice will contain strings for each character.", "splitn(s, sub, max)": "169🚀 SplitN(s, sub, max) 🔔 This function is similar to Split, but accepts an additional int argument that specifies the maximum number of substrings to return. The last substring in the result slice will contain the unsplit portion of the source string.", "splitn()": "169🚀 SplitN(s, sub, max) 🔔 This function is similar to Split, but accepts an additional int argument that specifies the maximum number of substrings to return. The last substring in the result slice will contain the unsplit portion of the source string.", "splitn(string, sub, max)": "169🚀 SplitN(s, sub, max) 🔔 This function is similar to Split, but accepts an additional int argument that specifies the maximum number of substrings to return. The last substring in the result slice will contain the unsplit portion of the source string.", "splitafter(s, sub)": "169🚀 SplitAfter(s, sub) 🔔 This function is similar to Split but includes the substring used in the results.", "splitafter()": "169🚀 SplitAfter(s, sub) 🔔 This function is similar to Split but includes the substring used in the results.", "splitafter(string, sub)": "169🚀 SplitAfter(s, sub) 🔔 This function is similar to Split but includes the substring used in the results.", "splitaftern(s, sub, max)": "169🚀 SplitAfterN(s, sub, max) 🔔 This function is similar to SplitAfter, but accepts an additional int argument that specifies the maximum number of substrings to return.", "splitaftern()": "169🚀 SplitAfterN(s, sub, max) 🔔 This function is similar to SplitAfter, but accepts an additional int argument that specifies the maximum number of substrings to return.", "splitaftern(string, sub, max)": "169🚀 SplitAfterN(s, sub, max) 🔔 This function is similar to SplitAfter, but accepts an additional int argument that specifies the maximum number of substrings to return.", "replace(s, old, new, n)": "179🚀 Replace(s, old, new, n) 🔔 This function alters the string s by replacing occurrences of the string old with the string new. The maximum number of occurrences that will be replaced is specified by the int argument n.", "replace()": "179🚀 Replace(s, old, new, n) 🔔 This function alters the string s by replacing occurrences of the string old with the string new. The maximum number of occurrences that will be replaced is specified by the int argument n.", "replace(1, 2, 3, 4)": "179🚀 Replace(s, old, new, n) 🔔 This function alters the string s by replacing occurrences of the string old with the string new. The maximum number of occurrences that will be replaced is specified by the int argument n.", "replace(1,2,3,4)": "179🚀 Replace(s, old, new, n) 🔔 This function alters the string s by replacing occurrences of the string old with the string new. The maximum number of occurrences that will be replaced is specified by the int argument n.", "replace(string, old, new, number)": "179🚀 Replace(s, old, new, n) 🔔 This function alters the string s by replacing occurrences of the string old with the string new. The maximum number of occurrences that will be replaced is specified by the int argument n.", "replaceall(s, old, new)": "179🚀 ReplaceAll(s, old, new) 🔔 This function alters the string s by replacing all occurrences of the string old with the string new. Unlike the Replace function, there is no limit on the number of occurrences that will be replaced.", "replaceall()": "179🚀 ReplaceAll(s, old, new) 🔔 This function alters the string s by replacing all occurrences of the string old with the string new. Unlike the Replace function, there is no limit on the number of occurrences that will be replaced.", "replaceall(string, old, new)": "179🚀 ReplaceAll(s, old, new) 🔔 This function alters the string s by replacing all occurrences of the string old with the string new. Unlike the Replace function, there is no limit on the number of occurrences that will be replaced.", "map(func, s)": "179🚀 Map(func, s) 🔔 This function generates a string by invoking the custom function for each character in the string s and concatenating the results. If the function produces a negative value, the current character is dropped without a replacement.", "map()": "179🚀 Map(func, s) 🔔 This function generates a string by invoking the custom function for each character in the string s and concatenating the results. If the function produces a negative value, the current character is dropped without a replacement.", "map(function, string)": "179🚀 Map(func, s) 🔔 This function generates a string by invoking the custom function for each character in the string s and concatenating the results. If the function produces a negative value, the current character is dropped without a replacement.", "replace(s)": "183🚀 Replace(s) 🔔 This method returns a string for which all the replacements specified with the constructor have been performed on the string s.", "replace(string)": "183🚀 Replace(s) 🔔 This method returns a string for which all the replacements specified with the constructor have been performed on the string s.", "replace(str)": "183🚀 Replace(s) 🔔 This method returns a string for which all the replacements specified with the constructor have been performed on the string s.", "writestring(writer, s)": "183🚀 WriteString(writer, s) 🔔 This method is used to perform the replacements specified with the constructor and write the results to an io.Writer", "writestring(w, s)": "183🚀 WriteString(writer, s) 🔔 This method is used to perform the replacements specified with the constructor and write the results to an io.Writer", "writestring(w, str)": "183🚀 WriteString(writer, s) 🔔 This method is used to perform the replacements specified with the constructor and write the results to an io.Writer", "writestring(w, string)": "183🚀 WriteString(writer, s) 🔔 This method is used to perform the replacements specified with the constructor and write the results to an io.Writer", "join(slice, sep)": "184🚀 Join(slice, sep) 🔔 This function combines the elements in the specified string slice, with the specified separator string placed between elements.", "join(slice, specified)": "184🚀 Join(slice, sep) 🔔 This function combines the elements in the specified string slice, with the specified separator string placed between elements.", "join()": "184🚀 Join(slice, sep) 🔔 This function combines the elements in the specified string slice, with the specified separator string placed between elements.", "repeat(s, count)": "184🚀 Repeat(s, count) 🔔 This function generates a string by repeating the string s for a specified number of times.", "repeat(str, count)": "184🚀 Repeat(s, count) 🔔 This function generates a string by repeating the string s for a specified number of times.", "writestring(s)": "186🚀 WriteString(s) 🔔 This method appends the string s to the string being built.", "writestring(string)": "186🚀 WriteString(s) 🔔 This method appends the string s to the string being built.", "writestring()": "186🚀 WriteString(s) 🔔 This method appends the string s to the string being built.", "writerune(r)": "186🚀 WriteRune(r) 🔔 This method appends the character r to the string being built.", "writerune()": "186🚀 WriteRune(r) 🔔 This method appends the character r to the string being built.", "writebyte(b)": "186🚀 WriteByte(b) 🔔 This method appends the byte b to the string being built.", "writebyte(byte)": "186🚀 WriteByte(b) 🔔 This method appends the byte b to the string being built.", "writebyte()": "186🚀 WriteByte(b) 🔔 This method appends the byte b to the string being built.", "string()": "186🚀 String() 🔔 This method returns the string that has been created by the builder.", "reset()": "186🚀 Reset() 🔔 This method resets the string created by the builder.", "len()": "186🚀 Len() 🔔 This method returns the number of bytes used to store the string created by the builder.", "cap()": "186🚀 Cap() 🔔 This method returns the number of bytes that have been allocated by the builder.", "grow(size)": "186🚀 Grow(size) 🔔 This method increases the number of bytes used allocated by the builder to store the string that is being built.", "grow(siz)": "186🚀 Grow(size) 🔔 This method increases the number of bytes used allocated by the builder to store the string that is being built.", "grow(sizes)": "186🚀 Grow(size) 🔔 This method increases the number of bytes used allocated by the builder to store the string that is being built.", "matchstring(s)": "192🚀 MatchString(s) 🔔 This method returns true if the string s matches the compiled pattern.", "matchstring(str)": "192🚀 MatchString(s) 🔔 This method returns true if the string s matches the compiled pattern.", "matchstring(string)": "192🚀 MatchString(s) 🔔 This method returns true if the string s matches the compiled pattern.", "matchstring(strings)": "192🚀 MatchString(s) 🔔 This method returns true if the string s matches the compiled pattern.", "findstringindex(s)": "192🚀 FindStringIndex(s) 🔔 This method returns an int slice containing the location for the left most match made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findstringindex(str)": "192🚀 FindStringIndex(s) 🔔 This method returns an int slice containing the location for the left most match made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findstringindex(string)": "192🚀 FindStringIndex(s) 🔔 This method returns an int slice containing the location for the left most match made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findstringindex(strings)": "192🚀 FindStringIndex(s) 🔔 This method returns an int slice containing the location for the left most match made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findallstringindex(s, max)": "192🚀 FindAllStringIndex(s, max) 🔔 This method returns a slice of int slices that contain the location for all the matches made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findallstringindex(str, max)": "192🚀 FindAllStringIndex(s, max) 🔔 This method returns a slice of int slices that contain the location for all the matches made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findallstringindex(string, max)": "192🚀 FindAllStringIndex(s, max) 🔔 This method returns a slice of int slices that contain the location for all the matches made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findallstringindex(strings, max)": "192🚀 FindAllStringIndex(s, max) 🔔 This method returns a slice of int slices that contain the location for all the matches made by the compiled pattern in the string s. A nil result indicates that no matches were made.", "findstring(s)": "192🚀 FindString(s) 🔔 This method returns a string containing the left-most match made by the compiled pattern in the string s. An empty string will be returned if no match is made.", "findstring(str)": "192🚀 FindString(s) 🔔 This method returns a string containing the left-most match made by the compiled pattern in the string s. An empty string will be returned if no match is made.", "findstring(string)": "192🚀 FindString(s) 🔔 This method returns a string containing the left-most match made by the compiled pattern in the string s. An empty string will be returned if no match is made.", "findstring(strings)": "192🚀 FindString(s) 🔔 This method returns a string containing the left-most match made by the compiled pattern in the string s. An empty string will be returned if no match is made.", "findallstring(s, max)": "192🚀 FindAllString(s, max) 🔔 This method returns a string slice containing the matches made by the compiled pattern in the string s. The int argument max specifies the maximum number of matches, with -1 specifying no limit. A nil result is returned if there are no matches.", "findallstring(str, max)": "192🚀 FindAllString(s, max) 🔔 This method returns a string slice containing the matches made by the compiled pattern in the string s. The int argument max specifies the maximum number of matches, with -1 specifying no limit. A nil result is returned if there are no matches.", "findallstring(string, max)": "192🚀 FindAllString(s, max) 🔔 This method returns a string slice containing the matches made by the compiled pattern in the string s. The int argument max specifies the maximum number of matches, with -1 specifying no limit. A nil result is returned if there are no matches.", "split(s, max)": "192🚀 Split(s, max) 🔔 This method splits the string s using matches from the compiled pattern as separators and returns a slice containing the split substrings.", "split(str, max)": "192🚀 Split(s, max) 🔔 This method splits the string s using matches from the compiled pattern as separators and returns a slice containing the split substrings.", "split(string, max)": "192🚀 Split(s, max) 🔔 This method splits the string s using matches from the compiled pattern as separators and returns a slice containing the split substrings.", "findstringsubmatch(s)": "197🚀 FindStringSubmatch(s) 🔔 This method returns a slice containing the first match made by the pattern and the text for the subexpressions that the pattern defines.", "findstringsubmatch(str)": "197🚀 FindStringSubmatch(s) 🔔 This method returns a slice containing the first match made by the pattern and the text for the subexpressions that the pattern defines.", "findstringsubmatch(string)": "197🚀 FindStringSubmatch(s) 🔔 This method returns a slice containing the first match made by the pattern and the text for the subexpressions that the pattern defines.", "findallstringsubmatch(s, max)": "197🚀 FindAllStringSubmatch(s, max) 🔔 This method returns a slice containing all the matches and the text for the subexpressions. The int argument is used to specify the maximum number of matches. A value of -1 specifies all matches.", "findallstringsubmatch(str, max)": "197🚀 FindAllStringSubmatch(s, max) 🔔 This method returns a slice containing all the matches and the text for the subexpressions. The int argument is used to specify the maximum number of matches. A value of -1 specifies all matches.", "findallstringsubmatch(string, max)": "197🚀 FindAllStringSubmatch(s, max) 🔔 This method returns a slice containing all the matches and the text for the subexpressions. The int argument is used to specify the maximum number of matches. A value of -1 specifies all matches.", "findstringsubmatchindex(s)": "197🚀 FindStringSubmatchIndex(s) 🔔 This method is equivalent to FindStringSubmatch but returns indices rather than substrings. FindAllStringSubmatchIndex", "findstringsubmatchindex(str)": "197🚀 FindStringSubmatchIndex(s) 🔔 This method is equivalent to FindStringSubmatch but returns indices rather than substrings. FindAllStringSubmatchIndex", "findstringsubmatchindex(string)": "197🚀 FindStringSubmatchIndex(s) 🔔 This method is equivalent to FindStringSubmatch but returns indices rather than substrings. FindAllStringSubmatchIndex", "numsubexp()": "197🚀 NumSubexp() 🔔 This method returns the number of subexpressions.", "subexpindex(name)": "197🚀 SubexpIndex(name) 🔔 This method returns the index of the subexpression with the specified name or -1 if there is no such subexpression.", "subexpindex(names)": "197🚀 SubexpIndex(name) 🔔 This method returns the index of the subexpression with the specified name or -1 if there is no such subexpression.", "subexpindex(n)": "197🚀 SubexpIndex(name) 🔔 This method returns the index of the subexpression with the specified name or -1 if there is no such subexpression.", "subexpnames()": "197🚀 SubexpNames() 🔔 This method returns the names of the subexpressions, expressed in the order in which they are defined.", "replaceallstring(s, template)": "199🚀 ReplaceAllString(s, template) 🔔 This method replaces the matched portion of the string s with the specified template, which is expanded before it is included in the result to incorporate subexpressions.", "replaceallstring(str, temp)": "199🚀 ReplaceAllString(s, template) 🔔 This method replaces the matched portion of the string s with the specified template, which is expanded before it is included in the result to incorporate subexpressions.", "replaceallstring(string, temp)": "199🚀 ReplaceAllString(s, template) 🔔 This method replaces the matched portion of the string s with the specified template, which is expanded before it is included in the result to incorporate subexpressions.", "replaceallstring(strings, temp)": "199🚀 ReplaceAllString(s, template) 🔔 This method replaces the matched portion of the string s with the specified template, which is expanded before it is included in the result to incorporate subexpressions.", "replaceallstring(strings, template)": "199🚀 ReplaceAllString(s, template) 🔔 This method replaces the matched portion of the string s with the specified template, which is expanded before it is included in the result to incorporate subexpressions.", "replaceallstring(string, template)": "199🚀 ReplaceAllString(s, template) 🔔 This method replaces the matched portion of the string s with the specified template, which is expanded before it is included in the result to incorporate subexpressions.", "replaceallliteralstring(s, sub)": "199🚀 ReplaceAllLiteralString(s, sub) 🔔 This method replaces the matched portion of the string s with the specified content, which is included in the result without being expanded for subexpressions.", "replaceallliteralstring(str, sub)": "199🚀 ReplaceAllLiteralString(s, sub) 🔔 This method replaces the matched portion of the string s with the specified content, which is included in the result without being expanded for subexpressions.", "replaceallliteralstring(string, sub)": "199🚀 ReplaceAllLiteralString(s, sub) 🔔 This method replaces the matched portion of the string s with the specified content, which is included in the result without being expanded for subexpressions.", "replaceallliteralstring(string, specified)": "199🚀 ReplaceAllLiteralString(s, sub) 🔔 This method replaces the matched portion of the string s with the specified content, which is included in the result without being expanded for subexpressions.", "replaceallstringfunc(s, func)": "199🚀 ReplaceAllStringFunc(s, func) 🔔 This method replaces the matched portion of the string s with the result produced by the specified function.", "replaceallstringfunc(str, func)": "199🚀 ReplaceAllStringFunc(s, func) 🔔 This method replaces the matched portion of the string s with the result produced by the specified function.", "replaceallstringfunc(string, function)": "199🚀 ReplaceAllStringFunc(s, func) 🔔 This method replaces the matched portion of the string s with the result produced by the specified function.", "print(...vals)": "203🚀 Print(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out. Spaces are added between values that are not strings.", "print()": "203🚀 Print(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out. Spaces are added between values that are not strings.", "print(...)": "203🚀 Print(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out. Spaces are added between values that are not strings.", "print(...values)": "203🚀 Print(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out. Spaces are added between values that are not strings.", "println(...vals)": "203🚀 Println(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out, separated by spaces and followed by a newline character.", "println(vals)": "203🚀 Println(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out, separated by spaces and followed by a newline character.", "println()": "203🚀 Println(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out, separated by spaces and followed by a newline character.", "println(values)": "203🚀 Println(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out, separated by spaces and followed by a newline character.", "println(...value)": "203🚀 Println(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out, separated by spaces and followed by a newline character.", "println(...)": "203🚀 Println(...vals) 🔔 This function accepts a variable number of arguments and writes out their values to the standard out, separated by spaces and followed by a newline character.", "fprint(writer, ...vals)": "203🚀 Fprint(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer, Spaces are added between values that are not strings.", "fprint(w, ...vals)": "203🚀 Fprint(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer, Spaces are added between values that are not strings.", "fprint(w, vals)": "203🚀 Fprint(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer, Spaces are added between values that are not strings.", "fprint()": "203🚀 Fprint(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer, Spaces are added between values that are not strings.", "fprint(wr, vals)": "203🚀 Fprint(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer, Spaces are added between values that are not strings.", "fprintln(writer, ...vals)": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "fprintln(wri, ...vals)": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "fprintln(w, ...vals)": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "fprintln(w, vals)": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "fprintln(w, values)": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "fprintln(w, v)": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "fprintln()": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "fprintln(wrt, vls)": "203🚀 Fprintln(writer, ...vals) 🔔 This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values.", "sprintf(t, ...vals)": "206🚀 Sprintf(t, ...vals) 🔔 This function returns a string, which is created by processing the template t.", "sprintf(t, vals)": "206🚀 Sprintf(t, ...vals) 🔔 This function returns a string, which is created by processing the template t.", "sprintf()": "206🚀 Sprintf(t, ...vals) 🔔 This function returns a string, which is created by processing the template t.", "sprintf(1,...2)": "206🚀 Sprintf(t, ...vals) 🔔 This function returns a string, which is created by processing the template t.", "printf(t, ...vals)": "206🚀 Printf(t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to the standard out.", "printf(t, ...)": "206🚀 Printf(t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to the standard out.", "printf()": "206🚀 Printf(t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to the standard out.", "printf(1,2)": "206🚀 Printf(t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to the standard out.", "printf(1,...2)": "206🚀 Printf(t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to the standard out.", "fprintf(writer, t, ...vals)": "206🚀 Fprintf(writer, t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to a Writer, which is described in Chapter 20.", "fprintf(write, t, ...vals)": "206🚀 Fprintf(writer, t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to a Writer, which is described in Chapter 20.", "fprintf()": "206🚀 Fprintf(writer, t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to a Writer, which is described in Chapter 20.", "fprintf(1,2,3)": "206🚀 Fprintf(writer, t, ...vals) 🔔 This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to a Writer, which is described in Chapter 20.", "errorf(t, ...values)": "206🚀 Errorf(t, ...values) 🔔 This function creates an error by processing the template t. The remaining arguments are used as values for the template verbs. The result is an error value whose Error method returns the formatted string.", "errorf(t, ...val)": "206🚀 Errorf(t, ...values) 🔔 This function creates an error by processing the template t. The remaining arguments are used as values for the template verbs. The result is an error value whose Error method returns the formatted string.", "errorf(t, ...vals)": "206🚀 Errorf(t, ...values) 🔔 This function creates an error by processing the template t. The remaining arguments are used as values for the template verbs. The result is an error value whose Error method returns the formatted string.", "errorf()": "206🚀 Errorf(t, ...values) 🔔 This function creates an error by processing the template t. The remaining arguments are used as values for the template verbs. The result is an error value whose Error method returns the formatted string.", "errorf(1,2)": "206🚀 Errorf(t, ...values) 🔔 This function creates an error by processing the template t. The remaining arguments are used as values for the template verbs. The result is an error value whose Error method returns the formatted string.", "errorf(1,...2)": "206🚀 Errorf(t, ...values) 🔔 This function creates an error by processing the template t. The remaining arguments are used as values for the template verbs. The result is an error value whose Error method returns the formatted string.", "scan(...vals)": "220🚀 Scan(...vals) 🔔 This function reads text from the standard in and stores the space- separated values into specified arguments. Newlines are treated as spaces, and the function reads until it has received values for all of its arguments. The result is the number of values that have been read and an error that describes any problems.", "scan(...)": "220🚀 Scan(...vals) 🔔 This function reads text from the standard in and stores the space- separated values into specified arguments. Newlines are treated as spaces, and the function reads until it has received values for all of its arguments. The result is the number of values that have been read and an error that describes any problems.", "scan()": "220🚀 Scan(...vals) 🔔 This function reads text from the standard in and stores the space- separated values into specified arguments. Newlines are treated as spaces, and the function reads until it has received values for all of its arguments. The result is the number of values that have been read and an error that describes any problems.", "scan(...2)": "220🚀 Scan(...vals) 🔔 This function reads text from the standard in and stores the space- separated values into specified arguments. Newlines are treated as spaces, and the function reads until it has received values for all of its arguments. The result is the number of values that have been read and an error that describes any problems.", "scanln(...vals)": "220🚀 Scanln(...vals) 🔔 This function works in the same way as Scan but stops reading when it encounters a newline character.", "scanln(...)": "220🚀 Scanln(...vals) 🔔 This function works in the same way as Scan but stops reading when it encounters a newline character.", "scanln()": "220🚀 Scanln(...vals) 🔔 This function works in the same way as Scan but stops reading when it encounters a newline character.", "scanln(...2)": "220🚀 Scanln(...vals) 🔔 This function works in the same way as Scan but stops reading when it encounters a newline character.", "scanf(template, ...vals)": "220🚀 Scanf(template, ...vals) 🔔 This function works in the same way as Scan but uses a template string to select the values from the input it receives.", "scanf(temp, ...vals)": "220🚀 Scanf(template, ...vals) 🔔 This function works in the same way as Scan but uses a template string to select the values from the input it receives.", "scanf(temp, ...values)": "220🚀 Scanf(template, ...vals) 🔔 This function works in the same way as Scan but uses a template string to select the values from the input it receives.", "scanf(1,2)": "220🚀 Scanf(template, ...vals) 🔔 This function works in the same way as Scan but uses a template string to select the values from the input it receives.", "scanf(1,...2)": "220🚀 Scanf(template, ...vals) 🔔 This function works in the same way as Scan but uses a template string to select the values from the input it receives.", "scanf()": "220🚀 Scanf(template, ...vals) 🔔 This function works in the same way as Scan but uses a template string to select the values from the input it receives.", "fscan(reader, ...vals)": "220🚀 Fscan(reader, ...vals) 🔔 This function reads space-separated values from the specified reader, which is described in Chapter 20. Newlines are treated as spaces, and the function returns the number of values that have been read and an error that describes any problems.", "fscan(reade, ...vals)": "220🚀 Fscan(reader, ...vals) 🔔 This function reads space-separated values from the specified reader, which is described in Chapter 20. Newlines are treated as spaces, and the function returns the number of values that have been read and an error that describes any problems.", "fscan(reader, ...values)": "220🚀 Fscan(reader, ...vals) 🔔 This function reads space-separated values from the specified reader, which is described in Chapter 20. Newlines are treated as spaces, and the function returns the number of values that have been read and an error that describes any problems.", "fscan(reader, values)": "220🚀 Fscan(reader, ...vals) 🔔 This function reads space-separated values from the specified reader, which is described in Chapter 20. Newlines are treated as spaces, and the function returns the number of values that have been read and an error that describes any problems.", "fscan(1,2)": "220🚀 Fscan(reader, ...vals) 🔔 This function reads space-separated values from the specified reader, which is described in Chapter 20. Newlines are treated as spaces, and the function returns the number of values that have been read and an error that describes any problems.", "fscan()": "220🚀 Fscan(reader, ...vals) 🔔 This function reads space-separated values from the specified reader, which is described in Chapter 20. Newlines are treated as spaces, and the function returns the number of values that have been read and an error that describes any problems.", "fscanln(reader, ...vals)": "220🚀 Fscanln(reader, ...vals) 🔔 This function works in the same way as Fscan but stops reading when it encounters a newline character.", "fscanln(reader, ...values)": "220🚀 Fscanln(reader, ...vals) 🔔 This function works in the same way as Fscan but stops reading when it encounters a newline character.", "fscanln(reader, values)": "220🚀 Fscanln(reader, ...vals) 🔔 This function works in the same way as Fscan but stops reading when it encounters a newline character.", "fscanln(read, values)": "220🚀 Fscanln(reader, ...vals) 🔔 This function works in the same way as Fscan but stops reading when it encounters a newline character.", "fscanln(1,2)": "220🚀 Fscanln(reader, ...vals) 🔔 This function works in the same way as Fscan but stops reading when it encounters a newline character.", "fscanln()": "220🚀 Fscanln(reader, ...vals) 🔔 This function works in the same way as Fscan but stops reading when it encounters a newline character.", "fscanf(reader, template, ...vals)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(reader, template, ...values)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(reader, template, values)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(reader, temp, values)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(read, temp, values)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(read, temp, val)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(read, temp, vals)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(1,2,3)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf(1, 2, 3)": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "fscanf()": "220🚀 Fscanf(reader, template, ...vals) 🔔 This function works in the same way as Fscan but uses a template to select the values from the input it receives.", "sscan(str, ...vals)": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscan(str, vals)": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscan(str, values)": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscan(string, values)": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscan(string, ...values)": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscan()": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscan(1,2)": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscan(1, 2)": "220🚀 Sscan(str, ...vals) 🔔 This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems.", "sscanf(str, template, ...vals)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(string, template, ...vals)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(string, template, vals)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(string, template, values)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(str, template, values)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(str, temp, values)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(str, temp, vals)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(1,2,3)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf(1, 2, 3)": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanf()": "220🚀 Sscanf(str, template, ...vals) 🔔 This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the", "sscanln(str, template, ...vals)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(string, template, ...vals)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(string, template, vals)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(string, template, values)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(str, temp, vals)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(str, template, vals)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(str, template, values)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(1,2,3)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln(1, 2, 3)": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "sscanln()": "220🚀 Sscanln(str, template, ...vals) 🔔 This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered.", "abs(val)": "226🚀 Abs(val) 🔔 This function returns the absolute value of a float64 value, meaning the distance from zero without considering direction.", "abs()": "226🚀 Abs(val) 🔔 This function returns the absolute value of a float64 value, meaning the distance from zero without considering direction.", "ceil(val)": "226🚀 Ceil(val) 🔔 This function returns the smallest integer that is equal to or greater than the specified float64 value. The result is also a float64 value, even though it represents an integer number.", "ceil()": "226🚀 Ceil(val) 🔔 This function returns the smallest integer that is equal to or greater than the specified float64 value. The result is also a float64 value, even though it represents an integer number.", "copysign(x, y)": "226🚀 Copysign(x, y) 🔔 This function returns a float64 value, which is the absolute value of x with the sign of y.", "copysign(1,2)": "226🚀 Copysign(x, y) 🔔 This function returns a float64 value, which is the absolute value of x with the sign of y.", "copysign(1, 2)": "226🚀 Copysign(x, y) 🔔 This function returns a float64 value, which is the absolute value of x with the sign of y.", "copysign()": "226🚀 Copysign(x, y) 🔔 This function returns a float64 value, which is the absolute value of x with the sign of y.", "floor(val)": "226🚀 Floor(val) 🔔 This function returns the largest integer that is smaller or equal to the specified float64 value. The result is also a float64 value, even though it represents an integer number.", "floor()": "226🚀 Floor(val) 🔔 This function returns the largest integer that is smaller or equal to the specified float64 value. The result is also a float64 value, even though it represents an integer number.", "max(x, y)": "226🚀 Max(x, y) 🔔 This function returns whichever of the specified float64 value is the largest.", "max(x,y)": "226🚀 Max(x, y) 🔔 This function returns whichever of the specified float64 value is the largest.", "max(1,2)": "226🚀 Max(x, y) 🔔 This function returns whichever of the specified float64 value is the largest.", "max(1, 2)": "226🚀 Max(x, y) 🔔 This function returns whichever of the specified float64 value is the largest.", "max()": "226🚀 Max(x, y) 🔔 This function returns whichever of the specified float64 value is the largest.", "min(x, y)": "226🚀 Min(x, y) 🔔 This function returns whichever of the specified float64 value is smallest.", "min(x,y)": "226🚀 Min(x, y) 🔔 This function returns whichever of the specified float64 value is smallest.", "min(1,2)": "226🚀 Min(x, y) 🔔 This function returns whichever of the specified float64 value is smallest.", "min(1, 2)": "226🚀 Min(x, y) 🔔 This function returns whichever of the specified float64 value is smallest.", "min()": "226🚀 Min(x, y) 🔔 This function returns whichever of the specified float64 value is smallest.", "mod(x, y)": "226🚀 Mod(x, y) 🔔 This function returns the remainder of x/y.", "mod(x,y)": "226🚀 Mod(x, y) 🔔 This function returns the remainder of x/y.", "mod(1,2)": "226🚀 Mod(x, y) 🔔 This function returns the remainder of x/y.", "mod(1, 2)": "226🚀 Mod(x, y) 🔔 This function returns the remainder of x/y.", "mod()": "226🚀 Mod(x, y) 🔔 This function returns the remainder of x/y.", "pow(x, y)": "226🚀 Pow(x, y) 🔔 This function returns x raised to the exponent y.", "pow(x,y)": "226🚀 Pow(x, y) 🔔 This function returns x raised to the exponent y.", "pow()": "226🚀 Pow(x, y) 🔔 This function returns x raised to the exponent y.", "pow(1,2)": "226🚀 Pow(x, y) 🔔 This function returns x raised to the exponent y.", "pow(1, 2)": "226🚀 Pow(x, y) 🔔 This function returns x raised to the exponent y.", "round(val)": "226🚀 Round(val) 🔔 This function rounds the specified value to the nearest integer, rounding half values up. The result is a float64 value, even though it represents an integer.", "round()": "226🚀 Round(val) 🔔 This function rounds the specified value to the nearest integer, rounding half values up. The result is a float64 value, even though it represents an integer.", "roundtoeven(val)": "226🚀 RoundToEven(val) 🔔 This function rounds the specified value to the nearest integer, rounding half values to the nearest even number. The result is a float64 value, even though it represents an integer.", "roundtoeven()": "226🚀 RoundToEven(val) 🔔 This function rounds the specified value to the nearest integer, rounding half values to the nearest even number. The result is a float64 value, even though it represents an integer.", "seed(s)": "228 🚀 Seed(s) 🔔 This function sets the seed value using the specified int64 value.", "seed()": "228 🚀 Seed(s) 🔔 This function sets the seed value using the specified int64 value.", "float32()": "228 🚀 Float32() 🔔 This function generates a random float32 value between 0 and 1.", "float64()": "228 🚀 Float64() 🔔 This function generates a random float64 value between 0 and 1.", "int()": "228 🚀 Int() 🔔 This function generates a random int value.", "intn(max)": "228 🚀 Intn(max) 🔔 This function generates a random int smaller than a specified value, as described after the table.", "intn()": "228 🚀 Intn(max) 🔔 This function generates a random int smaller than a specified value, as described after the table.", "uint32()": "228 🚀 UInt32() 🔔 This function generates a random uint32 value.", "uint64()": "228 🚀 UInt64() 🔔 This function generates a random uint64 value.", "shuffle(count, func)": "228 🚀 Shuffle(count, func) 🔔 This function is used to randomize the order of elements, as described after the table.", "float64s(slice)": "234🚀 Float64s(slice) 🔔 This function sorts a slice of float64 values. The elements are sorted in place.", "float64s()": "234🚀 Float64s(slice) 🔔 This function sorts a slice of float64 values. The elements are sorted in place.", "float64saresorted(slice)": "234🚀 Float64sAreSorted(slice) 🔔 This function returns true if the elements in the specified float64 slice are in order.", "float64saresorted()": "234🚀 Float64sAreSorted(slice) 🔔 This function returns true if the elements in the specified float64 slice are in order.", "ints(slice)": "234🚀 Ints(slice) 🔔 This function sorts a slice of int values. The elements are sorted in place.", "ints()": "234🚀 Ints(slice) 🔔 This function sorts a slice of int values. The elements are sorted in place.", "intsaresorted(slice)": "234🚀 IntsAreSorted(slice) 🔔 This function returns true if the elements in the specified int slice are in order.", "intsaresorted()": "234🚀 IntsAreSorted(slice) 🔔 This function returns true if the elements in the specified int slice are in order.", "strings(slice)": "234🚀 Strings(slice) 🔔 This function sorts a slice of string values. The elements are sorted in place.", "strings()": "234🚀 Strings(slice) 🔔 This function sorts a slice of string values. The elements are sorted in place.", "stringsaresorted(slice)": "234🚀 StringsAreSorted(slice) 🔔 This function returns true if the elements in the specified string slice are in order.", "stringsaresorted()": "234🚀 StringsAreSorted(slice) 🔔 This function returns true if the elements in the specified string slice are in order.", "typeof(val)": "478🚀 TypeOf(val) 🔔 This function returns a value that implements the Type interface, which describes the type of the specified value. There is a lot of detail behind the TypeOf and ValueOf functions and their results, and it is easy to lose sight of why reflection can be useful.", "valueof(val)": "478🚀 ValueOf(val) 🔔 This function returns a Value struct, which allows the specified value to be inspected and manipulated. There is a lot of detail behind the TypeOf and ValueOf functions and their results, and it is easy to lose sight of why reflection can be useful.", "name()": "481🚀 Name() 🔔 This method returns the name of the type.", "pkgpath()": "481🚀 PkgPath() 🔔 This method returns the package path for the type. The empty string is returned for built-in types, such as int and bool.", "kind()": "481🚀 Kind() 🔔 This method returns the kind of type, using a value that matches one of the constant values defined by the reflect package, as described in Table 27-5.", "481 string()": "481🚀 String() 🔔 This method returns a string representation of the type name, including the package name.", "comparable()": "481🚀 Comparable() 🔔 This method returns true if values of this type can be compared using the standard comparison operator, as described in the “Comparing Values” section.", "assignableto(type)": "481🚀 AssignableTo(type) 🔔 This method returns true if values of this type can be assigned to variables or fields of the specified reflected type.", "assignableto()": "481🚀 AssignableTo(type) 🔔 This method returns true if values of this type can be assigned to variables or fields of the specified reflected type.", "484 kind()": "484🚀 Kind() 🔔 This method returns the kind of the value's type.", "type()": "484🚀 Type() 🔔 This method returns the Type for the Value.", "isnil()": "484🚀 IsNil() 🔔 This method returns true if the value is nil. This method will panic if the underlying value isn't a function, an interface, a pointer, a slice, or a channel.", "iszero()": "484🚀 IsZero() 🔔 This method returns true if the underlying value is the zero value for its type.", "bool()": "484🚀 Bool() 🔔 This method returns the underlying bool value. The method panics if the underlying value's Kind is not Bool.", "bytes()": "484🚀 Bytes() 🔔 This method returns the underlying []byte value. The method panics if the underlying value is not a byte slice. I demonstrate how to determine the type of a slice in the “Identifying Byte Slices” section.", "484 int()": "484🚀 Int() 🔔 This method returns the underlying value as an int64. The method panics if the underlying value's Kind is not Int, Int8, Int16, Int32, or Int64.", "unit()": "484🚀 Uint() 🔔 This method returns the underlying value as an uint64. The method panics if the underlying value's Kind is not Uint, Uint8, Uint16, Uint32, or Uint64.", "float()": "484🚀 Float() 🔔 This method returns the underlying value as an float64. The method panics if the underlying value's Kind is not Float32, or Float64.", "484 string()": "484🚀 String() 🔔 This method returns the underlying value as a string if the value's Kind is String. For other Kind values, this method returns the string <T Value> where T is the underlying type, such as <int Value>.", "elem()": "484🚀 Elem() 🔔 This method returns the Value to which a pointer refers. This method can also be used with interfaces, as described in Chapter 29. This method panics if the underlying value's Kind is not Ptr.", "isvalid()": "484🚀 IsValid() 🔔 This method returns false if the Value is the zero value, created as Value{} rather than obtained using ValueOf, for example. This method doesn't relate to reflected values that are the zero value of their reflected type. If this method returns false, then all other Value methods will panic.", }, }
View Source
var OriginalTimeData = DataBase{
Alldatafield: `
243.Putting Dates, Times, and Durations in Context
What are they?
The features provided by the time package are used to represent
specific moments in time and intervals or durations.
Why are they useful?
These features are useful in any application that needs to deal with
calendaring or alarm and for the development of any feature that
requires delays or notifications in the future.
How are they used?
The time package defines data types for representing dates and
individual units of time and functions for manipulating them. There
are also features integrated into the Go channel system.
Are there any pitfalls or limitations?
Dates can be complex, and care must be taken to deal with calendar
and time zone issues.
Are there any alternatives?
These are optional features, and their use is not required.
████████████████████████████████████████████████████████████████████████
244.The Functions in the time Package for Creating Time Values
Name Description
-------- -----------------------------
Now() This function creates a Time representing the current moment in time.
Date(y, m, d, h, min, sec, nsec, loc) This function creates a Time representing a specified moment in time, which is
expressed by the year, month, day, hour, minute, second, nanosecond, and Location
arguments. (The Location type is described in the “Parsing Time Values from Strings”
section.)
Unix(sec, nsec) This function creates a Time value from the number of seconds and nanoseconds since
January 1, 1970, UTC, commonly known as Unix time.
████████████████████████████████████████████████████████████████████████
245.The Methods for Accessing Time Components
Name Description
-------- ----------------------------
Date() This method returns the year, month, and day components. The year and day are
expressed as int values and the month as a Month value.
Clock() This method returns the hour, minutes, and seconds components of the Time.
Year() This method returns the year component, expressed as an int.
YearDay() This method returns the day of the year, expressed as an int between 1 and 366 (to accommodate leap years).
Month() This method returns the month component, expressed using the Month type.
Day() This method returns the day of the month, expressed as an int.
Weekday() This method returns the day of the week, expressed as a Weekday.
Hour() This method returns the hour of the day, expressed as an int between 0 and 23.
Minute() This method returns the number of minutes elapsed into the hour of the day, expressed as an int between 0 and 59.
Second() This method returns the number of seconds elapsed into the minute of the hour, expressed as an int between 0 and 59.
Nanosecond() This method returns the number of nanoseconds elapsed into the second of the minute,
expressed as an int between 0 and 999,999,999.
████████████████████████████████████████████████████████████████████████
246.The Types Used to Describe Time Components
Name Description
------- ------------------------------------------------------------------------
Month This type represents a month, and the time package defines constant values for the English-
language month names: January, February, etc. The Month type defines a String method that
uses these names when formatting strings.
Weekday This type represents a day of the week, and the time package defines constant values for the
English-language weekday names: Sunday, Monday, etc. The Weekday type defines a String
method that uses these names when formatting strings.
████████████████████████████████████████████████████████████████████████
247.Creating Time Values
example:
package main
import "time"
func PrintTime(label string, t *time.Time) {
Printfln("%s: Day: %v: Month: %v Year: %v",
label, t.Day(), t.Month(), t.Year())
}
func main() {
current := time.Now()
specific := time.Date(1995, time.June, 9, 0, 0, 0, 0, time.Local)
unix := time.Unix(1433228090, 0)
PrintTime("Current", ¤t)
PrintTime("Specific", &specific)
PrintTime("UNIX", &unix)
}
Output:
Current: Day: 15: Month: September Year: 2023
Specific: Day: 9: Month: June Year: 1995
UNIX: Day: 2: Month: June Year: 2015
example:
package main
import "time"
func PrintTime(label string, t *time.Time) {
Printfln("%s: Day: %v: Month: %v Year: %v",
label, t.Day(), t.Month(), t.Year())
}
func main() {
current := time.Now()
specific := time.Date(1993, time.June, 0, 0, 0, 0, 0, time.Local)
unix := time.Unix(0, 0)
PrintTime("Current", ¤t)
PrintTime("Specific", &specific)
PrintTime("UNIX", &unix)
}
Output:
Current: Day: 15: Month: September Year: 2023
Specific: Day: 31: Month: May Year: 1993
UNIX: Day: 31: Month: December Year: 1969
████████████████████████████████████████████████████████████████████████
248.The Time Method for Creating Formatted Strings
Name Description
-------------- --------------------
Format(layout) This method returns a formatted string, which is created using the specified layout.
example:
package main
import (
"fmt"
"time"
)
func PrintTime(label string, t *time.Time) {
layout := "Day: 02 Month: Jan Year: 2006"
fmt.Println(label, t.Format(layout))
}
func main() {
current := time.Now()
specific := time.Date(1993, time.June, 0, 0, 0, 0, 0, time.Local)
unix := time.Unix(0, 0)
PrintTime("Current", ¤t)
PrintTime("Specific", &specific)
PrintTime("UNIX", &unix)
}
Output:
Current Day: 16 Month: Sep Year: 2023
Specific Day: 31 Month: May Year: 1993
UNIX Day: 31 Month: Dec Year: 1969
████████████████████████████████████████████████████████████████████████
249.The Layout Constants Defined by the time Package
Name Reference Date Format
----------- ----------------------------------
ANSIC Mon Jan _2 15:04:05 2006
UnixDate Mon Jan _2 15:04:05 MST 2006
RubyDate Mon Jan 02 15:04:05 -0700 2006
RFC822 02 Jan 06 15:04 MST
RFC822Z 02 Jan 06 15:04 -0700
RFC850 Monday, 02-Jan-06 15:04:05 MST
RFC1123 Mon, 02 Jan 2006 15:04:05 MST
RFC1123Z Mon, 02 Jan 2006 15:04:05 -0700
RFC3339 2006-01-02T15:04:05Z07:00
RFC3339Nano 2006-01-02T15:04:05.999999999Z07:00
Kitchen 3:04PM
Stamp Jan _2 15:04:05
StampMilli Jan _2 15:04:05.000
StampMicro Jan _2 15:04:05.000000
StampNano Jan _2 15:04:05.000000000
████████████████████████████████████████████████████████████████████████
250.The time Package Functions for Parsing Strings into Time Values
Name Description
-------------------------------------- -----------------------------------
Parse(layout, str) This function parses a string using the specified layout to create a Time value.
An error is returned to indicate problems parsing the string.
ParseInLocation(layout, str, location) This function parses a string, using the specified layout and using the
Location if no time zone is included in the string. An error is returned to
indicate problems parsing the string.
example:
package main
import (
"fmt"
"time"
)
func PrintTime(label string, t *time.Time) {
fmt.Println(label, t.Format(time.RFC822Z))
}
func main() {
dates := []string{
"09 Jun 95 00:00 GMT",
"02 Jun 15 00:00 GMT",
}
for _, d := range dates {
time, err := time.Parse(time.RFC822, d)
if err == nil {
PrintTime("Parsed", &time)
} else {
Printfln("Error: %s", err.Error())
}
}
}
Output:
Parsed 09 Jun 95 00:00 +0000
Parsed 02 Jun 15 00:00 +0000
████████████████████████████████████████████████████████████████████████
251.time.ParseInLocation()
example:
package main
import (
"fmt"
"time"
)
func PrintTime(label string, t *time.Time) {
//layout := "Day: 02 Month: Jan Year: 2006"
fmt.Println(label, t.Format(time.RFC822Z))
}
func main() {
layout := "02 Jan 06 15:04"
date := "09 Jun 95 19:30"
london, lonerr := time.LoadLocation("Europe/London")
newyork, nycerr := time.LoadLocation("America/New_York")
Tehran, TehranErr := time.LoadLocation("Asia/Tehran")
if lonerr == nil && nycerr == nil && TehranErr == nil{
TehranTime, _ := time.ParseInLocation(layout, date, Tehran)
PrintTime("Tehran:",&TehranTime)
nolocation, _ := time.Parse(layout, date)
PrintTime("No location:", &nolocation)
londonTime, _ := time.ParseInLocation(layout, date, london)
PrintTime("London:", &londonTime)
newyorkTime, _ := time.ParseInLocation(layout, date, newyork)
PrintTime("New York:", &newyorkTime)
} else {
fmt.Println(lonerr.Error(), nycerr.Error())
}
}
Output:
Tehran: 09 Jun 95 19:30 +0430
No location: 09 Jun 95 19:30 +0000
London: 09 Jun 95 19:30 +0100
New York: 09 Jun 95 19:30 -0400
████████████████████████████████████████████████████████████████████████
252.The Functions for Creating Locations
Name Description
------------------------- -----------------------------------------
LoadLocation(name) This function returns a *Location for the specified name and an
error that indicates any problems.
LoadLocationFromTZData(name,data) This function returns a *Location from a byte slice that contains a
formatted time zone database.
FixedZone(name, offset) This function returns a *Location that always uses the specified
name and offset from UTC.
The place names are defined in the IANA time zone database,
https://www.iana.org/time-zones ,
and are listed by
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
████████████████████████████████████████████████████████████████████████
253.Embedding The Time Zone Database
example:
package main
import (
"fmt"
"time"
)
func PrintTime(label string, t *time.Time) {
//layout := "Day: 02 Month: Jan Year: 2006"
fmt.Println(label, t.Format(time.RFC822Z))
}
func main() {
layout := "02 Jan 06 15:04"
date := "09 Jun 95 19:30"
Tehran, TehranErr := time.LoadLocation("Asia/Tehran")
local, _ := time.LoadLocation("Local")
if TehranErr == nil{
TehranTime, _ := time.ParseInLocation(layout, date, Tehran)
PrintTime("Tehran:",&TehranTime)
localTime, _ := time.ParseInLocation(layout, date, local)
PrintTime("Local:", &localTime)
nolocation, _ := time.Parse(layout, date)
PrintTime("No location:", &nolocation)
} else {
fmt.Println(TehranErr.Error())
}
}
Output:
Tehran: 09 Jun 95 19:30 +0430
Local: 09 Jun 95 19:30 -0400
No location: 09 Jun 95 19:30 +0000
████████████████████████████████████████████████████████████████████████
254.Specifying Time Zones
The arguments to the FixedZone function are a name and the number of seconds offset from UTC. This
example creates three fixed time zones, one of which is an hour ahead of UTC, one of which is four hours
behind, and one of which has no offset.
example:
package main
import (
"fmt"
"time"
)
func PrintTime(label string, t *time.Time) {
//layout := "Day: 02 Month: Jan Year: 2006"
fmt.Println(label, t.Format(time.RFC822Z))
}
func main() {
layout := "02 Jan 06 15:04"
date := "09 Jun 95 19:30"
london := time.FixedZone("BST", 1*60*60)
newyork := time.FixedZone("EDT", -4*60*60)
local := time.FixedZone("Local", 0)
nolocation, _ := time.Parse(layout, date)
londonTime, _ := time.ParseInLocation(layout, date, london)
newyorkTime, _ := time.ParseInLocation(layout, date, newyork)
localTime, _ := time.ParseInLocation(layout, date, local)
PrintTime("No location:", &nolocation)
PrintTime("London:", &londonTime)
PrintTime("New York:", &newyorkTime)
PrintTime("Local:", &localTime)
}
Output:
No location: 09 Jun 95 19:30 +0000
London: 09 Jun 95 19:30 +0100
New York: 09 Jun 95 19:30 -0400
Local: 09 Jun 95 19:30 +0000
████████████████████████████████████████████████████████████████████████
255.The Methods for Working with Time Values
Name Description
---------------- -------------------------------------------------
Add(duration) This method adds the specified Duration to the Time and returns the result.
Sub(time) This method returns a Duration that expresses the difference between the Time on
which the method has been called and the Time provided as the argument.
AddDate(y, m, d) This method adds the specified number of years, months, and days to the Time and
returns the result.
After(time) This method returns true if the Time on which the method has been called occurs
after the Time provided as the argument.
Before(time) This method returns true if the Time on which the method has been called occurs
before the Time provided as the argument.
Equal(time) This method returns true if the Time on which the method has been called is equal
to the Time provided as the argument.
IsZero() This method returns true if the Time on which the method has been called
represents the zero-time instant, which is January 1, year 1, 00:00:00 UTC.
In(loc) This method returns the Time value, expressed in the specified Location.
Location() This method returns the Location that is associated with the Time, effectively
allowing a time to be expressed in a different time zone.
Round(duration) This method rounds the Time to the nearest interval represented by a Duration
value.
Truncate(duration) This method rounds the Time down to the nearest interval represented by a
Duration value.
████████████████████████████████████████████████████████████████████████
256.time.Parse()
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
t, err := time.Parse(time.RFC822, "09 Jun 95 04:59 BST")
if err == nil {
Printfln("After: %v", t.After(time.Now()))
Printfln("Round: %v", t.Round(time.Hour))
Printfln("Truncate: %v", t.Truncate(time.Hour))
} else {
fmt.Println(err.Error())
}
}
Output:
After: false
Round: 1995-06-09 05:00:00 +0100 BST
Truncate: 1995-06-09 04:00:00 +0100 BST
████████████████████████████████████████████████████████████████████████
257.Comparing Time Values
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
t1, _ := time.Parse(time.RFC822Z, "09 Jun 95 04:59 +0100")
t2, _ := time.Parse(time.RFC822Z, "08 Jun 95 23:59 -0400")
Printfln("Equal Method: %v", t1.Equal(t2))
Printfln("Equality Operator: %v", t1 == t2)
}
Output:
Equal Method: true
Equality Operator: false
████████████████████████████████████████████████████████████████████████
258.The Duration Constants in the time Package
Name Description
------------ ----------------------------------------
Hour This constant represents 1 hour.
Minute This constant represents 1 minute.
Second This constant represents 1 second.
Millisecond This constant represents 1 millisecond.
Microsecond This constant represents 1 microsecond.
Nanosecond This constant represents 1 nanosecond.
████████████████████████████████████████████████████████████████████████
259.The Duration Methods
Name Description
---------------- ---------------------------------------------
Hours() This method returns a float64 that represents the Duration in hours.
Minutes() This method returns a float64 that represents the Duration in minutes.
Seconds() This method returns a float64 that represents the Duration in seconds.
Milliseconds() This method returns an int64 that represents the Duration in milliseconds.
Microseconds() This method returns an int64 that represents the Duration in microseconds.
Nanoseconds() This method returns an int64 that represents the Duration in nanoseconds.
Round(duration) This method returns a Duration, which is rounded to the nearest multiple of the
specified Duration.
Truncate(duration) This method returns a Duration, which is rounded down to the nearest multiple of
the specified Duration.
████████████████████████████████████████████████████████████████████████
260.Hours() - Minutes() - Seconds() - rounded.Hours() - rounded.Minutes()
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
var d time.Duration = time.Hour + (30 * time.Minute)
Printfln("Hours: %v", d.Hours())
Printfln("Mins: %v", d.Minutes())
Printfln("Seconds: %v", d.Seconds())
Printfln("Millseconds: %v", d.Milliseconds())
rounded := d.Round(time.Hour)
Printfln("Rounded Hours: %v", rounded.Hours())
Printfln("Rounded Mins: %v", rounded.Minutes())
trunc := d.Truncate(time.Hour)
Printfln("Truncated Hours: %v", trunc.Hours())
Printfln("Rounded Mins: %v", trunc.Minutes())
}
Output:
Hours: 1.5
Mins: 90
Seconds: 5400
Millseconds: 5400000
Rounded Hours: 2
Rounded Mins: 120
Truncated Hours: 1
Rounded Mins: 60
████████████████████████████████████████████████████████████████████████
261.The time Functions for Creating Duration Values relative to a Time
Name Description
----------- ----------------------------------------
Since(time) This function returns a Duration expressing the elapsed time since the specified Time value.
Until(time) This function returns a Duration expressing the elapsed time until the specified Time value.
████████████████████████████████████████████████████████████████████████
262.time.Until(time) - Since(time)
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
toYears := func(d time.Duration) int {
return int(d.Hours() / (24 * 365))
}
future := time.Date(2051, 0, 0, 0, 0, 0, 0, time.Local)
past := time.Date(1965, 0, 0, 0, 0, 0, 0, time.Local)
Printfln("this year is %v.",time.Now().Year())
Printfln("Future: %v is %v.", toYears(time.Until(future)), future.Year())
Printfln("Past: %v is %v.", toYears(time.Since(past)), past.Year())
}
Output:
this year is 2023.
Future: 27 is 2050.
Past: 58 is 1964.
████████████████████████████████████████████████████████████████████████
263.time.ParseDuration function
This function returns a Duration and an error, indicating if there were problems
parsing the specified string.
The format of the strings supported by the ParseDuration function is a sequence of number values
followed by the unit indicators:
Unit Description
----- --------------------
h This unit denotes hours.
m This unit denotes minutes.
s This unit denotes seconds.
ms This unit denotes milliseconds.
us or μs These units denotes microseconds.
ns This unit denotes nanoseconds.
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
d, err := time.ParseDuration("1h30m")
if err == nil {
Printfln("Hours: %v", d.Hours())
Printfln("Mins: %v", d.Minutes())
Printfln("Seconds: %v", d.Seconds())
Printfln("Millseconds: %v", d.Milliseconds())
} else {
fmt.Println(err.Error())
}
}
Output:
Hours: 1.5
Mins: 90
Seconds: 5400
Millseconds: 5400000
████████████████████████████████████████████████████████████████████████
264.Using the Time Features for Goroutines and Channels
The time package provides a small set of functions that are useful for working with goroutines and channels.
The time Package Functions:
Name Description
---------------------- ----------------------------------------
Sleep(duration) This function pauses the current goroutine for at least the specified duration.
AfterFunc(duration,func) This function executes the specified function in its own goroutine after the
specified duration. The result is a *Timer, whose Stop method can be used to
cancel the execution of the function before the duration elapses.
After(duration) This function returns a channel that blocks for the specified duration and then
yields a Time value. See the “Receiving Timed Notifications” section for details.
Tick(duration) This function returns a channel that periodically sends a Time value, where the
period is specified as a duration.
████████████████████████████████████████████████████████████████████████
265.time.Sleep(time.Second * 1)
Pausing a Goroutine
The Sleep function pauses execution of the current goroutine for a specified duration
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func writeToChannel(channel chan<- string) {
names := []string{"Alice", "Bob", "Charlie", "Dora"}
for _, name := range names {
channel <- name
time.Sleep(time.Second * 1)
}
close(channel)
}
func main() {
nameChannel := make(chan string)
go writeToChannel(nameChannel)
for name := range nameChannel {
Printfln("Read name: %v", name)
}
}
Output:
Read name: Alice
// 1 second delaying
Read name: Bob
// 1 second delaying
Read name: Charlie
// 1 second delaying
Read name: Dora
████████████████████████████████████████████████████████████████████████
266.time.AfterFunc() function
The AfterFunc function is used to defer the execution of a function for a specified period
Deferring a Function:
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func writeToChannel(channel chan<- string) {
names := []string{"Alice", "Bob", "Charlie", "Dora"}
for _, name := range names {
channel <- name
// time.Sleep(time.Second * 1)
}
close(channel)
}
func main() {
nameChannel := make(chan string)
time.AfterFunc(time.Second*5, func() {
writeToChannel(nameChannel)
})
for name := range nameChannel {
Printfln("Read name: %v", name)
}
}
Output:
// It waits for 5 seconds and then continues the program.
Read name: Alice
Read name: Bob
Read name: Charlie
Read name: Dora
████████████████████████████████████████████████████████████████████████
267.time.After(time.Second * 2)
The result from the After function is a channel that carries Time values. The channel blocks for the
specified duration, when a Time value is sent, indicating the duration has passed. In this example, the
value sent over the channel acts as a signal and is not used directly, which is why it is assigned to the blank
identifier, like this:
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func writeToChannel(channel chan<- string) {
Printfln("Waiting for initial duration...")
_ = <-time.After(time.Second * 2)
Printfln("Initial duration elapsed.")
names := []string{"Alice", "Bob", "Charlie", "Dora"}
for _, name := range names {
channel <- name
time.Sleep(time.Second * 1)
}
close(channel)
}
func main() {
nameChannel := make(chan string)
go writeToChannel(nameChannel)
for name := range nameChannel {
Printfln("Read name: %v", name)
}
}
Output:
Waiting for initial duration... // Wait for 2 seconds
Initial duration elapsed.
Read name: Alice // wait for 1 second
Read name: Bob // wait for 1 second
Read name: Charlie // wait for 1 second
Read name: Dora // wait for 1 second
████████████████████████████████████████████████████████████████████████
268.time.Sleep(time.Second * 3) with select statement
Using a Timeout in a Select Statement
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func writeToChannel(channel chan<- string) {
Printfln("Waiting for initial duration...")
_ = <-time.After(time.Second * 2)
Printfln("Initial duration elapsed.")
names := []string{"Alice", "Bob", "Charlie", "Dora"}
for _, name := range names {
channel <- name
time.Sleep(time.Second * 3)
}
close(channel)
}
func main() {
nameChannel := make(chan string)
go writeToChannel(nameChannel)
channelOpen := true
for channelOpen {
Printfln("Starting channel read")
select {
case name, ok := <-nameChannel:
if !ok {
channelOpen = false
} else {
Printfln("Read name: %v", name)
}
case <-time.After(time.Second * 2):
Printfln("Timeout")
}
}
}
Output:
Starting channel read
Waiting for initial duration...
Timeout
Starting channel read
Initial duration elapsed.
Read name: Alice
Starting channel read
Timeout
Starting channel read
Read name: Bob
Starting channel read
Timeout
Starting channel read
Read name: Charlie
Starting channel read
Timeout
Starting channel read
Read name: Dora
Starting channel read
Timeout
Starting channel read
████████████████████████████████████████████████████████████████████████
269.NewTimer(duration)
This function returns a *Timer with the specified period.
Caution Be careful when stopping a timer.
The timer's channel is not closed, which means that reads from
the channel will continue to block even after the timer has stopped.
The Methods Defined by the Timer Struct:
Name Description
------------ -------------------------------------------
C This field returns the channel over which the Time will send its Time value.
Stop() This method stops the timer. The result is a bool that will be true if the timer has been
stopped and false if the timer had already sent its message.
Reset(duration) This method stops a timer and resets it so that its interval is the specified Duration.
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func writeToChannel(channel chan<- string) {
timer := time.NewTimer(time.Minute * 10)
go func() {
time.Sleep(time.Second * 2)
Printfln("Resetting timer")
timer.Reset(time.Second)
}()
Printfln("Waiting for initial duration...")
<-timer.C
Printfln("Initial duration elapsed.")
names := []string{"Alice", "Bob", "Charlie", "Dora"}
for _, name := range names {
channel <- name
}
close(channel)
}
func main() {
nameChannel := make(chan string)
go writeToChannel(nameChannel)
for name := range nameChannel {
Printfln("Read name: %v", name)
}
}
Output:
Waiting for initial duration...
Resetting timer
Initial duration elapsed.
Read name: Alice
Read name: Bob
Read name: Charlie
Read name: Dora
████████████████████████████████████████████████████████████████████████
270.time.Tick(time.Second)
Receiving Recurring Notifications دریافت اعلان های مکرر
The Tick function returns a channel over which Time values are sent at a specified interval
Tick is a convenience wrapper for NewTicker providing access to the ticking channel only.
While Tick is useful for clients that have no need to shut down the Ticker,
be aware that without a way to shut it down the underlying
Ticker cannot be recovered by the garbage collector; it "leaks".
Unlike NewTicker, Tick will return nil if d <= 0.
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func writeToChannel(nameChannel chan<- string) {
names := []string{"Alice", "Bob", "Charlie", "Dora"}
tickChannel := time.Tick(time.Second)
index := 0
for {
<-tickChannel
nameChannel <- names[index]
index++
if index == len(names) {
index = 0
}
}
}
func main() {
nameChannel := make(chan string)
go writeToChannel(nameChannel)
for name := range nameChannel {
Printfln("Read name: %v", name)
}
}
Output:
Read name: Alice
Read name: Bob
Read name: Charlie
Read name: Dora
Read name: Alice
Read name: Bob
Read name: Charlie
Read name: Dora
Read name: Alice
Read name: Bob
Read name: Charlie
Read name: Dora
...
████████████████████████████████████████████████████████████████████████
271.NewTicker(duration)
The result of the NewTicker function is a pointer to a Ticker struct, which defines the field and methods
The time Function for Creating a Ticker:
Name Description
--------------------- ---------------------------------
NewTicker(duration) This function returns a *Ticker with the specified period.
The Field and Methods Defined by the Ticker Struct:
Name Description
---------------- --------------------------------
C This field returns the channel over which the Ticker will send its Time values.
Stop() This method stops the ticker (but does not close the channel returned by the C field).
Reset(duration) This method stops a ticker and resets it so that its interval is the specified Duration.
████████████████████████████████████████████████████████████████████████
272.Creating a Ticker in the main.go
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func writeToChannel(nameChannel chan<- string) {
names := []string{"Alice", "Bob", "Charlie", "Dora"}
ticker := time.NewTicker(time.Second / 10)
index := 0
for {
<-ticker.C
nameChannel <- names[index]
index++
if index == len(names) {
ticker.Stop()
close(nameChannel)
break
}
}
}
func main() {
nameChannel := make(chan string)
go writeToChannel(nameChannel)
for name := range nameChannel {
Printfln("Read name: %v", name)
}
}
Output:
// This is printed after milliseconds
Read name: Alice
Read name: Bob
Read name: Charlie
Read name: Dora`,
}
View Source
var OriginalUsingReflection = DataBase{
Alldatafield: `
474.Using Reflection
`,
}
View Source
var OriginalWordsGoNum = WordsInGolangNumbered{ MapOfNumbered: map[string]string{ "0": `0.Create Environment GO in Command Line Interface Go: 1- go mod init YOURNAME 2- go work init YOURWORKDIRECTORY 3- go run main.go OR go run projectName.go package main = first executable file main.go func main() {} = every execute files in "package main" import = for importing another package function A function is an independent section of code that mapszero or more input parameters to zero or more outputparameters. Functions (also known as procedures orsubroutines) are often represented as a black box: (theblack box represents the function) func FunctionName(ParameterName ParameterType) ReturnType { body of function} example: func getUserName(UserInput string) string { return UserInput } returning multiple values Go is also capable of returning multiple values from afunction variadic functions func add(args ...int) int {} `, "1": `1.build Using the Go Command The go build command compiles the source code in the current directory and generates an executable file. `, "2": `2.clean go clean command removes the output produced by the go build command, the executable and any temporary files that were created during the build. `, "3": `3.doc The go doc command generates documentation from source code. `, "4": `4.fmt The go fmt command ensures consistent indentation and alignment in source code files. fmt = for printing in CLI fmt.Printf("%v %s %f", variable, string, float32) `, "5": `5.get The go get command downloads and installs external packages. flag flag package: Command-line flags are a common way to specify options for command-line programs. For example, in wc -l the -l is a command-line flag. Go provides a flag package supporting basic command-line flag parsing. We'll use this package to implement our example command-line program. `, "6": `6.install The go install command downloads packages and is usually used to install tool packages. `, "7": `7.help The go help command displays help information for other Go features. The command go help build, for example, displays information about the build argument. `, "8": `8.mod The go mod command is used to create and manage a Go module. `, "9": `9.run The go run command builds and executes the source code in a specified folder without creating an executable output `, "10": `10.test The go test command executes unit tests `, "11": `11.version The go version command writes out the Go version number. `, "12": `12.vet The go vet command detects common problems in Go code `, "13": `13.print <expr> Useful Debugger State Commands This command evaluates an expression and displays the result. It can be used to display a value (print i) or perform a more complex test (print i > 0). `, "14": `14.set <variable> = <value> This command changes the value of the specified variable. این دستور مقدار متغیر مشخص شده را تغییر می دهد. `, "15": `15.locals This command prints the value of all local variables. `, "16": `16.whatis <expr> This command prints the type of the specified expression such as whatis `, "17": `17.continue Useful Debugger Commands for Controlling Execution This command resumes execution of the application. `, "18": `18.next This command moves to the next statement. `, "19": `19.step This command steps into the current statement. `, "20": `20.stepout This command steps out of the current statement. `, "21": `21.restart This command restarts the process. Use the continue command to begin execution. `, "22": `22.exit This command exits the debugger. `, "23": `23.int = integers Understanding the Basic Data Types This type represents a whole number, which can be positive or negative. The int type size is platform-dependent and will be either 32 or 64 bits. There are also integer types that have a specific size, such as int8, int16, int32, and int64, but the int type should be used unless you need a specific size. Literal Value Examples: 20, -20. Values can also be expressed in hex (0x14), octal (0o24), and binary notation (0b0010100). `, "24": `24.unit uint8, uint16, uint32, uint64,int8, int16, int32 This type represents a positive whole number. The uint type size is platform- dependent and will be either 32 or 64 bits. There are also unsigned integer types that have a specific size, such as uint8, uint16, uint32, and uint64, but the uint type should be used unless you need a specific size. Literal Value Examples: There are no uint literals. All literal whole numbers are treated as int values. `, "25": `25.byte = uint8 This type is an alias for uint8 and is typically used to represent a byte of data. byte = 8 bits, 1024bytes = 1 kilobyte 1024 kilobytes = 1 megabyte Literal Value Examples: There are no byte literals. Bytes are typically expressed as integer literals (such as 101) or run literals ('e') since the byte type is an alias for the uint8 type. `, "26": `26.float32, float64 Floating Point Numbers. e.g 3.14, 123.541, 100.4020401 These types represent numbers with a fraction. These types allocate 32 or 64 bits to store the value. Literal Value Examples: 20.2, -20.2, 1.2e10, 1.2e-10. Values can also be expressed in hex notation (0x2p10), although the exponent is expressed in decimal digits. `, "27": `27.complex64, complex128 These types represent numbers that have real and imaginary components. These types allocate 64 or 128 bits to store the value. `, "28": `28.bool bolean This type represents a Boolean truth with the values true and false. Literal Value Examples: true, false. `, "29": `29.string This type represents a sequence of characters. Literal Value Examples: "Hello". Character sequences escaped with a backslash are interpreted if the value is enclosed in double quotes ("Hello\n"). Escape sequences are not interpreted if the value is enclosed in backquotes ('Hello\n'). `, "30": `30.rune = int32 This type represents a single Unicode code point. Unicode is complicated, but—loosely—this is the representation of a single character. The rune type is an alias for int32. Literal Value Examples: 'A', '\n', '\u00A5', '¥'. Characters, glyphs, and escape sequences are enclosed in single quotes (the ' character). `, "31": `31.var = variable Variables are defined using the var keyword, and, unlike constants, the value assigned to a variable can be changed `, "32": `32.const = constant Constants are basically variables whose values cannot be changed later. The Zero Values for the Basic Data Types Type Zero Value ------ ----------- int 0 unit 0 byte 0 float64 0 bool false string “” (the empty string) rune 0 `, "33": `33.+, -, *, /, % Operations and Conversions These operators are used to perform arithmetic using numeric values. `, "34": `34.==, !=, <, <=, >, >= These operators compare two values. `, "35": `35., &&, ! These are the logical operators, which are applied to bool values and return a bool value. `, "36": `36.=, := These are the assignment operators. The standard assignment operator (=) is used to set the initial value when a constant or variable is defined, or to change the value assigned to a previously defined variable. The shorthand operator (:=) is used to define a variable and assign a value. `, "37": `37.-=, +=, ++, -- These operators increment and decrement numeric values. `, "38": `38.&, , ^, &^, <<, >> These are the bitwise operators, which can be applied to integer values. These operators are not often required in mainstream development where the operator is used to configure the Go logging features. The Arithmetic Operators Operator Description ------- --------------------- + This operator returns the sum of two operands. - This operator returns the difference between two operands. * This operator returns the product of two operands. / This product returns the quotient of two operators. % This product returns the remainder, which is similar to the modulo operator provided by other programming languages but can return negative values, as described in the “Using the Remainder Operator” section. The Comparison Operators Operator Description ------- --------------------- == This operator returns true if the operands are equal. != This operator returns true if the operands are not equal. < This operator returns true if the first operand is less than the second operand. > This operator returns true if the first operand is greater than the second operand. <= This operator returns true if the first operand is less than or equal to the second operand. >= This operator returns true if the first operand is greater than or equal to the second operand. The Logical Operators Operator Description ------- --------------------- || This operator returns true if either operand is true. If the first operand is true, then the second operand will not be evaluated. && This operator returns true if both operands are true. If the first operand is false, then the second operand will not be evaluated. ! This operator is used with a single operand. It returns true if the operand is false and false if the operand is true. `, "39": `39.Ceil(value) Converting Floating-Point Values to Integers Functions in the math Package for Converting Numeric Types This function returns the smallest integer that is greater than the specified floating- point value. The smallest integer that is greater than 27.1, for example, is 28. `, "40": `40.Floor(value) This function returns the largest integer that is less than the specified floating-point value. The largest integer that is less than 27.1, for example, is 28. `, "41": `41.Round(value) This function rounds the specified floating-point value to the nearest integer. `, "42": `42.RoundToEven(value) This function rounds the specified floating-point value to the nearest even integer. `, "43": `43.ParseBool(str) Parsing from Strings Functions for Parsing Strings into Other Data Types This function parses a string into a bool value. Recognized string values are "true", "false", "TRUE", "FALSE", "True", "False", "T", "F", "0", and "1". `, "44": `44.ParseFloat(str,size) This function parses a string into a floating-point value with the specified size, as described in the “Parsing Floating-Point Numbers” section. `, "45": `45.ParseInt(str,base, size) This function parses a string into an int64 with the specified base and size. Acceptable base values are 2 for binary, 8 for octal, 16 for hex, and 10. `, "46": `46.ParseUint(str,base, size) This function parses a string into an unsigned integer value with the specified base and size. `, "47": `47.Atoi(str) This function parses a string into a base 10 int and is equivalent to calling ParseInt(str, 10, 0) `, "48": `48.FormatBool(val) Formatting Values as Strings The strconv Functions for Converting Values into Strings This function returns the string true or false based on the value of the specified bool. `, "49": `49.FormatInt(val, base) This function returns a string representation of the specified int64 value, expressed in the specified base. `, "50": `50.FormatUint(val, base) This function returns a string representation of the specified uint64 value, expressed in the specified base. `, "51": `51.FormatFloat(val, format, precision, size) This function returns a string representation of the specified float64 value, expressed using the specified format, precision, and size. `, "52": `52.Itoa(val) This function returns a string representation of the specified int value, expressed using base 10. Commonly Used Format Options for Floating-Point String Formatting Function f The floating-point value will be expressed in the form ±ddd.ddd without an exponent, such as 49.95. e, E The floating-point value will be expressed in the form ±ddd.ddde±dd, such as 4.995e+01 or 4.995E+01. The case of the letter denoting the exponent is determined by the case of the rune used as the formatting argument. g, G The floating-point value will be expressed using format e/E for large exponents or format f for smaller values. `, "53": `53.if Understanding Flow Control The if keyword is followed by the expression and then the group of statements to be executed, surrounded by braces `, "54": `54.else The else keyword can be used to create additional clauses in an if statement `, "55": `55.else if The else/if combination can be repeated to create a sequence of clauses `, "56": `56.for Go allows loops only inside of functions. The for keyword is used to create loops that repeatedly execute statements. The most basic for loops will repeat indefinitely unless interrupted by the break keyword Incorporating the Condition into the Loop example: for (counter <= 3) {} Enumerating Sequences: for index, character := range product { fmt.Println("Index:", index, "Character:", string(character)) } range : is for variable like slice or array like this : for name := range sliceOfNames `, "57": `57.switch A switch statement provides an alternative way to control execution flow, based on matching the result of an expression to a specific value, as opposed to evaluating a true or false result example: switch (character) { case 'K': fmt.Println("K at position", index) case 'y': fmt.Println("y at position", index) } Matching Multiple Values: example: switch (character) { case 'K', 'k': fmt.Println("K or k at position", index) case 'y': fmt.Println("y at position", index) } Terminate case Statement Execution: example: switch (character) { case 'K', 'k': if (character == 'k') { fmt.Println("Lowercase k at position", index) break } fmt.Println("Uppercase K at position", index) case 'y': fmt.Println("y at position", index) } `, "58": `58.Falling Through Go switch statements don't automatically fall through, but this behavior can be enabled using the fallthrough keyword example: switch (character) { case 'K': fmt.Println("Uppercase character") fallthrough case 'k': fmt.Println("k at position", index) case 'y': fmt.Println("y at position", index) } `, "59": `59.default The default keyword is used to define a clause that will be executed when none of the case statements matches the switch statement's value example: switch (character) { case 'K', 'k': if (character == 'k') { fmt.Println("Lowercase k at position", index) break } fmt.Println("Uppercase K at position", index) case 'y': fmt.Println("y at position", index) default: fmt.Println("Character", string(character), "at position", index) } `, "60": `60.goto label Label statements allow execution to jump to a different point, giving greater flexibility than other flow control features example: counter := 0 target: fmt.Println("Counter", counter) counter++ if (counter < 5) { goto target } `, "61": `61.Array Go arrays are a fixed length and contain elements of a single type, which are accessed by index var names [3]string An array is a numbered sequence of elements of a sin-gle type with a fixed length. Array Literal Syntax: names := [3]string { "Kayak", "Lifejacket", "Paddle" } The number of elements specified with the literal syntax can be less than the capacity of the array. Any position in the array for which a value is not provided will be assigned the zero value for the array type. Enumerating Arrays: example: names := [3]string { "Kayak", "Lifejacket", "Paddle" } for index, value := range names { fmt.Println("Index:", index, "Value:", value) } `, "62": `62.Slices The slice type in this example is []string The best way to think of slices is as a variable-length array because they are useful when you don't know how many values you need to store or when the number changes over time. One way to define a slice is to use the built-in make function. A slice is a segment of an array. Like arrays slices areindexable and have a length. Unlike arrays this lengthis allowed to change. if you want to create a slice you should use the built-inmake function example: names := make([]string, 3) names[0] = "Kayak" names[1] = "Lifejacket" names[2] = "Paddle" Literal Syntax: example: names := []string {"Kayak", "Lifejacket", "Paddle"} `, "63": `63.append If you define a slice variable but don't initialize it, then the result is a slice that has a length of zero and a capacity of zero, and this will cause an error when an element is appended to it. One of the key advantages of slices is that they can be expanded to accommodate additional elements Appending Elements to a Slice: example: names := []string {"Kayak", "Lifejacket", "Paddle"} names = append(names, "Hat", "Gloves") Appending Items to a Slice: example: names := []string {"Kayak", "Lifejacket", "Paddle"} appendedNames := append(names, "Hat", "Gloves") names[0] = "Canoe" Allocating Additional Slice Capacity: example: names := make([]string, 3, 6) names[0] = "Kayak" names[1] = "Lifejacket" names[2] = "Paddle" Adding Elements to a Slice: example: names := make([]string, 3, 6) names[0] = "Kayak" names[1] = "Lifejacket" names[2] = "Paddle" appendedNames := append(names, "Hat", "Gloves") names[0] = "Canoe" fmt.Println("names:",names) fmt.Println("appendedNames:", appendedNames) Appending One Slice to Another: example: names := make([]string, 3, 6) names[0] = "Kayak" names[1] = "Lifejacket" names[2] = "Paddle" moreNames := []string { "Hat Gloves"} appendedNames := append(names, moreNames...) fmt.Println("appendedNames:", appendedNames) Creating Slices from Existing Arrays: example: products := [4]string { "Kayak", "Lifejacket", "Paddle", "Hat"} someNames := products[1:3] allNames := products[:] Specifying Capacity When Creating a Slice from an Array: example: products := [4]string { "Kayak", "Lifejacket", "Paddle", "Hat"} someNames := products[1:3:3] allNames := products[:] someNames = append(someNames, "Gloves") Creating Slices from Other Slices: example: products := [4]string { "Kayak", "Lifejacket", "Paddle", "Hat"} allNames := products[1:] someNames := allNames[1:3] allNames = append(allNames, "Gloves") allNames[1] = "Canoe" Comparing Slices: example: Go restricts the use of the comparison operator so that slices can be compared only to the nil value. p1 := []string { "Kayak", "Lifejacket", "Paddle", "Hat"} p2 := p1 fmt.Println("Equal:", p1 == p2) Output: .\main.go:13:30: invalid operation: p1 == p2 (slice can only be compared to nil) `, "64": `64.copy The copy function is used to copy elements between slices. This function can be used to ensure that slices have separate arrays and to create slices that combine elements from different sources. Duplicating a Slice: The copy function accepts two arguments: example: products := [4]string { "Kayak", "Lifejacket", "Paddle", "Hat"} allNames := products[1:] someNames := make([]string, 2) copy(someNames, allNames) Output: allNames [Lifejacket Paddle Hat] someNames: [Lifejacket Paddle] the Uninitialized Slice Pitfall: the copy function doesn't resize the destination slice. A common pitfall is to try to copy elements into a slice that has not been initialized example: products := [4]string { "Kayak", "Lifejacket", "Paddle", "Hat"} allNames := products[1:] var someNames []string copy(someNames, allNames) Output: someNames: [] allNames [Lifejacket Paddle Hat] Specifying Ranges When Copying Slices: Fine-grained control over the elements that are copied can be achieved using ranges example: products := [4]string { "Kayak", "Lifejacket", "Paddle", "Hat"} allNames := products[1:] someNames := []string { "Boots", "Canoe"} copy(someNames[1:], allNames[2:3]) Output: allNames [Lifejacket Paddle Hat] someNames: [Boots Hat] Copying Slices with Different Sizes: products := []string { "Kayak", "Lifejacket", "Paddle", "Hat"} replacementProducts := []string { "Canoe", "Boots"} copy(products, replacementProducts) Output: products: [Canoe Boots Paddle Hat] Copying a Larger Source Slice example: products := []string { "Kayak", "Lifejacket", "Paddle", "Hat"} replacementProducts := []string { "Canoe", "Boots"} copy(products[0:1], replacementProducts) fmt.Println("products:", products) Output: products: [Canoe Lifejacket Paddle Hat] Deleting Slice Elements: products := [4]string { "Kayak", "Lifejacket", "Paddle", "Hat"} deleted := append(products[:2], products[3:]...) fmt.Println("Deleted:", deleted) Enumerating Slices: example: products := []string { "Kayak", "Lifejacket", "Paddle", "Hat"} for index, value := range products[2:] { fmt.Println("Index:", index, "Value:", value) } Output: Index: 0 Value: Paddle Index: 1 Value: Hat `, "65": `65.sort There is no built-in support for sorting slices, but the standard library includes the sort package, which defines functions for sorting different types of slice. example: products := []string { "Kayak", "Lifejacket", "Paddle", "Hat"} sort.Strings(products) for index, value := range products { fmt.Println("Index:", index, "Value:", value) } Output: Index: 0 Value: Hat Index: 1 Value: Kayak Index: 2 Value: Lifejacket Index: 3 Value: Paddle `, "66": `66.DeepEqual The DeepEqual function can be used to compare a wider range of data types than the equality operator, including slices. example: package main import ( "fmt" "reflect" ) func main() { p1 := []string { "Kayak", "Lifejacket", "Paddle", "Hat"} p2 := p1 fmt.Println("Equal:", reflect.DeepEqual(p1, p2)) } Output: Equal: true `, "67": `67.map Maps are a built-in data structure that associates data values with keys. Unlike arrays, where values are associated with sequential integer locations, maps can use other data types as keys. A map is an unordered collection of key-value pairs. Also known as an associative array, a hash table or dictionary, maps are used to look up a value by its associated key. example: products := make(map[string]float64, 10) products["Kayak"] = 279 products["Lifejacket"] = 48.95 Map Literal Syntax: products := map[string]float64 { "Kayak" : 279, "Lifejacket": 48.95, } Checking for Items in a Map: example: products := map[string]float64 { "Kayak" : 279, "Lifejacket": 48.95, "Hat": 0, } value, ok := products["Hat"] if (ok) { fmt.Println("Stored value:", value) } else { fmt.Println("No stored value") } Removing Items from a Map: example: products := map[string]float64 { "Kayak" : 279, "Lifejacket": 48.95, "Hat": 0, } delete(products, "Hat") Enumerating the Contents of a Map: example: products := map[string]float64 { "Kayak" : 279, "Lifejacket": 48.95, "Hat": 0, } for key, value := range products { fmt.Println("Key:", key, "Value:", value) } Enumerating a Map in Order: example: import ( "fmt" "sort" ) func main() { products := map[string]float64 { "Kayak" : 279, "Lifejacket": 48.95, "Hat": 0, } keys := make([]string, 0, len(products)) for key, := range products { keys = append(keys, key) } sort.Strings(keys) for , key := range keys { fmt.Println("Key:", key, "Value:", products[key]) } } `, "68": `68.strconv Understanding the Dual Nature of Strings Indexing and Slicing a String: var price string = "$48.95" var currency byte = price[0] var amountString string = price[1:] amount, parseErr := strconv.ParseFloat(amountString, 64) fmt.Println("Currency:", currency) if (parseErr == nil) { fmt.Println("Amount:", amount) } else { fmt.Println("Parse Error:", parseErr) } Output: Currency: 36 Amount: 48.95 Converting the Result: var price string = "$48.95" var currency string = string(price[0]) var amountString string = price[1:] amount, parseErr := strconv.ParseFloat(amountString, 64) fmt.Println("Currency:", currency) if (parseErr == nil) { fmt.Println("Amount:", amount) } else { fmt.Println("Parse Error:", parseErr) } Output: Currency: $ Amount: 48.95 Changing the Currency Symbol: example: var price string = "€48.95" var currency string = string(price[0]) var amountString string = price[1:] amount, parseErr := strconv.ParseFloat(amountString, 64) fmt.Println("Currency:", currency) if (parseErr == nil) { fmt.Println("Amount:", amount) } else { fmt.Println("Parse Error:", parseErr) } Output: Currency: â Parse Error: strconv.ParseFloat: parsing "\x82\xac48.95": invalid syntax Obtaining the Length: fmt.Println("Length:", len(price)) Output: Length: 8 `, "69": `69.rune Converting a String to Runes The rune type represents a Unicode code point, which is essentially a single character. To avoid slicing strings in the middle of characters, an explicit conversion to a rune slice can be performed Unicode is incredibly complex, as you would expect from any standard that aims to describe multiple writing systems evolved over thousands of years. Converting to Runes: var price []rune = []rune("€48.95") var amountString string = string(price[1:]) Enumerating Strings example: var price = "€48.95" for index, char := range price { fmt.Println(index, char, string(char)) } Output: 0 8364 € 3 52 4 4 56 8 5 46 . 6 57 9 7 53 5 Enumerating the Bytes example: var price = "€48.95" for index, char := range []byte(price) { fmt.Println(index, char) } Output: 0 226 1 130 2 172 3 52 4 56 5 46 6 57 7 53 `, "70": `70.func Functions are groups of code statements that are executed only when the function is invoked during the flow of execution. `, "71": `71.Function Parameters Parameters allow a function to receive data values when it is called, allowing its behavior to be altered. Values for parameters are supplied as arguments when invoking the function, meaning that different values can be provided each time the function is called. Arguments are provided between the parentheses that follow the function name, separated by commas and in the same order in which the parameters have been defined example: func printPrice(product string, price float64, taxRate float64) { taxAmount := price * taxRate fmt.Println(product, "price:", price, "Tax:", taxAmount) } `, "72": `72.Defining Variadic Parameters example: func printSuppliers(product string, suppliers []string ) { for , supplier := range suppliers { fmt.Println("Product:", product, "Supplier:", supplier) } } example: func printSuppliers(product string, suppliers ...string ) { for , supplier := range suppliers { fmt.Println("Product:", product, "Supplier:", supplier) } } `, "73": `73.Dealing with No Arguments for a Variadic Parameter example: func printSuppliers(product string, suppliers ...string ) { for , supplier := range suppliers { fmt.Println("Product:", product, "Supplier:", supplier) } } func main() { printSuppliers("Kayak", "Acme Kayaks", "Bob's Boats", "Crazy Canoes") printSuppliers("Lifejacket", "Sail Safe Co") printSuppliers("Soccer Ball") } Output: Product: Kayak Supplier: Acme Kayaks Product: Kayak Supplier: Bob's Boats Product: Kayak Supplier: Crazy Canoes Product: Lifejacket Supplier: Sail Safe Co `, "74": `74.return Function Results example: func calcTax(price float64) float64 { return price + (price * 0.2) } `, "75": `75.Returning Multiple Function Results example: func swapValues(first, second int) (int, int) { return second, first } func main() { val1, val2 := 10, 20 fmt.Println("Before calling function", val1, val2) val1, val2 = swapValues(val1, val2) fmt.Println("After calling function", val1, val2) } `, "76": `76.Using Named Results example: func calcTax(price float64) (float64, bool) { if (price > 100) { return price * 0.2, true } return 0, false } func calcTotalPrice(products map[string]float64, minSpend float64) (total, tax float64) { total = minSpend for , price := range products { if taxAmount, due := calcTax(price); due { total += taxAmount; tax += taxAmount } else { total += price } } return } `, "77": `77.defer The defer keyword is used to schedule a function call that will be performed immediately before the current function returns The defer keyword lets you group the statements that create, use, and release the resource together. The defer keyword can be used with any function call a single function can use the defer keyword multiple times. Immediately before the function returns, Go will perform the calls scheduled with the defer keyword in the order in which they were defined. `, "78": `78.Function Types Functions in Go have a data type, which describes the combination of parameters the function consumes and the results the function produces. This type can be specified explicitly or inferred from a function defined using a literal syntax. Function types are defined using the func keyword, followed by a signature that describes the parameters and results. No function body is specified. Go does not support arrow functions, where functions are expressed more concisely using the => operator, without the func keyword and a code block surrounded by braces. In Go, functions must always be defined with the keyword and a body. `, "79": `79.Function Comparisons and the Zero Type example: func calcWithTax(price float64) float64 { return price + (price * 0.2) } func calcWithoutTax(price float64) float64 { return price } func main() { products := map[string]float64 { "Kayak" : 275, "Lifejacket": 48.95, } for product, price := range products { var calcFunc func(float64) float64 fmt.Println("Function assigned:", calcFunc == nil) if (price > 100) { calcFunc = calcWithTax } else { calcFunc = calcWithoutTax } fmt.Println("Function assigned:", calcFunc == nil) totalPrice := calcFunc(price) fmt.Println("Product:", product, "Price:", totalPrice) } } `, "80": `80.Functions as Arguments example: func calcWithTax(price float64) float64 { return price + (price * 0.2) } func calcWithoutTax(price float64) float64 { return price } func printPrice(product string, price float64, calculator func(float64) float64 ) { fmt.Println("Product:", product, "Price:", calculator(price)) } func main() { products := map[string]float64 { "Kayak" : 275, "Lifejacket": 48.95, } for product, price := range products { if (price > 100) { printPrice(product, price, calcWithTax) } else { printPrice(product, price, calcWithoutTax) } } } `, "81": `81.Functions as Results example: func calcWithTax(price float64) float64 { return price + (price * 0.2) } func calcWithoutTax(price float64) float64 { return price } func printPrice(product string, price float64, calculator func(float64) float64 ) { fmt.Println("Product:", product, "Price:", calculator(price)) } func selectCalculator(price float64) func(float64) float64 { if (price > 100) { return calcWithTax } return calcWithoutTax } func main() { products := map[string]float64 { "Kayak" : 275, "Lifejacket": 48.95, } for product, price := range products { printPrice(product, price, selectCalculator(price)) } } `, "82": `82.Function Type Aliases Go supports type aliases, which can be used to assign a name to a function signature so that the parameter and result types are not specified every time the function type is used. example: type calcFunc func(float64) float64 func calcWithTax(price float64) float64 { return price + (price * 0.2) } func calcWithoutTax(price float64) float64 { return price } func printPrice(product string, price float64, calculator calcFunc) { fmt.Println("Product:", product, "Price:", calculator(price)) } func selectCalculator(price float64) calcFunc { if (price > 100) { return calcWithTax } return calcWithoutTax } func main() { products := map[string]float64 { "Kayak" : 275, "Lifejacket": 48.95, } for product, price := range products { printPrice(product, price, selectCalculator(price)) } } `, "83": `83.the Literal Function Syntax example: func selectCalculator(price float64) calcFunc { if (price > 100) { var withTax calcFunc = func (price float64) float64 { return price + (price * 0.2) } return withTax } `, "84": `84.Function Variable Scope example: func selectCalculator(price float64) calcFunc { if (price > 100) { var withTax calcFunc = func (price float64) float64 { return price + (price * 0.2) } return withTax } else if (price < 10) { return withTax } withoutTax := func (price float64) float64 { return price } return withoutTax } `, "85": `85.Functions Values Directly example: func selectCalculator(price float64) calcFunc { if (price > 100) { return func (price float64) float64 { return price + (price * 0.2) } } return func (price float64) float64 { return price } } `, "86": `86.Literal Function Argument example: func main() { products := map[string]float64 { "Kayak" : 275, "Lifejacket": 48.95, } for product, price := range products { printPrice(product, price, func (price float64) float64 { return price + (price * 0.2) }) } } `, "87": `87.Function Closure Functions defined using the literal syntax can reference variables from the surrounding code, a feature known as closure. example: type calcFunc func(float64) float64 func printPrice(product string, price float64, calculator calcFunc) { fmt.Println("Product:", product, "Price:", calculator(price)) } func main() { watersportsProducts := map[string]float64 { "Kayak" : 275, "Lifejacket": 48.95, } soccerProducts := map[string] float64 { "Soccer Ball": 19.50, "Stadium": 79500, } calc := func(price float64) float64 { if (price > 100) { return price + (price * 0.2) } return price; } for product, price := range watersportsProducts { printPrice(product, price, calc) } calc = func(price float64) float64 { if (price > 50) { return price + (price * 0.1) } return price } for product, price := range soccerProducts { printPrice(product, price, calc) } } `, "88": `88.struct What are they? Structs are data types, comprised of fields. Why are they useful? Structs allow custom data types to be defined. How are they used? The type and struct keywords are used to define a type, allowing field names and types to be specified. A struct can mix regular and embedded field types. example: func main() { type Product struct { name, category string price float64 } kayak := Product { name: "Kayak", category: "Watersports", price: 275, } fmt.Println(kayak.name, kayak.category, kayak.price) kayak.price = 300 fmt.Println("Changed price:", kayak.price) } Go doesn't differentiate between structs and classes, in the way that other languages do. All custom data types are defined as structs, and the decision to pass them by reference or by value is made depending on whether a pointer is used. Go doesn't allow structs to be used with the const keyword, and the compiler will report an error if you try to define a constant struct. `, "89": `89.struct tag The struct type can be defined with tags, which provide additional information about how a field should be processed. Struct tags are just strings that are interpreted by the code that processes struct values, using the features provided by the reflect package. `, "90": `90.struct values Values do not have to be provided for all fields when creating a struct value example: func main() { type Product struct { name, category string price float64 } kayak := Product { name: "Kayak", category: "Watersports", } } `, "91": `91.new the new function to create struct values var lifejacket = new(Product) The result is a pointer to a struct value whose fields are initialized with their type's zero value. This is equivalent to this statement: var lifejacket = &Product{} These approaches are interchangeable, and choosing between them is a matter of preference. `, "92": `92.Field Positions to Create Struct Values example: func main() { type Product struct { name, category string price float64 } var kayak = Product { "Kayak", "Watersports", 275.00 } fmt.Println("Name:", kayak.name) fmt.Println("Category:", kayak.category) fmt.Println("Price:", kayak.price) } `, "93": `93.Defining Embedded Fields If a field is defined without a name, it is known as an embedded field, and it is accessed using the name of its type. example: func main() { type Product struct { name, category string price float64 } type StockLevel struct { Product count int } stockItem := StockLevel { Product: Product { "Kayak", "Watersports", 275.00 }, count: 100, } fmt.Println("Name:", stockItem.Product.name) fmt.Println("Count:", stockItem.count) } `, "94": `94.Defining an Additional Field example: func main() { type Product struct { name, category string price float64 } type StockLevel struct { Product Alternate Product count int } stockItem := StockLevel { Product: Product { "Kayak", "Watersports", 275.00 }, Alternate: Product{"Lifejacket", "Watersports", 48.95 }, count: 100, } fmt.Println("Name:", stockItem.Product.name) fmt.Println("Alt Name:", stockItem.Alternate.name) } `, "95": `95.Comparing Struct Values example: func main() { type Product struct { name, category string price float64 } p1 := Product { name: "Kayak", category: "Watersports", price: 275.00 } p2 := Product { name: "Kayak", category: "Watersports", price: 275.00 } p3 := Product { name: "Kayak", category: "Boats", price: 275.00 } fmt.Println("p1 == p2:", p1 == p2) fmt.Println("p1 == p3:", p1 == p3) } `, "96": `96.Anonymous Struct Types Anonymous struct types are defined without using a name example: package main import "fmt" func writeName(val struct { name, category string price float64}) { fmt.Println("Name:", val.name) } func main() { type Product struct { name, category string price float64 //otherNames []string } type Item struct { name string category string price float64 } prod := Product { name: "Kayak", category: "Watersports", price: 275.00 } item := Item { name: "Stadium", category: "Soccer", price: 75000 } writeName(prod) writeName(item) } `, "97": `97.Creating Arrays, Slices, and Maps Containing Struct Values Omitting the Struct Type example: package main import "fmt" func main() { type Product struct { name, category string price float64 //otherNames []string } type StockLevel struct { Product Alternate Product count int } array := [1]StockLevel { { Product: Product { "Kayak", "Watersports", 275.00 }, Alternate: Product{"Lifejacket", "Watersports", 48.95 }, count: 100, }, } fmt.Println("Array:", array[0].Product.name) slice := []StockLevel { { Product: Product { "Kayak", "Watersports", 275.00 }, Alternate: Product{"Lifejacket", "Watersports", 48.95 }, count: 100, }, } fmt.Println("Slice:", slice[0].Product.name) kvp := map[string]StockLevel { "kayak": { Product: Product { "Kayak", "Watersports", 275.00 }, Alternate: Product{"Lifejacket", "Watersports", 48.95 }, count: 100, }, } fmt.Println("Map:", kvp["kayak"].Product.name) } `, "98": `98.Structs and Pointers Assigning a struct to a new variable or using a struct as a function parameter creates a new value that copies the field values. `, "99": `99.Copying a Struct Value example: package main import "fmt" func main() { type Product struct { name, category string price float64 } p1 := Product { name: "Kayak", category: "Watersports", price: 275, } p2 := p1 p1.name = "Original Kayak" fmt.Println("P1:", p1.name) fmt.Println("P2:", p2.name) } `, "100": `100.Using a Pointer to a Struct example: package main import "fmt" func main() { type Product struct { name, category string price float64 } p1 := Product { name: "Kayak", category: "Watersports", price: 275, } p2 := &p1 p1.name = "Original Kayak" fmt.Println("P1:", p1.name) fmt.Println("P2:", (*p2).name) } `, "101": `101.the Struct Pointer Convenience Syntax example: package main import "fmt" type Product struct { name, category string price float64 } func calcTax(product *Product) { if ((*product).price > 100) { (*product).price += (*product).price * 0.2 } } func main() { kayak := Product { name: "Kayak", category: "Watersports", price: 275, } calcTax(&kayak) fmt.Println("Name:", kayak.name, "Category:", kayak.category, "Price", kayak.price) } `, "102": `102.Struct Constructor Functions A constructor function is responsible for creating struct values using values received through parameters Constructor functions are used to create struct values consistently. Constructor functions are usually named new or New followed by the struct type so that the constructor function for creating Product values is named newProduct. example: package main import "fmt" type Product struct { name, category string price float64 } func newProduct(name, category string, price float64) *Product { return &Product{name, category, price} } func main() { products := [2]*Product { newProduct("Kayak", "Watersports", 275), newProduct("Hat", "Skiing", 42.50), } for , p := range products { fmt.Println("Name:", p.name, "Category:", p.category, "Price", p.price) } }`, "103": `103.Modifying a Constructor The benefit of using constructor functions is consistency, ensuring that changes to the construction process are reflected in all the struct values created by the function. example: func newProduct(name, category string, price float64) *Product { return &Product{name, category, price - 10} } `, "104": `104.Pointer Types for Struct Fields example: package main import "fmt" type Product struct { name, category string price float64 *Supplier } type Supplier struct { name, city string } func newProduct(name, category string, price float64, supplier *Supplier) *Product { return &Product{name, category, price -10, supplier} } func main() { acme := &Supplier { "Acme Co", "New York"} products := [2]*Product { newProduct("Kayak", "Watersports", 275, acme), newProduct("Hat", "Skiing", 42.50, acme), } for , p := range products { fmt.Println("Name:", p.name, "Supplier:", p.Supplier.name, p.Supplier.city) } } `, "105": `105.Pointer Field Copying Care must be taken when copying structs to consider the effect on pointer fields example: package main import "fmt" type Product struct { name, category string price float64 *Supplier } type Supplier struct { name, city string } func newProduct(name, category string, price float64, supplier *Supplier) *Product { return &Product{name, category, price -10, supplier} } func main() { acme := &Supplier { "Acme Co", "New York"} p1 := newProduct("Kayak", "Watersports", 275, acme) p2 := *p1 p1.name = "Original Kayak" p1.Supplier.name = "BoatCo" for , p := range []Product { *p1, p2 } { fmt.Println("Name:", p.name, "Supplier:", p.Supplier.name, p.Supplier.city) } } `, "106": `106.Method What are they? Methods are functions that are invoked on a struct and have access to all of the fields defined by the value's type. Interfaces define sets of methods, which can be implemented by struct types. Why are they useful? These features allow types to be mixed and used through their common characteristics. How are they used? Methods are defined using the func keyword, but with the addition of a receiver. Interfaces are defined using the type and interface keywords. Are there any pitfalls or limitations? Careful use of pointers is important when creating methods, and care must be taken when using interfaces to avoid problems with the underlying dynamic types. `, "107": `107.Defining and Using Method example: package main import "fmt" type Product struct { name, category string price float64 } func newProduct(name, category string, price float64) *Product { return &Product{ name, category, price } } func (product *Product) printDetails() { fmt.Println("Name:", product.name, "Category:", product.category, "Price", product.price) } func main() { products := []*Product { newProduct("Kayak", "Watersports", 275), newProduct("Lifejacket", "Watersports", 48.95), newProduct("Soccer Ball", "Soccer", 19.50), } for , p := range products { p.printDetails() } } `, "108": `108.Defining Method Parameters and Results example: package main import "fmt" type Product struct { name, category string price float64 } func newProduct(name, category string, price float64) *Product { return &Product{ name, category, price } } func (product *Product) printDetails() { fmt.Println("Name:", product.name, "Category:", product.category, "Price", product.price) } func main() { products := []*Product { newProduct("Kayak", "Watersports", 275), newProduct("Lifejacket", "Watersports", 48.95), newProduct("Soccer Ball", "Soccer", 19.50), } for , p := range products { p.calcTax(0.2, 100) //<-------------------------------here } } `, "109": `109.Defining and Using Interfaces One interface can enclose another, with the effect that types must implement all the methods defined by the enclosing and enclosed interfaces. Interfaces are simpler than structs, and there are no fields or method to promote. The result of composing interfaces is a union of the method defined by the enclosing and enclosed types. example: package main import "fmt" type Expense interface { getName() string getCost(annual bool) float64 } func main() { expenses := []Expense { Product { "Kayak", "Watersports", 275 }, Service {"Boat Cover", 12, 89.50 }, } for , expense := range expenses { fmt.Println("Expense:", expense.getName(), "Cost:", expense.getCost(true)) } } `, "110": `110.an Interface in a Function example: package main import "fmt" type Expense interface { getName() string getCost(annual bool) float64 } func calcTotal(expenses []Expense) (total float64) { for , item := range expenses { total += item.getCost(true) } return } func main() { expenses := []Expense { Product { "Kayak", "Watersports", 275 }, Service {"Boat Cover", 12, 89.50 }, } for , expense := range expenses { fmt.Println("Expense:", expense.getName(), "Cost:", expense.getCost(true)) } fmt.Println("Total:", calcTotal(expenses)) } `, "111": `111.an Interface for Struct Fields example: package main import "fmt" type Expense interface { getName() string getCost(annual bool) float64 } func calcTotal(expenses []Expense) (total float64) { for , item := range expenses { total += item.getCost(true) } return } type Account struct { accountNumber int expenses []Expense } func main() { account := Account { accountNumber: 12345, expenses: []Expense { Product { "Kayak", "Watersports", 275 }, Service {"Boat Cover", 12, 89.50 }, }, } for , expense := range account.expenses { fmt.Println("Expense:", expense.getName(), "Cost:", expense.getCost(true)) } fmt.Println("Total:", calcTotal(account.expenses)) } `, "112": `112.Comparing Interface Values Care must be taken when comparing interface values, and inevitably, some knowledge of the dynamic types is required. The first two Expense values are not equal. That's because the dynamic type for these values is a pointer type, and pointers are equal only if they point to the same memory location example: package main import "fmt" type Expense interface { getName() string getCost(annual bool) float64 } func main() { var e1 Expense = &Product { name: "Kayak" } var e2 Expense = &Product { name: "Kayak" } var e3 Expense = Service { description: "Boat Cover" } var e4 Expense = Service { description: "Boat Cover" } fmt.Println("e1 == e2", e1 == e2) fmt.Println("e3 == e4", e3 == e4) } Output: e1 == e2 false e3 == e4 true `, "113": `113.Empty Interface Go allows the user of the empty interface—which means an interface that defines no methods—to represent any type, which can be a useful way to group disparate types that share no common features The empty interface represents all types, including the built-in types and any structs and interfaces that have been defined. `, "114": `114.Empty Interface for Function Parameters The empty interface can be used as the type for a function parameter, allowing a function to be called with any value example: package main import "fmt" type Expense interface { getName() string getCost(annual bool) float64 } type Person struct { name, city string } func processItem(item interface{}) { switch value := item.(type) { case Product: fmt.Println("Product:", value.name, "Price:", value.price) case *Product: fmt.Println("Product Pointer:", value.name, "Price:", value.price) case Service: fmt.Println("Service:", value.description, "Price:", value.monthlyFee * float64(value.durationMonths)) case Person: fmt.Println("Person:", value.name, "City:", value.city) case *Person: fmt.Println("Person Pointer:", value.name, "City:", value.city) case string, bool, int: fmt.Println("Built-in type:", value) default: fmt.Println("Default:", value) } } func main() { var expense Expense = &Product { "Kayak", "Watersports", 275 } data := []interface{} { expense, Product { "Lifejacket", "Watersports", 48.95 }, Service {"Boat Cover", 12, 89.50, []string{} }, Person { "Alice", "London"}, &Person { "Bob", "New York"}, "This is a string", 100, true, } for , item := range data { processItem(item) } } `, "115": `115.Package Packages are the Go feature that allows projects to be structured so that related functionality can be grouped together, without the need to put all the code into a single file or folder. What are they? Packages allow projects to be structured so that related features can be developed together. Why are they useful? Packages are how Go implements access controls so that the implementation of a feature can be hidden from the code that consumes it. How are they used? Packages are defined by creating code files in folders and using the package keyword to denote which package they belong to. `, "116": `116.the Module File This name is important because it is used to import features from other packages created within the same project and third-party packages The go statement specifies the version of Go that is used. `, "117": `117.Package Access Control Go has an unusual approach to access control. Instead of relying on dedicated keywords, like public and private, Go examines the first letter of the names given to the features in a code file, such as types, functions, and methods. If the first letter is lowercase, then the feature can be used only within the package that defines it. Features are exported for use outside of the package by giving them an uppercase first letter. The access control rules do not apply to individual function or method parameters, which means that the NewProduct function has to have an uppercase first character to be exported, but the parameter names can be lowercase. `, "118": `118.Adding Code Files to Packages Packages can contain multiple code files, and to simplify development, access control rules and package prefixes do not apply when accessing features defined in the same package. `, "119": `119.func init() Each code file can contain an initialization function that is executed only when all packages have been loaded and all other initialization—such as defining constants and variables—has been done. The most common use for initialization functions is to perform calculations that are difficult to perform or that require duplication to perform The initialization function is called init, and it is defined without parameters and a result. The init function is called automatically and provides an opportunity to prepare the package for use. The init function is not a regular Go function and cannot be invoked directly. And, unlike regular functions, a single file can define multiple init functions, all of which will be executed. AVOIDING THE MULTIPLE INITIALIZATION FUNCTION PITFALL Each code file can have its own initialization function. When using the standard Go compiler, the initialization functions are executed based on the alphabetic order of the filenames, so the function in the a.go file will be executed before the function in the b.go file, and so on. But this order is not part of the Go language specification and should not be relied on. Your initialization functions should be self-contained and not rely on other init functions having been invoked previously. `, "120": `120.Creating Nested Packages Packages can be defined within other packages, making it easy to break up complex features into as many units as possible. The package statement is used just as with any other package, without the need to include the name of the parent or enclosing package. And dependency on custom packages must include the full package path. example: Create the packages/store/cart folder and add to it a file named cart.go with the contents: #1 contents: package cart import "packages/store" type Cart struct { CustomerName string Products []store.Product } func (cart *Cart) GetTotal() (total float64) { for , p := range cart.Products { total += p.Price() } return } The features defined by the nested package are accessed using the package name, just like any other package. When importing a nested package, the package path starts with the module name and lists the sequence of packages. example: package main import ( "fmt" "packages/store" . "packages/fmt" "packages/store/cart" ) func main() { product := store.NewProduct("Kayak", "Watersports", 279) cart := cart.Cart { CustomerName: "Alice", Products: []store.Product{ *product }, } fmt.Println("Name:", cart.CustomerName) fmt.Println("Total:", ToCurrency(cart.GetTotal())) } `, "121": `121.Initialization Function example: func init() { for category, price := range categoryMaxPrices { categoryMaxPrices[category] = price + (price * defaultTaxRate) } } `, "122": `122.Importing a Package Only for Initialization Effects Go prevents packages from being imported but not used, which can be a problem if you rely on the effect of an initialization function but don't need to use any of the features the package exports. If I need the effect of the initialization function, but I don't need to use the GetData function the package exports. example: package main import ( "fmt" "packages/store" . "packages/fmt" "packages/store/cart" "packages/data" ) `, "123": `123.Finding Go Packages #1 https://pkg.go.dev #2 https://github.com/golang/go/wiki/Projects Many Go modules are written by individual developers to solve a problem and then published for anyone else to use. This creates a rich module ecosystem, but it does mean that maintenance and support can be inconsistent. `, "124": `124.indirect The indirect comment at the end of the statements is added automatically because the packages are not used by the code in the project. A file named go.sum is created when the module is obtained and contains checksums used to validate the packages. example: module packages go 1.17 require ( github.com/fatih/color v1.10.0 // indirect github.com/mattn/go-colorable v0.1.8 // indirect github.com/mattn/go-isatty v0.0.12 // indirect golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect ) Note: You can also use the go.mod file to create dependencies on projects you have created locally The first time you run the go get command, you will see a list of the modules that are downloaded, which illustrated that modules have their own dependencies and that these are resolved automatically: go: downloading github.com/fatih/color v1.10.0 go: downloading github.com/mattn/go-isatty v0.0.12 go: downloading github.com/mattn/go-colorable v0.1.8 go: downloading golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae `, "125": `125.Managing External Packages Removing a Package To update the go.mod file to reflect the change, run the command `, "126": `126.go mod tidyPutting Type and Interface Composition in Context What is it? Composition is the process by which new types are created by combining structs and interfaces. Why is it useful? Composition allows types to be defined based on existing types. How is it used? Existing types are embedded in new types. Are there any pitfalls or limitations? Composition doesn't work in the same way as inheritance, and care must be taken to achieve the desired outcome. Are there any alternatives? Composition is optional, and you can create entirely independent types. Go doesn't support classes or inheritance and focuses on composition instead. But, despite the differences, composition can be used to create hierarchies of types, just in a different way. Steps: 1-Defining the Base Type -> struct - method 2-Defining a Constructor -> for create struct example: // struct: type Product struct { Name, Category string price float64 } // method: func (p *Product) Price(taxRate float64) float64 { return p.price + (p.price * taxRate) } // Constructor: func NewProduct(name, category string, price float64) *Product { return &Product{ name, category, price } } `, "127": `127.Creating Struct Values in Packages example: package main import ( "fmt" "composition/store" ) func main() { // use Constructor: kayak := store.NewProduct("Kayak", "Watersports", 275) // use the literal syntax: lifejacket := &store.Product{ Name: "Lifejacket", Category: "Watersports"} for , p := range []*store.Product { kayak, lifejacket} { fmt.Println("Name:", p.Name, "Category:", p.Category, "Price:", p.Price(0.2)) } } `, "128": `128.Steps of composition 1-Defining the Base Type -> struct - method 2-Defining a Constructor -> for create struct 3-Composing Types 4-Using the Boat Struct in the main.go A struct can mix regular and embedded field types, but the embedded fields are an important part of the composition feature Go gives special treatment to struct types that have fields whose type is another struct type, in the way that the Boat type has a *Product field in the example project. You can see this special treatment in the statement in the for loop, which is responsible for writing out details of each Boat. Go allows the fields of the nested type to be accessed in two ways. The first is the conventional approach of navigating the hierarchy of types to reach the value that is required. The *Product field is embedded, which means that its name its its type. The Boat type doesn't define a Name field, but it can be treated as though it did because of the direct access feature. This is known as field promotion, and Go essentially flattens the types so that the Boat type behaves as though it defines the fields that are provided by the nested Product type example: product.go: // struct: type Product struct { Name, Category string price float64 } // method: func (p *Product) Price(taxRate float64) float64 { return p.price + (p.price * taxRate) } // Constructor: func NewProduct(name, category string, price float64) *Product { return &Product{ name, category, price } } boat.go: package store The Boat struct type defines an embedded *Product field type Boat struct { *Product -------------------> Embedded Type Capacity int -----------------> Reguler Fields Motorized bool } func NewBoat(name string, price float64, capacity int, motorized bool) *Boat { return &Boat { NewProduct(name, "Watersports", price), capacity, motorized, } } main.go: package main import ( "fmt" "composition/store" ) func main() { boats := []*store.Boat { store.NewBoat("Kayak", 275, 1, false), store.NewBoat("Canoe", 400, 3, false), store.NewBoat("Tender", 650.25, 2, true), } for , b := range boats { fmt.Println("Conventional:", b.Product.Name, "Direct:", b.Name) } } Output: Conventional: Kayak Direct: Kayak Conventional: Canoe Direct: Canoe Conventional: Tender Direct: Tender If the field type is a value, such as Product, then any methods defined with Product or *Product receivers will be promoted. If the field type is a pointer, such as *Product, then only methods with *Product receivers will be prompted. There is no Price method defined for the *Boat type, but Go promotes the method defined with a *Product receiver. Calling a Method in the main.go: package main import ( "fmt" "composition/store" ) func main() { boats := []*store.Boat { store.NewBoat("Kayak", 275, 1, false), store.NewBoat("Canoe", 400, 3, false), store.NewBoat("Tender", 650.25, 2, true), } for , b := range boats { fmt.Println("Boat:", b.Name, "Price:", b.Price(0.2)) } } Output: Boat: Kayak Price: 330 Boat: Canoe Price: 480 Boat: Tender Price: 780.3 `, "129": `129.Creating a Chain of Nested Types The composition feature can be used to create complex chains of nested types, whose fields and methods are promoted to the top-level enclosing type. Go performs promotion so that the fields defined by all three types in the chain can be accessed directly. Go promotes fields from the nested Boat and Product types so they can be accessed through the top- level RentalBoat type, which allows the Name field to be read. Methods are also promoted to the top-level type, which is why I can use the Price method, even though it is defined on the *Product type, which is at the end of the chain. example: rentalboats.go: package store type RentalBoat struct { *Boat IncludeCrew bool } func NewRentalBoat(name string, price float64, capacity int, motorized, crewed bool) *RentalBoat { return &RentalBoat{NewBoat(name, price, capacity, motorized), crewed} } Accessing Nested Fields Directly in the main.go: package main import ( "fmt" "composition/store" ) func main() { rentals := []*store.RentalBoat { store.NewRentalBoat("Rubber Ring", 10, 1, false, false), store.NewRentalBoat("Yacht", 50000, 5, true, true), store.NewRentalBoat("Super Yacht", 100000, 15, true, true), } for , r := range rentals { fmt.Println("Rental Boat:", r.Name, "Rental Price:", r.Price(0.2)) } } Output: Rental Boat: Rubber Ring Rental Price: 12 Rental Boat: Yacht Rental Price: 60000 Rental Boat: Super Yacht Rental Price: 120000 `, "130": `130.Multiple Nested Types in the Same Struct example: rentalboats.go: package store type Crew struct { Captain, FirstOfficer string } type RentalBoat struct { *Boat IncludeCrew bool *Crew } func NewRentalBoat(name string, price float64, capacity int, motorized, crewed bool, captain, firstOfficer string) *RentalBoat { return &RentalBoat{NewBoat(name, price, capacity, motorized), crewed, &Crew{captain, firstOfficer}} } Using Promoted Fields in the main.go: package main import ( "fmt" "composition/store" ) func main() { rentals := []*store.RentalBoat { store.NewRentalBoat("Rubber Ring", 10, 1, false, false, "N/A", "N/A"), store.NewRentalBoat("Yacht", 50000, 5, true, true, "Bob", "Alice"), store.NewRentalBoat("Super Yacht", 100000, 15, true, true, "Dora", "Charlie"), } for , r := range rentals { fmt.Println("Rental Boat:", r.Name, "Rental Price:", r.Price(0.2), "Captain:", r.Captain) } } Output: Rental Boat: Rubber Ring Rental Price: 12 Captain: N/A Rental Boat: Yacht Rental Price: 60000 Captain: Bob Rental Boat: Super Yacht Rental Price: 120000 Captain: Dora `, "131": `131.When Promotion Cannot Be Performed example: specialdeal.go File in the store Folder: package store type SpecialDeal struct { Name string *Product price float64 } func NewSpecialDeal(name string, p *Product, discount float64) *SpecialDeal { return &SpecialDeal{ name, p, p.price - discount } } func (deal *SpecialDeal ) GetDetails() (string, float64, float64) { return deal.Name, deal.price, deal.Price(0) } Using a New Type in the main.go: package main import ( "fmt" "composition/store" ) func main() { product := store.NewProduct("Kayak", "Watersports", 279) deal := store.NewSpecialDeal("Weekend Special", product, 50) Name, price, Price := deal.GetDetails() fmt.Println("Name:", Name) fmt.Println("Price field:", price) fmt.Println("Price method:", Price) } Output: Name: Weekend Special Price field: 229 Price method: 279 is a more concise way of expressing this statement: return deal.Name, deal.price, deal.Product.Price(0) The new Price method stops Go from promoting the Product method and produces the following result Defining a Method in the specialdeal.go: package store type SpecialDeal struct { Name string *Product price float64 } func NewSpecialDeal(name string, p *Product, discount float64) *SpecialDeal { return &SpecialDeal{ name, p, p.price - discount } } func (deal *SpecialDeal ) GetDetails() (string, float64, float64) { return deal.Name, deal.price, deal.Price(0) } func (deal *SpecialDeal) Price(taxRate float64) float64 { return deal.price } Output: Name: Weekend Special Price field: 229 Price method: 229 `, "132": `132.Promotion Ambiguity A related issue arises when two embedded fields use the same field or method names example: An Ambiguous Method in the main.go: package main import ( "fmt" "composition/store" ) func main() { kayak := store.NewProduct("Kayak", "Watersports", 279) type OfferBundle struct { *store.SpecialDeal *store.Product } bundle := OfferBundle { store.NewSpecialDeal("Weekend Special", kayak, 50), store.NewProduct("Lifrejacket", "Watersports", 48.95), } fmt.Println("Price:", bundle.Price(0)) } Output: .\main.go:22:33: ambiguous selector bundle.Price `, "133": `133.Composition and Interfaces example: Mixing Types in the main.go: package main import ( "fmt" "composition/store" ) func main() { products := map[string]*store.Product { "Kayak": store.NewBoat("Kayak", 279, 1, false), "Ball": store.NewProduct("Soccer Ball", "Soccer", 19.50), } for , p := range products { fmt.Println("Name:", p.Name, "Category:", p.Category, "Price:", p.Price(0.2)) } } Output: .\main.go:11:9: cannot use store.NewBoat("Kayak", 279, 1, false) (type *store.Boat) as type *store.Product in map value `, "134": `134.Composition to Implement Interfaces Go takes promoted methods into account when determining whether a type conforms to an interface, which avoids the need to duplicate methods that are already present through an embedded field. add a file named forsale.go to the store folder: The Contents of the forsale.go: package store type ItemForSale interface { Price(taxRate float64) float64 } Using an Interface in the main.go: package main import ( "fmt" "composition/store" ) func main() { products := map[string]store.ItemForSale { "Kayak": store.NewBoat("Kayak", 279, 1, false), "Ball": store.NewProduct("Soccer Ball", "Soccer", 19.50), } for key, p := range products { fmt.Println("Key:", key, "Price:", p.Price(0.2)) } } Output: Key: Kayak Price: 334.8 Key: Ball Price: 23.4 `, "135": `135.the Type Switch Limitation example: main.go: package main import ( "fmt" "composition/store" ) func main() { products := map[string]store.ItemForSale { "Kayak": store.NewBoat("Kayak", 279, 1, false), "Ball": store.NewProduct("Soccer Ball", "Soccer", 19.50), } for key, p := range products { switch item := p.(type) { case *store.Product, *store.Boat: fmt.Println("Name:", item.Name, "Category:", item.Category, "Price:", item.Price(0.2)) default: fmt.Println("Key:", key, "Price:", p.Price(0.2)) } } } Output: .\main.go:21:42: item.Name undefined (type store.ItemForSale has no field or method Name) .\main.go:21:66: item.Category undefined (type store.ItemForSale has no field or method Category) Using Separate case Statements in the main.go: package main import ( "fmt" "composition/store" ) func main() { products := map[string]store.ItemForSale { "Kayak": store.NewBoat("Kayak", 279, 1, false), "Ball": store.NewProduct("Soccer Ball", "Soccer", 19.50), } for key, p := range products { switch item := p.(type) { case *store.Product: fmt.Println("Name:", item.Name, "Category:", item.Category, "Price:", item.Price(0.2)) case *store.Boat: fmt.Println("Name:", item.Name, "Category:", item.Category, "Price:", item.Price(0.2)) default: fmt.Println("Key:", key, "Price:", p.Price(0.2)) } } } Output: Name: Kayak Category: Watersports Price: 334.8 Name: Soccer Ball Category: Soccer Price: 23.4 `, "136": `136.the Type Switch Limitation An alternative solution example: Defining an Interface in the product.go: package store type Product struct { Name, Category string price float64 } func NewProduct(name, category string, price float64) *Product { return &Product{ name, category, price } } func (p *Product) Price(taxRate float64) float64 { return p.price + (p.price * taxRate) } type Describable interface { GetName() string GetCategory() string } func (p *Product) GetName() string { return p.Name } func (p *Product) GetCategory() string { return p.Category } Using Interfaces in the main.go: package main import ( "fmt" "composition/store" ) func main() { products := map[string]store.ItemForSale { "Kayak": store.NewBoat("Kayak", 279, 1, false), "Ball": store.NewProduct("Soccer Ball", "Soccer", 19.50), } for key, p := range products { switch item := p.(type) { case store.Describable: fmt.Println("Name:", item.GetName(), "Category:", item.GetCategory(), "Price:", item.(store.ItemForSale).Price(0.2)) default: fmt.Println("Key:", key, "Price:", p.Price(0.2)) } } } Output: Name: Kayak Category: Watersports Price: 334.8 Name: Soccer Ball Category: Soccer Price: 23.4 `, "137": `137.Composing Interfaces Go allows interfaces to be composed from other interfaces example: Composing an Interface in the product.go: package store type Product struct { Name, Category string price float64 } func NewProduct(name, category string, price float64) *Product { return &Product{ name, category, price } } func (p *Product) Price(taxRate float64) float64 { return p.price + (p.price * taxRate) } type Describable interface { GetName() string GetCategory() string ItemForSale } func (p *Product) GetName() string { return p.Name } func (p *Product) GetCategory() string { return p.Category } `, "138": `138.Goroutines and Channels What are they? Goroutines are lightweight threads created and managed by the Go runtime. Channels are pipes that carry values of a specific type. Why are they useful? Goroutines allow functions to be executed concurrently, without needing to deal with the complications of operating system threads. Channels allow goroutines to produce results asynchronously. How are they used? Goroutines are created using the go keyword. Channels are defined as data types. Are there any or limitations? pitfalls Care must be taken to manage the direction of channels. Goroutines that share data require additional features. Are there any alternatives? Goroutines and channels are the built-in Go concurrency features, but some applications can rely on a single thread of execution, which is created by default to execute the main function. Receiving from a channel is a blocking operation, meaning that execution will not continue until a value has been received Problem Solution --------------------------------- ------------------------- Execute a function asynchronously Create a goroutine Produce a result from a function Use a channel executed asynchronously Send and receive values Use arrow expressions using a channel Indicate that no further values Use the close function will be sent over a channel Enumerate the values received Use a for loop with the range keyword from a channel `, "139": `139.How Go Executes Code All Go programs use at least one goroutine because this is how Go executes the code in the main function. When compiled Go code is executed, the runtime creates a goroutine that starts executing the statements in the entry point, which is the main function in the main package. Each statement in the main function is executed in the order in which they are defined. The goroutine keeps executing statements until it reaches the end of the main function, at which point the application terminates. The goroutine executes each statement in the main function synchronously, which means that it waits for the statement to complete before moving on to the next statement. The statements in the main function `, "140": `140.Creating Additional Goroutines Go allows the developer to create additional goroutines, which execute code at the same time as the main goroutine. Go makes it easy to create new goroutines example: package main import ( "fmt" "time ) func main(){ fmt.Println("first statement") fmt.Println("second statement") time.Sleep(time.Second * 2) go fmt.Println("first statement") fmt.Println("first statement") } `, "141": `141.Returning Results from Goroutines Receiving from a channel is a blocking operation, meaning that execution will not continue until a value has been received Getting a result from a function that is being executed asynchronously can be complicated because it requires coordination between the goroutine that produces the result and the goroutine that consumes the result. To address this issue, Go provides channels, which are conduits through which data can be sent and received. Defining a Channel: example: var channel chan float64 = make(chan float64) `, "142": `142.Sending a Result Using a Channel Receiving from a channel is a blocking operation, meaning that execution will not continue until a value has been received example: resultChannel <- total `, "143": `143.Receiving a Result Using a Channel Receiving from a channel is a blocking operation, meaning that execution will not continue until a value has been received example: storeTotal += <- channel `, "144": `144.using adapters to execute functions asynchronously It isn't always possible to rewrite existing functions or methods to use channels, but it is a simple matter to execute synchronous functions asynchronously in a wrapper, like this: The syntax is a little awkward because the arguments used to invoke the function are expressed immediately following the function definition. But the result is the same, which is that a synchronous function can be executed by a goroutine with the result being sent through a channel. example: ... calcTax := func(price float64) float64 { return price + (price * 0.2) } wrapper := func (price float64, c chan float64) { c <- calcTax(price) } resultChannel := make(chan float64) go wrapper(275, resultChannel) result := <- resultChannel fmt.Println("Result:", result) ... The wrapper function receives a channel, which it uses to send the value received from executing the calcTax function synchronously. This can be expressed more concisely by defining a function without assigning it to a variable, like this: ... go func (price float64, c chan float64) { c <- calcTax(price) }(275, resultChannel) `, "145": `145.Coordinating Channels By default, sending and receiving through a channel are blocking operations. This means a goroutine that sends a value will not execute any further statements until another goroutine receives the value from the channel. If a second goroutine sends a value, it will be blocked until the channel is cleared, causing a queue of goroutines waiting for values to be received. This happens in the other direction, too, so that goroutines that receive values will block until another goroutine sends one. example: If Bob has a message for Alice, the default channel behavior requires Alice and Bob to agree on a meeting place, and whoever gets there first will wait for the other to arrive. Bob will only give the message to Alice when they are both present. When Charlie also has a message for Alice, he will form a queue behind Bob. Everyone waits patiently, messages are transferred only when the sender and receiver are both available, and messages are processed sequentially. `, "146": `146.Buffered Channel The default channel behavior can lead to bursts of activity as goroutines do their work, followed by a long idle period waiting for messages to be received. This doesn't have an impact on the example application because the goroutines finish once their messages are received, but in a real project goroutines often have repetitive tasks to perform, and waiting for a receiver can cause a performance bottleneck. An alternative approach is to create a channel with a buffer, which is used to accept values from a sender and store them until a receiver becomes available. This makes sending a message a nonblocking operation, allowing a sender to pass its value to the channel and continue working without having to wait for a receiver. example: This is similar to Alice having an inbox on her desk. Senders come to Alice's office and put their message into the inbox, leaving it for Alice to read when she is ready. But, if the inbox is full, then they will have to wait until she has processed some of her backlog before sending a new message. Creating a Buffered Channel example: var channel chan float64 = make(chan float64, 2) Result explain: For this example, I have set the size of the buffer to 2, meaning that two senders will be able to send values through the channel without having to wait for them to be received. `, "147": `147.Inspecting a Channel Buffer You can determine the size of a channel's buffer using the built-in cap function and determine how many values are in the buffer using the len function The modified statement uses the len and cap functions to report the number of values in the channel's buffer and the overall size of the buffer. example: len(channel), "items in buffer, size", cap(channel)) `, "148": `148.Closing a Channel The solution for this problem is for the sender to indicate when no further values are coming through the channel, which is done by closing the channel The built-in close function accepts a channel as its argument and is used to indicate that there will be no further values sent through the channel. Receivers can check if a channel is closed when requesting a value. You need to close channels only when it is helpful to do so to coordinate your goroutines. Go doesn't require channels to be closed to free up resources or perform any kind of housekeeping task. example: close(channel) The receive operator can be used to obtain two values. The first value is assigned the value received from the channel, and the second value indicates whether the channel is closed It is illegal to send values to a channel once it has been closed. example: if details, open := <- dispatchChannel; open { fmt.Println("Dispatch to", details.Customer, ":", details.Quantity, "x", details.Product.Name) } else { fmt.Println("Channel has been closed") break } `, "149": `149.Enumerating Channel Values A for loop can be used with the range keyword to enumerate the values sent through a channel, allowing the values to be received more easily and terminating the loop when the channel is closed The range expression produces one value per iteration, which is the value received from the channel. The for loop will continue to receive values until the channel is closed. (You can use a for...range loop on a channel that isn't closed, in which case the loop will never exit.) example: go DispatchOrders(dispatchChannel) for details := range dispatchChannel { fmt.Println("Dispatch to", details.Customer, ":", details.Quantity, "x", details.Product.Name) } fmt.Println("Channel has been closed") `, "150": `150.Restricting Channel Direction By default, channels can be used to send and receive data, but this can be restricted when using channels as arguments, such that only send or receive operations can be performed. example: func DispatchOrders(channel chan<- DispatchNotification) The location of the arrow specifies the direction of the channel. When the arrow follows the chan keyword, then the channel can be used only to send. The channel can be used to receive only if the arrow precedes the chan keyword (<-chan, for example). Attempting to receive from a send-only (and vice versa) channel is a compile-time error, example: # concurrency .\orderdispatch.go:29:29: invalid operation: <-channel (receive from send-only type chan<- DispatchNotification) `, "151": `151.Restricting Channel Argument Direction Go allows bidirectional channels to be assigned to unidirectional channel variables, allowing restrictions to be applied example: func receiveDispatches(channel <-chan DispatchNotification) { for details := range channel { fmt.Println("Dispatch to", details.Customer, ":", details.Quantity, "x", details.Product.Name) } fmt.Println("Channel has been closed") } Restrictions on channel direction can also be created through explicit conversion The explicit conversion for the receive-only channel requires parentheses around the channel type to prevent the compiler from interpreting a conversion to the DispatchNotification type. example: func main() { dispatchChannel := make(chan DispatchNotification, 100) go DispatchOrders(chan<- DispatchNotification(dispatchChannel)) receiveDispatches((<-chan DispatchNotification)(dispatchChannel)) } `, "152": `152.Select Statements The select keyword is used to group operations that will send or receive from channels, which allows for complex arrangements of goroutines and channels to be created. There are several uses for select statements. The simplest use for select statements is to receive from a channel without blocking, ensuring that a goroutine won't have to wait when the channel is empty. A select statement has a similar structure to a switch statement, except that the case statements are channel operations. When the select statement is executed, each channel operation is evaluated until one that can be performed without blocking is reached. The channel operation is performed, and the statements enclosed in the case statement are executed. If none of the channel operations can be performed, the statements in the default clause are executed. example: for { select { case details, ok := <- dispatchChannel: if ok { fmt.Println("Dispatch to", details.Customer, ":", details.Quantity, "x", details.Product.Name) } else { fmt.Println("Channel has been closed") goto alldone } default: fmt.Println("-- No message ready to be received") time.Sleep(time.Millisecond * 500) } } alldone: fmt.Println("All values received") `, "153": `153.Receiving from Multiple Channels A select statement can be used to receive without blocking, when there are multiple channels, through which values are sent at different rates. A select statement will allow the receiver to obtain values from whichever channel has them, without blocking on any single channel In this example, the select statement is used to receive values from two channels, one that carries DispatchNofitication values and one that carries Product values. Each time the select statement is executed, it works its way through the case statements, building up a list of the ones from which a value can be read without blocking. One of the case statements is selected from the list at random and executed. If none of the case statements can be performed, the default clause is executed. example: package main import ( "fmt" "time" ) func enumerateProducts(channel chan<- *Product) { for , p := range ProductList[:3] { channel <- p time.Sleep(time.Millisecond * 800) } close(channel) } func main() { dispatchChannel := make(chan DispatchNotification, 100) go DispatchOrders(chan<- DispatchNotification(dispatchChannel)) productChannel := make(chan *Product) go enumerateProducts(productChannel) openChannels := 2 for { select { case details, ok := <- dispatchChannel: if ok { fmt.Println("Dispatch to", details.Customer, ":", details.Quantity, "x", details.Product.Name) } else { fmt.Println("Dispatch channel has been closed") dispatchChannel = nil openChannels-- } case product, ok := <- productChannel: if ok { fmt.Println("Product:", product.Name) } else { fmt.Println("Product channel has been closed") productChannel = nil openChannels-- } default: if (openChannels == 0) { goto alldone } fmt.Println("-- No message ready to be received") time.Sleep(time.Millisecond * 500) } } alldone: fmt.Println("All values received") } `, "154": `154.Sending Without Blocking A select statement can also be used to send to a channel without blocking example: func enumerateProducts(channel chan<- *Product) { for , p := range ProductList { select { case channel <- p: fmt.Println("Sent product:", p.Name) default: fmt.Println("Discarding product:", p.Name) time.Sleep(time.Second) } } close(channel) } `, "155": `155.Sending to Multiple Channels If there are multiple channels available, a select statement can be used to find a channel for which sending will not block You can combine case statements with send and receive operations in the same select statement. When the select statement is executed, the Go runtime builds a combined list of case statements that can be executed without blocking and picks one at random, which can be either a send or a receive statement. This example has two channels with small buffers. As with receiving, the select statement builds a list of the channels through which a value can be sent without blocking and then picks one at random from that list. If none of the channels can be used, then the default clause is executed. There is no default clause in this example, which means that the select statement will block until one of the channels can receive a value. example: func enumerateProducts(channel1, channel2 chan<- *Product) { for , p := range ProductList { select { case channel1 <- p: fmt.Println("Send via channel 1") case channel2 <- p: fmt.Println("Send via channel 2") } } close(channel1) close(channel2) } `, "156": `156.Error Handling What is it? Go's error handling allows exceptional conditions and failures to be represented and dealt with. Why is it useful? Applications will often encounter unexpected situations, and the error handling features provide a way to respond to those situations when they arise. How is it used? The error interface is used to define error conditions, which are typically returned as function results. The panic function is called when an unrecoverable error occurs. Are there any pitfalls or limitations? Care must be taken to ensure that errors are communicated to the part of the application that can best decide how serious the situation is. Are there any alternatives? You don't have to use the error interface in your code, but it is employed throughout the Go standard library and is difficult to avoid. Go provides a predefined interface named error that provides one way to resolve this issue. Here is the definition of the interface: type error interface { Error() string } Problem Solution ---------------------------------- --------------------------------------------------- Indicate that an error has occurred Create a struct that implements the error interface and return it as a function result Report an error over a channel Add an error field to the struct type used for channel messages Indicate that an unrecoverable Call the panic function error has occurred Recover from a panic Use the defer keyword to register a function that calls the recover function `, "157": `157.Generating Errors Functions and methods can express exceptional or unexpected outcomes by producing error responses Defining an Error example: package main import "fmt" func main() { categories := []string { "Watersports", "Chess", "Running" } for , cat := range categories { total, err := Products.TotalPrice(cat) if (err == nil) { fmt.Println(cat, "Total:", ToCurrency(total)) } else { fmt.Println(cat, "(no such category)") } } } Output: Watersports Total: $328.95 Chess Total: $1291.00 Running (no such category) `, "158": `158.Ignoring Error Results I don't recommend ignoring error results because it means you will lose important information. but if you don't need to know when something goes wrong, then you can use the blank identifier instead of a name for the error result, like this: example: package main import "fmt" func main() { categories := []string { "Watersports", "Chess", "Running" } for , cat := range categories { total, := Products.TotalPrice(cat) fmt.Println(cat, "Total:", ToCurrency(total)) } } `, "159": `159.Reporting Errors via Channels example: operations.go: package main type CategoryError struct { requestedCategory string } func (e *CategoryError) Error() string { return "Category " + e.requestedCategory + " does not exist" } type ChannelMessage struct { Category string Total float64 *CategoryError } func (slice ProductSlice) TotalPrice(category string) (total float64, err *CategoryError) { productCount := 0 for , p := range slice { if (p.Category == category) { total += p.Price productCount++ } } if (productCount == 0) { err = &CategoryError{ requestedCategory: category} } return } func (slice ProductSlice) TotalPriceAsync (categories []string, channel chan<- ChannelMessage) { for , c := range categories { total, err := slice.TotalPrice(c) channel <- ChannelMessage{ Category: c, Total: total, CategoryError: err, } } close(channel) } main.go: package main import "fmt" func main() { categories := []string { "Watersports", "Chess", "Running" } channel := make(chan ChannelMessage, 10) go Products.TotalPriceAsync(categories, channel) for message := range channel { if message.CategoryError == nil { fmt.Println(message.Category, "Total:", ToCurrency(message.Total)) } else { fmt.Println(message.Category, "(no such category)") } } } Output: Watersports Total: $328.95 Chess Total: $1291.00 Running (no such category) `, "160": `160.String Processing and Regular Expressions What are they? String processing includes a wide range of operations, from trimming whitespace to splitting a string into components. Regular expressions are patterns that allow string matching rules to be concisely defined. Why are they useful? These operations are useful when an application needs to process string values. A common example is processing HTTP requests. How are they used? These features are contained in the strings and regexp packages, which are part of the standard library. Are there any pitfalls or limitations? There are some quirks in the way that some of these operations are performed, but they mostly behave as you would expect. Are there any alternatives? The use of these packages is optional, and they do not have to be used. That said, there is little point in creating your own implementations of these features since the standard library is well- written and thoroughly tested. Problem Solution ------------------- ------------------------------------------------------------------- Compare strings Use the Contains, EqualFold, or Has* function in the strings package Convert string case Use the ToLower, ToUpper, Title, or ToTitle function in the strings package Check or change Use the functions provided by the unicode package character case Find content in strings Use the functions provided by the strings or regexp package Split a string Use the Fields or Split* function in the strings and regexp packages Join strings Use the Join or Repeat function in the strings package Trim characters from Use the Trim* functions in the strings package a string Perform a substitution Use the Replace* or Map function in the strings package, تعویض انجام دهید use a Replacer, or use the Replace* functions in the regexp package Efficiently build a Use the Builder type in the strings package string `, "161": `161.Comparing Strings The strings Functions for Comparing Strings Function Description ------------- ------------------------ Contains(s, substr) This function returns true if the string s contains substr and false if it does not. ContainsAny(s, substr) This function returns true if the string s contains any of the characters contained in the string substr. ContainsRune(s, rune) This function returns true if the string s contains a specific rune. EqualFold(s1, s2) This function performs a case-insensitive comparison and returns true of strings s1 and s2 are the same. HasPrefix(s, prefix) This function returns true if the string s begins with the string prefix. HasSuffix(s, suffix) This function returns true if the string ends with the string suffix. example: package main import ( "fmt" "strings" ) func main() { product := "Kayak" fmt.Println("Contains:", strings.Contains(product, "yak")) fmt.Println("ContainsAny:", strings.ContainsAny(product, "abc")) fmt.Println("ContainsRune:", strings.ContainsRune(product, 'K')) fmt.Println("EqualFold:", strings.EqualFold(product, "KAYAK")) fmt.Println("HasPrefix:", strings.HasPrefix(product, "Ka")) fmt.Println("HasSuffix:", strings.HasSuffix(product, "yak")) } Output: Contains: true ContainsAny: true ContainsRune: true HasPrefix: true HasSuffix: true EqualFold: true `, "162": `162.Using The Byte-Oriented Functions example: package main import ( "fmt" "strings" "bytes" ) func main() { price := "€100" fmt.Println("Strings Prefix:", strings.HasPrefix(price, "€")) fmt.Println("Bytes Prefix:", bytes.HasPrefix([]byte(price), []byte { 226, 130 })) } Output: Strings Prefix: true Bytes Prefix: true `, "163": `163.Converting String Case The Case Functions in the strings Package Function Description -------------- ------------------------------------------------------ ToLower(str) This function returns a new string containing the characters in the specified string mapped to lowercase. ToUpper(str) This function returns a new string containing the characters in the specified string mapped to lowercase. Title(str) This function converts the specific string so that the first character of each word is uppercase and the remaining characters are lowercase. ToTitle(str) This function returns a new string containing the characters in the specified string mapped to title case. Care must be taken with the Title and ToTitle functions, which don't work the way you might expect. The Title function returns a string that is suitable for use as a title, but it treats all words the same Creating a Title: example: package main import ( "fmt" "strings" ) func main() { description := "A boat for sailing" fmt.Println("Original:", description) fmt.Println("Title:", strings.Title(description)) fmt.Println("Title:", strings.ToTitle(description)) } Output: Original: A boat for sailing Title: A Boat For Sailing Title: A BOAT FOR SAILING `, "164": `164.Title Case example: package main import ( "fmt" "strings" ) func main() { specialChar := "\u01c9" fmt.Println("Original:", specialChar, []byte(specialChar)) upperChar := strings.ToUpper(specialChar) fmt.Println("Upper:", upperChar, []byte(upperChar)) titleChar := strings.ToTitle(specialChar) fmt.Println("Title:", titleChar, []byte(titleChar)) } Output: Original: lj [199 137] Upper: LJ [199 135] Title: Lj [199 136] `, "165": `165.Working with Character Case The unicode package provides functions that can be used to determine or change the case of individual characters Functions in the unicode Package for Character Case Function Description ------------- ----------------------------------------------------------------- IsLower(rune) This function returns true if the specified rune is lowercase. ToLower(rune) This function returns the lowercase rune associated with the specified rune. IsUpper(rune) This function returns true if the specified rune is uppercase. ToUpper(rune) This function returns the upper rune associated with the specified rune. IsTitle(rune) This function returns true if the specified rune is title case. ToTitle(rune) This function returns the title case rune associated with the specified rune. `, "166": `166.the Rune Case Functions example: package main import ( "fmt" "unicode" ) func main() { product := "Kayak" for _, char := range product { fmt.Println(string(char), "Upper case:", unicode.IsUpper(char)) } } Output: K Upper case: true a Upper case: false y Upper case: false a Upper case: false k Upper case: false `, "167": `167.Inspecting Strings The strings Functions for Inspecting Strings Function Description ------------ -------------------------------------------------- Count(s, sub) This function returns an int that reports how many times the specified substring is found in the string s. Index(s, sub) These functions return the index of the first or last occurrence of a specified LastIndex(s, sub) substring string within the string s, or -1 if there is no occurrence. IndexAny(s, chars) These functions return the first or last occurrence of any character in the LastIndexAny(s, chars) specified string within the string s, or -1 if there is no occurrence. IndexByte(s, b) These functions return the index of the first or last occurrence of a specified LastIndexByte(s, b) byte within the string s, or -1 if there is no occurrence. IndexFunc(s, func) These functions return the index of the first or last occurrence of the LastIndexFunc(s, func) character in the string s for which the specified function returns true, as described in the “Inspecting Strings with Custom Functions” section. example: package main import ( "fmt" "strings" //"unicode" ) func main() { description := "A boat for one person" fmt.Println("Count:", strings.Count(description, "o")) fmt.Println("Index:", strings.Index(description, "o")) fmt.Println("LastIndex:", strings.LastIndex(description, "o")) fmt.Println("IndexAny:", strings.IndexAny(description, "abcd")) fmt.Println("LastIndex:", strings.LastIndex(description, "o")) fmt.Println("LastIndexAny:", strings.LastIndexAny(description, "abcd")) } Output: Count: 4 Index: 3 LastIndex: 19 IndexAny: 2 LastIndex: 19 LastIndexAny: 4 `, "168": `168.IndexFunc and LastIndexFunc functions Inspecting Strings with Custom Functions The IndexFunc and LastIndexFunc functions use a custom function to inspect strings, using custom functions Custom functions receive a rune and return a bool result that indicates if the character meets the desired condition. The IndexFunc function invokes the custom function for each character in the string until a true result is obtained, at which point the index is returned. The isLetterB variable is assigned a custom function that receives a rune and returns true if the rune is a uppercase or lowercase B. The custom function is passed to the strings.IndexFunc function example: package main import ( "fmt" "strings" ) func main() { description := "A boat for one person" isLetterB := func (r rune) bool { return r == 'B' || r == 'b' } fmt.Println("IndexFunc:", strings.IndexFunc(description, isLetterB)) } Output: IndexFunc: 2 `, "169": `169.Splitting Strings The Functions for Splitting Strings in the strings Package Function Description --------------- -------------------------------- Fields(s) This function splits a string on whitespace characters and returns a slice containing the nonwhitespace sections of the string s. FieldsFunc(s, func) This function splits the string s on the characters for which a custom function returns true and returns a slice containing the remaining sections of the string. Split(s, sub) This function splits the string s on every occurrence of the specified substring, returning a string slice. If the separator is the empty string, then the slice will contain strings for each character. SplitN(s, sub, max) This function is similar to Split, but accepts an additional int argument that specifies the maximum number of substrings to return. The last substring in the result slice will contain the unsplit portion of the source string. SplitAfter(s, sub) This function is similar to Split but includes the substring used in the results. SplitAfterN(s, sub, max) This function is similar to SplitAfter, but accepts an additional int argument that specifies the maximum number of substrings to return. example: package main import ( "fmt" "strings" ) func main() { description := "A boat for one person" splits := strings.Split(description, " ") for _, x := range splits { fmt.Println("Split >>" + x + "<<") } splitsAfter := strings.SplitAfter(description, " ") for _, x := range splitsAfter { fmt.Println("SplitAfter >>" + x + "<<") } } Output: Split >>A<< Split >>boat<< Split >>for<< Split >>one<< Split >>person<< SplitAfter >>A << SplitAfter >>boat << SplitAfter >>for << SplitAfter >>one << SplitAfter >>person<< `, "170": `170.SplitN and SplitAfterN functions Restricting the Number of Results The SplitN and SplitAfterN functions accept an int argument that specifies the maximum number of results that should be included in the results Restricting the Results example: package main import ( "fmt" "strings" ) func main() { description := "A boat for one person" splits := strings.SplitN(description, " ", 3) for _, x := range splits { fmt.Println("Split >>" + x + "<<") } } Output: Split >>A<< Split >>boat<< Split >>for one person<< `, "171": `171.strings.SplitN function Splitting on Whitespace Characters One limitation of the Split, SplitN, SplitAfter, and SplitAfterN functions is they do not deal with repeated sequences of characters, which can be a problem when splitting a string on whitespace characters The words in the source string are double-spaced, but the SplitN function splits only on the first space character, which produces odd results. example: package main import ( "fmt" "strings" ) func main() { description := "This is double spaced" splits := strings.SplitN(description, " ", 3) for _, x := range splits { fmt.Println("Split >>" + x + "<<") } } Output: Split >>This<< Split >><< Split >>is double spaced<< `, "172": `172.Fields Function The Fields function doesn't support a limit on the number of results but does deal with the double spaces properly. The Fields function has a better approach, which is to split on any character for which the IsSpace function in the unicode package returns true. example: package main import ( "fmt" "strings" ) func main() { description := "This is double spaced" splits := strings.Fields(description) for _, x := range splits { fmt.Println("Field >>" + x + "<<") } } Output: Field >>This<< Field >>is<< Field >>double<< Field >>spaced<< `, "173": `173.FieldsFunc function Splitting Using a Custom Function to Split Strings The FieldsFunc function splits a string by passing each character to a custom function and splitting when that function returns true The custom function receives a rune and returns true if that rune should cause the string to split. The FieldsFunc function is smart enough to deal with repeated characters example: package main import ( "fmt" "strings" ) func main() { description := "This is double spaced" splitter := func(r rune) bool { return r == ' ' } splits := strings.FieldsFunc(description, splitter) for _, x := range splits { fmt.Println("Field >>" + x + "<<") } } Output: Field >>This<< Field >>is<< Field >>double<< Field >>spaced<< `, "174": `174.Trimming Strings The Functions for Trimming Strings in the strings Package Function Description ------------ --------------------------------------- TrimSpace(s) This function returns the string s without leading or trailing whitespace characters. Trim(s, set) This function returns a string from which any leading or trailing characters contained in the string set are removed from the string s. TrimLeft(s, set) This function returns the string s without any leading character contained in the string set. This function matches any of the specified characters—use the TrimPrefix function to remove a complete substring. TrimRight(s, set) This function returns the string s without any trailing character contained in the string set. This function matches any of the specified characters—use the TrimSuffix function to remove a complete substring. TrimPrefix(s, prefix) This function returns the string s after removing the specified prefix string. This function removes the complete prefix string—use the TrimLeft function to remove characters from a set. TrimSuffix(s, suffix) This function returns the string s after removing the specified suffix string. This function removes the complete suffix string—use the TrimRight function to remove characters from a set. TrimFunc(s, func) This function returns the string s from which any leading or trailing character for which a custom function returns true are removed. TrimLeftFunc(s, func) This function returns the string s from which any leading character for which a custom function returns true are removed. TrimRightFunc(s, func) This function returns the string s from which any trailing character for which a custom function returns true are removed. `, "175": `175.TrimSpace function Trimming Whitespace example: package main import ( "fmt" "strings" ) func main() { username := " Alice" trimmed := strings.TrimSpace(username) fmt.Println("Trimmed:", ">>" + trimmed + "<<") } Output: Trimmed: >>Alice<< `, "176": `176.Trim, TrimLeft, and TrimRight functions example: package main import ( "fmt" "strings" ) func main() { description := "A boat for one person" trimmed := strings.Trim(description, "Anor ") fmt.Println("Trimmed:>>"+ trimmed+ "<<") } Ourput: Trimmed:>>boat for one pers<< `, "177": `177.TrimPrefix and TrimSuffix functions example: package main import ( "fmt" "strings" ) func main() { description := "A boat for one person" prefixTrimmed := strings.TrimPrefix(description, "A boat ") wrongPrefix := strings.TrimPrefix(description, "A hat ") fmt.Println("Trimmed:", prefixTrimmed) fmt.Println("Not trimmed:", wrongPrefix) } Output: Trimmed: for one person Not trimmed: A boat for one person `, "178": `178.TrimFunc, TrimLeftFunc, and TrimRightFunc functions example: package main import ( "fmt" "strings" ) func main() { description := "A boat for one person" trimmer := func(r rune) bool { return r == 'A' || r == 'n' } trimmed := strings.TrimFunc(description, trimmer) fmt.Println("Trimmed:", trimmed) } Output: Trimmed: boat for one person `, "179": `179.Altering Strings The Functions for Altering Strings in the strings Package Function Description ---------------- ----------------------------------- Replace(s, old, new, n) This function alters the string s by replacing occurrences of the string old with the string new. The maximum number of occurrences that will be replaced is specified by the int argument n. ReplaceAll(s, old, new) This function alters the string s by replacing all occurrences of the string old with the string new. Unlike the Replace function, there is no limit on the number of occurrences that will be replaced. Map(func, s) This function generates a string by invoking the custom function for each character in the string s and concatenating the results. If the function produces a negative value, the current character is dropped without a replacement. `, "180": `180.Replace and ReplaceAll functions The Replace function allows a maximum number of changes to be specified, while the ReplaceAll function will replace all the occurrences of the substring it finds example: package main import ( "fmt" "strings" ) func main() { text := "It was a boat. A small boat." replace := strings.Replace(text, "boat", "canoe", 1) replaceAll := strings.ReplaceAll(text, "boat", "truck") fmt.Println("Replace:", replace) fmt.Println("Replace All:", replaceAll) } Output: Replace: It was a canoe. A small boat. Replace All: It was a truck. A small truck. `, "181": `181.Map function example: package main import ( "fmt" "strings" ) func main() { text := "It was a boat. A small boat." mapper := func(r rune) rune { if r == 'b' { return 'c' } return r } mapped := strings.Map(mapper, text) fmt.Println("Mapped:", mapped) } Output: Mapped: It was a coat. A small coat. `, "182": `182.NewReplacer function The strings package exports a struct type named Replacer that is used to replace strings example: package main import ( "fmt" "strings" ) func main() { text := "It was a boat. A small boat. 111" replacer := strings.NewReplacer("boat", "kayak", "small", "huge", "111", "222", ) replaced := replacer.Replace(text) fmt.Println("Replaced:", replaced) } Output: Replaced: It was a kayak. A huge kayak. 222`, "183": `183.The Replacer Methods Name Description ------------------- -------------------------------------------- Replace(s) This method returns a string for which all the replacements specified with the constructor have been performed on the string s. WriteString(writer, s) This method is used to perform the replacements specified with the constructor and write the results to an io.Writer `, "184": `184.Building and Generating Strings The strings Functions for Generating Strings Function Description ---------------- ---------------------------------------------------------------------------------------- Join(slice, sep) This function combines the elements in the specified string slice, with the specified separator string placed between elements. Repeat(s, count) This function generates a string by repeating the string s for a specified number of times. `, "185": `185.Join and Repeat functions example: package main import ( "fmt" "strings" ) func main() { text := "It was a boat. A small boat." elements := strings.Fields(text) joined := strings.Join(elements, "--") fmt.Println("Joined:", joined) esplited := strings.Split(text, " ") fmt.Printf("%q\n",esplited) } Output: Joined: It--was--a--boat.--A--small--boat. ["It" "was" "a" "boat." "A" "small" "boat."] `, "186": `186.Building Strings The strings.Builder Methods Name Description --------------- -------------------------------------------- WriteString(s) This method appends the string s to the string being built. WriteRune(r) This method appends the character r to the string being built. WriteByte(b) This method appends the byte b to the string being built. String() This method returns the string that has been created by the builder. Reset() This method resets the string created by the builder. Len() This method returns the number of bytes used to store the string created by the builder. Cap() This method returns the number of bytes that have been allocated by the builder. Grow(size) This method increases the number of bytes used allocated by the builder to store the string that is being built. `, "187": `187.builder.String() Creating the string using the Builder is more efficient than using the concatenation operator on regular string values, especially if the Grow method is used to allocate storage in advance. Care must be taken to use pointers when passing Builder values to and from functions and methods; otherwise, the efficiency gains will be lost when the Builder is copied. example: package main import ( "fmt" "strings" ) func main() { text := "It was a boat. A small boat." var builder strings.Builder for _, sub := range strings.Fields(text) { if (sub == "small") { builder.WriteString("very ") } builder.WriteString(sub) builder.WriteRune(' ') } fmt.Println("String:", builder.String()) } `, "188": `188.new() function example: package main import "fmt" func main() { // 1. var ChannelName1 string = "AcronProject" fmt.Println("Value=", ChannelName1, "memory address=", &ChannelName1) // 2. var ChannelName2 *string = new(string) fmt.Println("Value ??? = ", ChannelName2, "memory address=", &ChannelName2) var ChannelName3 *string = new(string) fmt.Println("Value ??? = ", ChannelName3, "memory address=", &ChannelName3) fmt.Printf("%q \n",*ChannelName2 + *ChannelName3) } Output: Value= AcronProject memory address= 0xc000014070 Value ??? = 0xc000014090 memory address= 0xc00004e028 Value ??? = 0xc0000140a0 memory address= 0xc00004e030 "" `, "189": `189.Regular Expressions The regular expressions used in this section perform basic matches, but the regexp package supports an extensive pattern syntax, which is described at https://pkg.go.dev/regexp/syntax@go1.17.1. The Basic Functions Provided by the regexp Package Function Description ----------------- -------------------------------------------------------------------------- Match(pattern, b) This function returns a bool that indicates whether a pattern is matched by the byte slice b. MatchString(patten, s) This function returns a bool that indicates whether a pattern is matched by the string s. Compile(pattern) This function returns a RegExp that can be used to perform repeated pattern matching with the specified pattern. MustCompile(pattern) This function provides the same feature as Compile but panics, if the specified pattern cannot be compiled. example: package main import ( "fmt" //"strings" "regexp" ) func main() { description := "A boat for one person" match, err := regexp.MatchString("[A-z]oat", description) if (err == nil) { fmt.Println("Match:", match) } else { fmt.Println("Error:", err) } } Output: Match: true `, "190": `190.Compiling and Reusing Patterns The MatchString function is simple and convenient, but the full power of regular expressions is accessed through the Compile function `, "191": `191.regexp.Compile() function This is more efficient because the pattern has to be compiled only once. The result of the Compile function is an instance of the RegExp type, which defines the MatchString function. example: package main import ( "fmt" "regexp" ) func main() { pattern, compileErr := regexp.Compile("[A-z]oat") description := "A boat for one person" question := "Is that a goat?" preference := "I like oats" if (compileErr == nil) { fmt.Println("Description:", pattern.MatchString(description)) fmt.Println("Question:", pattern.MatchString(question)) fmt.Println("Preference:", pattern.MatchString(preference)) } else { fmt.Println("Error:", compileErr) } } Output: Description: true Question: true Preference: false `, "192": `192.Useful Basic Regexp Methods Function Description --------------- ---------------------------------------------------------------------- MatchString(s) This method returns true if the string s matches the compiled pattern. FindStringIndex(s) This method returns an int slice containing the location for the left- most match made by the compiled pattern in the string s. A nil result indicates that no matches were made. FindAllStringIndex(s, max) This method returns a slice of int slices that contain the location for all the matches made by the compiled pattern in the string s. A nil result indicates that no matches were made. FindString(s) This method returns a string containing the left-most match made by the compiled pattern in the string s. An empty string will be returned if no match is made. FindAllString(s, max) This method returns a string slice containing the matches made by the compiled pattern in the string s. The int argument max specifies the maximum number of matches, with -1 specifying no limit. A nil result is returned if there are no matches. Split(s, max) This method splits the string s using matches from the compiled pattern as separators and returns a slice containing the split substrings. example: package main import ( "fmt" "regexp" ) func getSubstring(s string, indices []int) string { return string(s[indices[0]:indices[1]]) } func main() { pattern := regexp.MustCompile("K[a-z]{4}|[A-z]oat") description := "Kayak. A boat for one person." firstIndex := pattern.FindStringIndex(description) allIndices := pattern.FindAllStringIndex(description, -1) fmt.Println("First index", firstIndex[0], "-", firstIndex[1], "=", getSubstring(description, firstIndex)) for i, idx := range allIndices { fmt.Println("Index", i, "=", idx[0], "-", idx[1], "=", getSubstring(description, idx)) } } Output: First index 0 - 5 = Kayak Index 0 = 0 - 5 = Kayak Index 1 = 9 - 13 = boat `, "193": `193.FindString and FindAllString methods If you dont need to know the location of the matches, then the FindString and FindAllString methods are more useful because their results are the substrings matched by the regular expression Getting Match Substrings example: package main import ( "fmt" "regexp" ) func main() { pattern := regexp.MustCompile("K[a-z]{4}|[A-z]oat") description := "Kayak. A boat for one person." firstMatch := pattern.FindString(description) allMatches := pattern.FindAllString(description, -1) fmt.Println("First match:", firstMatch) for i, m := range allMatches { fmt.Println("Match", i, "=", m) } } Output: First match: Kayak Match 0 = Kayak Match 1 = boat `, "194": `194.Splitting Strings Using a Regular Expression The Split method splits a string using the matches made by a regular expression, which can provide a more flexible alternative to the splitting functions example: package main import ( "fmt" "regexp" ) func main() { pattern := regexp.MustCompile(" |boat|one") description := "Kayak. A boat for one person." split := pattern.Split(description, -1) for _, s := range split { if s != "" { fmt.Println("Substring:", s) } } } Output: Substring: Kayak. Substring: A Substring: for Substring: person. example: package main import ( "fmt" "regexp" ) func main() { pattern := regexp.MustCompile(" |boat|one") description := "Kayak. A boat | | | | for one person." split := pattern.Split(description, -2) for _, s := range split { if s != "" { fmt.Println("Substring:", s) } } } Output: Substring: Kayak. Substring: A Substring: | Substring: | Substring: | Substring: | Substring: for Substring: person. `, "195": `195.Subexpressions Subexpressions allow parts of a regular expression to be accessed, which can make it easier to extract substrings from within a matched region. The pattern in this example matches a specific sentence structure. example: package main import ( "fmt" "regexp" ) func main() { pattern := regexp.MustCompile("A [A-z]* for [A-z]* person") description := "Kayak. A boat for one person." str := pattern.FindString(description) fmt.Println("Match:", str) } Output: Match: A boat for one person `, "196": `196.FindStringSubmatch method The FindStringSubmatch method performs the same task as FindString, but also includes the substrings matched by the expressions in its result. example: package main import ( "fmt" "regexp" ) func main() { pattern := regexp.MustCompile("A ([A-z]*) for ([A-z]*) person") description := "Kayak. A boat for one person." subs := pattern.FindStringSubmatch(description) for _, s := range subs { fmt.Println("Match:", s) } } Output: Match: A boat for one person Match: boat Match: one `, "197": `197.The Regexp Methods for Subexpressions Name Description --------------------------- ------------------------------------------------------------------ FindStringSubmatch(s) This method returns a slice containing the first match made by the pattern and the text for the subexpressions that the pattern defines. FindAllStringSubmatch(s, max) This method returns a slice containing all the matches and the text for the subexpressions. The int argument is used to specify the maximum number of matches. A value of -1 specifies all matches. FindStringSubmatchIndex(s) This method is equivalent to FindStringSubmatch but returns indices rather than substrings. FindAllStringSubmatchIndex (s, max) This method is equivalent to FindAllStringSubmatch but returns indices rather than substrings. NumSubexp() This method returns the number of subexpressions. SubexpIndex(name) This method returns the index of the subexpression with the specified name or -1 if there is no such subexpression. SubexpNames() This method returns the names of the subexpressions, expressed in the order in which they are defined. `, "198": `198.SubexpIndex(name) method the syntax for assigning names to subexpressions is awkward: within the parentheses, a question mark, followed by an uppercase P, followed by the name within angle brackets. pattern := regexp.MustCompile("A (?P<type>[A-z]*) for (?P<capacity>[A-z]*) person") The subexpressions are given the names type and capacity. The SubexpIndex method returns the position of a named subexpression in the results, which allows me to get the substrings matched by the type and capacity subexpressions. example: package main import ( "fmt" "regexp" ) func main() { pattern := regexp.MustCompile( "A (?P<type>[A-z]*) for (?P<capacity>[A-z]*) person") description := "Kayak. A boat for one person." subs := pattern.FindStringSubmatch(description) for _, name := range []string { "type", "capacity" } { fmt.Println(name, "=", subs[pattern.SubexpIndex(name)]) } } Output: type = boat capacity = one `, "199": `199.Replacing Substrings Using a Regular Expression Name Description ----------------------------- --------------------------------------------------------- ReplaceAllString(s, template) This method replaces the matched portion of the string s with the specified template, which is expanded before it is included in the result to incorporate subexpressions. ReplaceAllLiteralString(s, sub) This method replaces the matched portion of the string s with the specified content, which is included in the result without being expanded for subexpressions. ReplaceAllStringFunc(s, func) This method replaces the matched portion of the string s with the result produced by the specified function. `, "200": `200.ReplaceAllString method The result from the ReplaceAllString method is a string with the replaced content. Notice that the template is responsible for only part of the result from the ReplaceAllString method, The first part of the description string—the word Kayak, followed by a period and a space, is not matched by the regular expression and is included in the result without being modified. ■ Tip Use the ReplaceAllLiteralString method if you want to replace content without the new substring being interpreted for subexpressions. example: package main import ( "fmt" "regexp" ) func main() { pattern := regexp.MustCompile( "A (?P<type>[A-z]*) for (?P<capacity>[A-z]*) person") description := "Kayak. A boat for one person." template := "(type: ${type}, capacity: ${capacity})" replaced := pattern.ReplaceAllString(description, template) fmt.Println(replaced) } Output: Kayak. (type: boat, capacity: one). `, "201": `201.ReplaceAllStringFunc method The ReplaceAllStringFunc method replaces the matched section of a string with content generated by a function example: package main import ( "fmt" "regexp" ) func main() { pattern := regexp.MustCompile( "A (?P<type>[A-z]*) for (?P<capacity>[A-z]*) person") description := "Kayak. A boat for one person." replaced := pattern.ReplaceAllStringFunc(description, func(s string) string { return "This is the replacement content" }) fmt.Println(replaced) } Output: Kayak. This is the replacement content. `, "202": `202.Formatting and Scanning Strings Formatting is the process of composing a new string from one or more data values, while scanning is the process of parsing values from a string. What are they? Formatting is the process of composing values into a string. Scanning is the process of parsing a string for the values it contains. Why are they useful? Formatting a string is a common requirement and is used to produce strings for everything from logging and debugging to presenting the user with information. Scanning is useful for extracting data from strings, such as from HTTP requests or user input. How are they used? Both sets of features are provided through functions defined in the fmt package. Are there any pitfalls or limitations? The templates used to format strings can be hard to read, and there is no built-in function that allows a formatted string to be created to which a newline character is appended automatically. Are there any alternatives? Larger amounts of text and HTML content can be generated using the template features. `, "203": `203.fmt package The fmt package provides functions for composing and writing strings. Some of these functions use writers, which are part of the Go support for input/output. The Basic fmt Functions for Composing and Writing Strings. Name Description -------------------------- -------------------------------------------------------------------- Print(...vals) This function accepts a variable number of arguments and writes out their values to the standard out. Spaces are added between values that are not strings. Println(...vals) This function accepts a variable number of arguments and writes out their values to the standard out, separated by spaces and followed by a newline character. Fprint(writer, ...vals) This function writes out a variable number of arguments to the specified writer, Spaces are added between values that are not strings. Fprintln(writer, ...vals) This function writes out a variable number of arguments to the specified writer followed by a newline character. Spaces are added between all values. `, "204": `204.Println and Fprintln functions The Println and Fprintln functions add spaces between all the values, but the Print and Fprint functions only add spaces between values that are not strings. since the Print function adds spaces only between pairs of nonstring values, the results are different. example: package main import "fmt" func main() { fmt.Println("Product:", 1234 , "Price:", 5555) fmt.Print("Product:", 1234 , "Price:", 5555, "\n") } Output: Product: 1234 Price: 5555 Product:1234Price:5555 `, "205": `205.fmt.Printf The Printf function accepts a template string and a series of values. The template is scanned for verbs, which are denoted by the percentage sign (the % character) followed by a format specifier. The first verb is %v, and it specifies the default representation for a type. For a string value, for example, %v simply includes the string in the output. The %4.2f verb specifies the format for a floating-point value, with 4 digits before the decimal point and 2 digits after. The values for the template verbs are taken from the remaining arguments, used in the order they are specified. For the example, this means the %v verb is used to format the Product.Name value, and the %4.2f verb is used to format the Product.Price value. These values are formatted, inserted into the template string, and written out to the console. example: package main import "fmt" type Product struct { Name, Category string Price float64 } var Kayak = Product { Name: "Kayak", Category: "Watersports", Price: 275, } var Products = []Product { { "Kayak", "Watersports", 279 }, { "Lifejacket", "Watersports", 49.95 }, { "Soccer Ball", "Soccer", 19.50 }, { "Corner Flags", "Soccer", 34.95 }, { "Stadium", "Soccer", 79500 }, { "Thinking Cap", "Chess", 16 }, { "Unsteady Chair", "Chess", 75 }, { "Bling-Bling King", "Chess", 1200 }, } func main() { fmt.Printf("Product: %v, Price: $%4.2f", Kayak.Name, Kayak.Price) } Output: Product: Kayak, Price: $275.00 `, "206": `206.The fmt Functions for Formatting Strings Name Description --------------------------- ----------------------------------------- Sprintf(t, ...vals) This function returns a string, which is created by processing the template t. The remaining arguments are used as values for the template verbs. Printf(t, ...vals) This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to the standard out. Fprintf(writer, t, ...vals) This function creates a string by processing the template t. The remaining arguments are used as values for the template verbs. The string is written to a Writer, which is described in Chapter 20. Errorf(t, ...values) This function creates an error by processing the template t. The remaining arguments are used as values for the template verbs. The result is an error value whose Error method returns the formatted string. `, "207": `207.fmt.Sprintf Both of the formatted strings in this example use the %v value, which writes out values in their default form. example: package main import "fmt" type Product struct { Name, Category string Price float64 } var Kayak = Product{ Name: "Kayak", Category: "Watersports", Price: 275, } var Products = []Product{ {"Kayak", "Watersports", 279}, {"Lifejacket", "Watersports", 49.95}, {"Soccer Ball", "Soccer", 19.50}, {"Corner Flags", "Soccer", 34.95}, {"Stadium", "Soccer", 79500}, {"Thinking Cap", "Chess", 16}, {"Unsteady Chair", "Chess", 75}, {"Bling-Bling King", "Chess", 1200}, } func getProductName(index int) (name string, err error) { if len(Products) > index { name = fmt.Sprintf("Name of product: %v", Products[index].Name) } else { err = fmt.Errorf("error for index %v", index) } return } func main() { name, _ := getProductName(1) fmt.Println(name) _, err := getProductName(10) fmt.Println(err.Error()) } Output: Name of product: Lifejacket error for index 10 `, "208": `208.the General-Purpose Formatting Verbs The general-purpose verbs can be used to display any value. The Formatting Verbs for Any Value Verb Description ------ ----------------------------------------------- %v This verb displays the default format for the value. Modifying the verb with a plus sign (%+v) includes field names when writing out struct values. %#v This verb displays a value in a format that could be used to re-create the value in a Go code file. %T This verb displays the Go type of a value. example: package main import "fmt" type Product struct { Name, Category string Price float64 } var Kayak = Product{ Name: "Kayak", Category: "Watersports", Price: 275, } var Products = []Product{ {"Kayak", "Watersports", 279}, {"Lifejacket", "Watersports", 49.95}, {"Soccer Ball", "Soccer", 19.50}, {"Corner Flags", "Soccer", 34.95}, {"Stadium", "Soccer", 79500}, {"Thinking Cap", "Chess", 16}, {"Unsteady Chair", "Chess", 75}, {"Bling-Bling King", "Chess", 1200}, } func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { Printfln("Value: %v", Kayak) Printfln("Go syntax: %#v", Kayak) Printfln("Type: %T", Kayak) } Output: Value: {Kayak Watersports 275} Go syntax: main.Product{Name:"Kayak", Category:"Watersports", Price:275} Type: main.Product`, "209": `209.Controlling Struct Formatting Go has a default format for all data types that the %v verb relies on. For structs, the default value lists the field values within curly braces. The default verb can be modified with a plus sign to include the field names in the output. example: package main import "fmt" type Product struct { Name, Category string Price float64 } var Kayak = Product{ Name: "Kayak", Category: "Watersports", Price: 275, } var Products = []Product{ {"Kayak", "Watersports", 279}, {"Lifejacket", "Watersports", 49.95}, {"Soccer Ball", "Soccer", 19.50}, {"Corner Flags", "Soccer", 34.95}, {"Stadium", "Soccer", 79500}, {"Thinking Cap", "Chess", 16}, {"Unsteady Chair", "Chess", 75}, {"Bling-Bling King", "Chess", 1200}, } func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { Printfln("Value: %v", Kayak) Printfln("Value with fields: %+v", Kayak) } Output: Value: {Kayak Watersports 275} Value with fields: {Name:Kayak Category:Watersports Price:275} `, "210": `210.Custom Struct Format example: package main import "fmt" type Product struct { Name, Category string Price float64 } var Kayak = Product{ Name: "Kayak", Category: "Watersports", Price: 275, } var Products = []Product{ {"Kayak", "Watersports", 279}, {"Lifejacket", "Watersports", 49.95}, {"Soccer Ball", "Soccer", 19.50}, {"Corner Flags", "Soccer", 34.95}, {"Stadium", "Soccer", 79500}, {"Thinking Cap", "Chess", 16}, {"Unsteady Chair", "Chess", 75}, {"Bling-Bling King", "Chess", 1200}, } func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func (p Product) String() string { return fmt.Sprintf("Product: %v, Price: $%4.2f", p.Name, p.Price) } func main() { Printfln("Value: %v", Kayak) Printfln("Value with fields: %+v", Kayak) } Output: Value: Product: Kayak, Price: $275.00 Value with fields: Product: Kayak, Price: $275.00 `, "211": `211.Formating Arrays, Slices, Maps When arrays and slices are represented as strings, the output is a set of square brackets, within which are the individual elements, like this: example: [Kayak Lifejacket Paddle] Notice that no commas are separating the elements. When maps are represented as strings, the key- value pairs are displayed within square brackets, preceded by the map keyword, like this: example: map[1:Kayak 2:Lifejacket 3:Paddle] `, "212": `212.Integer Formatting Verbs Verb Description ------ ------------------------------------ %b This verb displays an integer value as a binary string. %d This verb displays an integer value as a decimal string. This is the default format for integer values, applied when the %v verb is used. %o, %O These verbs display an integer value as an octal string. The %O verb adds the 0o prefix. %x, %X These verbs display an integer value as a hexadecimal string. The letters A–F are displayed in lowercase by the %x verb and in uppercase by the %X verb. example: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { number := 250 Printfln("Binary: %b", number) Printfln("Decimal: %d", number) Printfln("Octal: %o, %O", number, number) Printfln("Hexadecimal: %x, %X", number, number) } Output: Binary: 11111010 Decimal: 250 Octal: 372, 0o372 Hexadecimal: fa, FA `, "213": `213.Floating-Point Formatting Verbs The Formatting Verbs for Floating-Point Values Verb Description ----- ------------------------------------ %b This verb displays a floating-point value with an exponent and without a decimal place. %e, %E These verbs display a floating-point value with an exponent and a decimal place. The %e uses a lowercase exponent indicator, while %E uses an uppercase indicator. %f, %F These verbs display a floating-point value with a decimal place but no exponent. The %f and %F verbs produce the same output. %g This verb adapts to the value it displays. The %e format is used for values with large exponents, and the %f format is used otherwise. This is the default format, applied when the %v verb is used. %G This verb adapts to the value it displays. The %E format is used for values with large exponents, and the %f format is used otherwise. %x, %X These verbs display a floating-point value in hexadecimal notation, with lowercase (%x) or uppercase (%X) letters. example: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } func main() { number := 279.00 Printfln("Decimalless with exponent: %b", number) Printfln("Decimal with exponent: %e", number) Printfln("Decimal without exponent: %f", number) Printfln("Hexadecimal: %x, %X", number, number) } Output: Decimalless with exponent: 4908219906392064p-44 Decimal with exponent: 2.790000e+02 Decimal without exponent: 279.000000 Hexadecimal: 0x1.17p+08, 0X1.17P+08 `, "214": `214.Controlling Formatting example: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } func main() { number := 279.00 Printfln("Decimal without exponent: >>%8.2f<<", number) } Output: Decimal without exponent: >> 279.00<< `, "215": `215.Specifying Precision example: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } func main() { number := 279.00 Printfln("Decimal without exponent: >>%.2f<<", number) } Output: Decimal without exponent: >>279.00<< `, "216": `216.The Formatting Verb Modifiers Modifier Description -------- -------------------------- + This modifier (the plus sign) always prints a sign, positive or negative, for numeric values. 0 This modifier uses zeros, rather than spaces, as padding when the width is greater than the number of characters required to display the value. - This modifier (the subtracts symbol) adds padding to the right of the number, rather than the left. example: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } func main() { number := 279.00 Printfln("Sign: >>%+.2f<<", number) Printfln("Zeros for Padding: >>%07.2f<<", number) Printfln("Zeros for Padding: >>%08.2f<<", number) Printfln("Right Padding: >>%-8.2f<<", number) } Output: Sign: >>+279.00<< Zeros for Padding: >>0279.00<< Zeros for Padding: >>00279.00<< Right Padding: >>279.00 << `, "217": `217.The Formatting Verbs for Strings and Runes Verb Description ---- ---------------------------------------------------- %s This verb displays a string. This is the default format, applied when the %v verb is used. %c This verb displays a character. Care must be taken to avoid slicing strings into individual bytes, as explained in the text after the table. %U This verb displays a character in the Unicode format so that the output begins with U+ followed by a hexadecimal character code. example: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } func main() { name := "Kayak" Printfln("String: %s", name) Printfln("Character: %c", []rune(name)[0]) Printfln("Unicode: %U", []rune(name)[0]) } Output: String: Kayak Character: K Unicode: U+004B `, "218": `218.The bool Formatting Verb Verb Description ---- ------------- %t This verb formats bool values and displays true or false. example: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } func main() { name := "Kayak" Printfln("Bool: %t", len(name) > 1) Printfln("Bool: %t", len(name) > 100) } Output: Bool: true Bool: false `, "219": `219.The Pointer Formatting Verb Verb Description ---- ----------------- %p This verb displays a hexadecimal representation of the pointer's storage location. example: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } func main() { name := "Kayak" Printfln("Pointer: %p", &name) } Output: Pointer: 0xc00004a240 `, "220": `220.The fmt Functions for Scanning Strings Name Description ------------------------ --------------------------------------------------------------------- Scan(...vals) This function reads text from the standard in and stores the space- separated values into specified arguments. Newlines are treated as spaces, and the function reads until it has received values for all of its arguments. The result is the number of values that have been read and an error that describes any problems. Scanln(...vals) This function works in the same way as Scan but stops reading when it encounters a newline character. Scanf(template, ...vals) This function works in the same way as Scan but uses a template string to select the values from the input it receives. Fscan(reader, ...vals) This function reads space-separated values from the specified reader, which is described in Chapter 20. Newlines are treated as spaces, and the function returns the number of values that have been read and an error that describes any problems. Fscanln(reader, ...vals) This function works in the same way as Fscan but stops reading when it encounters a newline character. Fscanf(reader, template, ...vals) This function works in the same way as Fscan but uses a template to select the values from the input it receives. Sscan(str, ...vals) This function scans the specified string for space-separated values, which are assigned to the remaining arguments. The result is the number of values scanned and an error that describes any problems. Sscanf(str, template, ...vals) This function works in the same way as Sscan but uses a template to select values from the string. Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the string as soon as a newline character is encountered. `, "221": `221.Scanning a String example: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } func main() { var name string var category string var price float64 fmt.Print("Enter text to scan: ") n, err := fmt.Scan(&name, &category, &price) if (err == nil) { Printfln("Scanned %v values", n) Printfln("Name: %v, Category: %v, Price: %.2f", name, category, price) } else { Printfln("Error: %v", err.Error()) } } Output: Enter text to scan: asd asd 123 Scanned 3 values Name: asd, Category: asd, Price: 123.00 `, "222": `222.Scanln Function example: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } func main() { var name string var category string var price float64 fmt.Print("Enter text to scan: ") n, err := fmt.Scanln(&name, &category, &price) if (err == nil) { Printfln("Scanned %v values", n) Printfln("Name: %v, Category: %v, Price: %.2f", name, category, price) } else { Printfln("Error: %v", err.Error()) } } Output: Error: unexpected newline `, "223": `223.Sscan Function example: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } func main() { var name string var category string var price float64 source := "Lifejacket Watersports 48.95" n, err := fmt.Sscan(source, &name, &category, &price) if (err == nil) { Printfln("Scanned %v values", n) Printfln("Name: %v, Category: %v, Price: %.2f", name, category, price) } else { Printfln("Error: %v", err.Error()) } } Output: Scanned 3 values Name: Lifejacket, Category: Watersports, Price: 48.95 `, "224": `224.Scanning Template The template ignores the term Product, skipping that part of the string and allowing the scanning to begin with the next term. example: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } func main() { var name string var category string var price float64 source := "Product Lifejacket Watersports 48.95" template := "Product %s %s %f" n, err := fmt.Sscanf(source, template, &name, &category, &price) if (err == nil) { Printfln("Scanned %v values", n) Printfln("Name: %v, Category: %v, Price: %.2f", name, category, price) } else { Printfln("Error: %v", err.Error()) } } Output: Scanned 3 values Name: Lifejacket, Category: Watersports, Price: 48.95 `, "225": `225.Putting Math Functions and Sorting Data in Context What are they? The math functions allow common calculations to be performed. Random numbers are numbers generated in a sequence that is difficult to predict. Sorting is the process of placing a sequence of values in a predetermined order. Why are they useful? These are features that are used throughout development. How are they used? These features are provided in the math, math/rand, and sort packages. Are there any pitfalls or limitations? Unless initialized with a seed value, the numbers produced by the math/rand package are not random. Are there any alternatives? You could implement both sets of features from scratch, although these packages are provided so that this is not required. `, "226": `226.Useful Functions from the math Package Name Description -------------- ---------------------------------------------------------- Abs(val) This function returns the absolute value of a float64 value, meaning the distance from zero without considering direction. Ceil(val) This function returns the smallest integer that is equal to or greater than the specified float64 value. The result is also a float64 value, even though it represents an integer number. Copysign(x, y) This function returns a float64 value, which is the absolute value of x with the sign of y. Floor(val) This function returns the largest integer that is smaller or equal to the specified float64 value. The result is also a float64 value, even though it represents an integer number. Max(x, y) This function returns whichever of the specified float64 value is the largest. Min(x, y) This function returns whichever of the specified float64 value is smallest. Mod(x, y) This function returns the remainder of x/y. Pow(x, y) This function returns x raised to the exponent y. Round(val) This function rounds the specified value to the nearest integer, rounding half values up. The result is a float64 value, even though it represents an integer. RoundToEven(val) This function rounds the specified value to the nearest integer, rounding half values to the nearest even number. The result is a float64 value, even though it represents an integer. example: package main import "math" func main() { val1 := 279.00 val2 := 48.95 Printfln("Abs: %v", math.Abs(val1)) Printfln("Ceil: %v", math.Ceil(val2)) Printfln("Copysign: %v", math.Copysign(val1, -5)) Printfln("Floor: %v", math.Floor(val2)) Printfln("Max: %v", math.Max(val1, val2)) Printfln("Min: %v", math.Min(val1, val2)) Printfln("Mod: %v", math.Mod(val1, val2)) Printfln("Pow: %v", math.Pow(val1, 2)) Printfln("Round: %v", math.Round(val2)) Printfln("RoundToEven: %v", math.RoundToEven(val2)) } Output: Abs: 279 Ceil: 49 Copysign: -279 Floor: 48 Max: 279 Min: 48.95 Mod: 34.249999999999986 Pow: 77841 Round: 49 RoundToEven: 49 `, "227": `227.The Limit Constants Name Description ---------------- ----------------------------- MaxInt8 These constants represent the largest and smallest values that can be stored MinInt8 using an int8. MaxInt16 These constants represent the largest and smallest values that can be stored MinInt16 using an int16. MaxInt32 These constants represent the largest and smallest values that can be stored MinInt32 using an int32. MaxInt64 These constants represent the largest and smallest values that can be stored MinInt64 using an int64. MaxUint8 This constant represents the largest value that can be represented using a uint8.The smallest value is zero. MaxUint16 This constant represents the largest value that can be represented using a uint16. The smallest value is zero. MaxUint32 This constant represents the largest value that can be represented using a uint32. The smallest value is zero. MaxUint64 This constant represents the largest value that can be represented using a uint64. The smallest value is zero. MaxFloat32 These constants represent the largest values that can be represented using MaxFloat64 float32 and float64 values. SmallestNonzeroFloat32 These constants represent the smallest nonzero values that can be SmallestNonzeroFloat32 represented using float32 and float64 values. `, "228": `228.Generating Random Numbers Useful math/rand Functions Name Description -------------------- --------------------------------------- Seed(s) This function sets the seed value using the specified int64 value. Float32() This function generates a random float32 value between 0 and 1. Float64() This function generates a random float64 value between 0 and 1. Int() This function generates a random int value. Intn(max) This function generates a random int smaller than a specified value, as described after the table. UInt32() This function generates a random uint32 value. UInt64() This function generates a random uint64 value. Shuffle(count, func) This function is used to randomize the order of elements, as described after the table. `, "229": `229.rand.Int() example: package main import "math/rand" func main() { for i := 0; i < 5; i++ { Printfln("Value %v : %v", i, rand.Int()) } } Output: Value 0 : 5577006791947779410 Value 1 : 8674665223082153551 Value 2 : 6129484611666145821 Value 3 : 4037200794235010051 Value 4 : 3916589616287113937 `, "230": `230.rand.Seed() Example: package main import ( "math/rand" "time" ) func main() { rand.Seed(time.Now().UnixNano()) for i := 0; i < 5; i++ { Printfln("Value %v : %v", i, rand.Int()) } } Output: Value 0 : 8113726196145714527 Value 1 : 3479565125812279859 Value 2 : 8074476402089812953 Value 3 : 3916870404047362448 Value 4 : 8226545715271170755 `, "231": `231.rand.Intn(rangeNumber) Generating a Random Number Within a Specific Range example: package main import ( "math/rand" "time" ) func main() { rand.Seed(time.Now().UnixNano()) for i := 0; i < 5; i++ { Printfln("Value %v : %v", i, rand.Intn(10)) } } Output: Value 0 : 7 Value 1 : 5 Value 2 : 4 Value 3 : 0 Value 4 : 7 `, "232": `232.Specifying a Lower Bound example: package main import ( "math/rand" "time" ) func IntRange(min, max int) int { return rand.Intn(max - min) + min } func main() { rand.Seed(time.Now().UnixNano()) for i := 0; i < 5; i++ { Printfln("Value %v : %v", i, IntRange(10, 20)) } } Output: Value 0 : 10 Value 1 : 19 Value 2 : 11 Value 3 : 10 Value 4 : 17 `, "233": `233.Shuffling Elements example: package main import ( "math/rand" "time" "fmt" ) var names = []string{"Alice", "Bob", "Charlie", "Dora", "Edith"} func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { rand.Seed(time.Now().UnixNano()) rand.Shuffle(len(names), func(first, second int) { names[first], names[second] = names[second], names[first] }) for i, name := range names { Printfln("Index %v: Name: %v", i, name) } } Output: Index 0: Name: Edith Index 1: Name: Dora Index 2: Name: Charlie Index 3: Name: Alice Index 4: Name: Bob `, "234": `234.The Basic Functions for Sorting Name Description -------------------- ------------------------------------------- Float64s(slice) This function sorts a slice of float64 values. The elements are sorted in place. Float64sAreSorted(slice) This function returns true if the elements in the specified float64 slice are in order. Ints(slice) This function sorts a slice of int values. The elements are sorted in place. IntsAreSorted(slice) This function returns true if the elements in the specified int slice are in order. Strings(slice) This function sorts a slice of string values. The elements are sorted in place. StringsAreSorted(slice) This function returns true if the elements in the specified string slice are in order. `, "235": `235.Sorting Slices example: package main import ( "fmt" "sort" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { ints := []int{9, 4, 2, -1, 10} Printfln("Ints: %v", ints) sort.Ints(ints) Printfln("Ints Sorted: %v", ints) floats := []float64{279, 48.95, 19.50} Printfln("Floats: %v", floats) sort.Float64s(floats) Printfln("Floats Sorted: %v", floats) strings := []string{"Kayak", "Lifejacket", "Stadium"} Printfln("Strings: %v", strings) if !sort.StringsAreSorted(strings) { sort.Strings(strings) Printfln("Strings Sorted: %v", strings) } else { Printfln("Strings Already Sorted: %v", strings) } } Output: Ints: [9 4 2 -1 10] Ints Sorted: [-1 2 4 9 10] Floats: [279 48.95 19.5] Floats Sorted: [19.5 48.95 279] Strings: [Kayak Lifejacket Stadium] Strings Already Sorted: [Kayak Lifejacket Stadium] `, "236": `236.Creating a Sorted Copy of a Slice example: package main import ( "fmt" "sort" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { ints := []int{9, 4, 2, -1, 10} sortedInts := make([]int, len(ints)) copy(sortedInts, ints) sort.Ints(sortedInts) Printfln("Ints: %v", ints) Printfln("Ints Sorted: %v", sortedInts) } Output: Ints: [9 4 2 -1 10] Ints Sorted: [-1 2 4 9 10] `, "237": `237.The Functions for Searching Sorted Data Name Description ---------------------- ---------------------------------- SearchInts(slice, val) This function searches the sorted slice for the specified int value. The result is the index of the specified value or, if the value is not found, the index at which the value can be inserted while maintaining the sorted order. SearchFloat64s(slice, val) This function searches the sorted slice for the specified float64 value. The result is the index of the specified value or, if the value is not found, the index at which the value can be inserted while maintaining the sorted order. SearchStrings(slice, val) This function searches the sorted slice for the specified string value. The result is the index of the specified value or, if the value is not found, the index at which the value can be inserted while maintaining the sorted order. Search(count, testFunc) This function invokes the test function for the specified number of elements. The result is the index for which the function returns true. If there is no match, then the result is the index at which the specified value can be inserted to maintain the sorted order. `, "238": `238.sort.SearchInts() When a value is located, the functions return its position in the slice. But unusually, if the value is not found, then the result is the position it can be inserted while maintaining the sort order. example: package main import ( "fmt" "sort" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { ints := []int{9, 1, 2, -1, 10,5} sortedInts := make([]int, len(ints)) copy(sortedInts, ints) sort.Ints(sortedInts) Printfln("Ints: %v", ints) Printfln("Ints Sorted: %v", sortedInts) // =========================================== indexOf4 := sort.SearchInts(sortedInts, 4) Printfln("Index of 4: %v", indexOf4) indexOf3 := sort.SearchInts(sortedInts, 3) Printfln("Index of 3: %v", indexOf3) } Output: Ints: [9 1 2 -1 10 5] Ints Sorted: [-1 1 2 5 9 10] Index of 4: 3 Index of 3: 3 `, "239": `239.sort.SearchInts() bool returned example: package main import ( "fmt" "sort" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { ints := []int{9, 1, 2, -1, 10,5} sortedInts := make([]int, len(ints)) copy(sortedInts, ints) sort.Ints(sortedInts) Printfln("Ints: %v", ints) Printfln("Ints Sorted: %v", sortedInts) // ========================= indexOf4:= sort.SearchInts(sortedInts, 4) indexOf3 := sort.SearchInts(sortedInts, 3) Printfln("Index of 4: %v (present: %v)", indexOf4, sortedInts[indexOf4] == 4) Printfln("Index of 3: %v (present: %v)", indexOf3, sortedInts[indexOf3] == 3) } Output: Ints: [9 1 2 -1 10 5] Ints Sorted: [-1 1 2 5 9 10] Index of 4: 3 (present: false) Index of 3: 3 (present: false) `, "240": `240.The Methods Defined by the sort.Interface Interface Name Description ---------- ---------------- Len() This method returns the number of items that will be sorted. Less(i, j) This method returns true if the element at index i should appear in the sorted sequence before the element j. If Less(i,j) and Less(j, i) are both false, then the elements are considered equal. Swap(i, j) This method swaps the elements at the specified indices. `, "241": `241.The Functions for Sorting Types That Implement Interface Name Description ---------- --------------------- Sort(data) This function uses the methods described in 240 Number to sort the specified data. Stable(data) This function uses the methods described in 240 Number to sort the specified data without changing the order of elements of equal value. IsSorted(data) This function returns true if the data is in sorted order. Reverse(data) This function reverses the order of the data. example: main.go: package main import ( "fmt" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { products := []Product{ {"Kayak", 279}, {"Lifejacket", 49.95}, {"Soccer Ball", 19.50}, } ProductSlices(products) for _, p := range products { Printfln("Name: %v, Price: %.2f", p.Name, p.Price) } } Name: Soccer Ball, Price: 19.50 Name: Lifejacket, Price: 49.95 Name: Kayak, Price: 279.00 `, "242": `242.Sorting with a Comparison Function go mod init AND main.go File: package main import ( "fmt" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { products := []Product{ {"Kayak", 279}, {"Lifejacket", 49.95}, {"Soccer Ball", 19.50}, } SortWith(products, func(p1, p2 Product) bool { return p1.Name < p2.Name }) for _, p := range products { Printfln("Name: %v, Price: %.2f", p.Name, p.Price) } } productsort.go File: package main import "sort" type Product struct { Name string Price float64 } type ProductSlice []Product func ProductSlices(p []Product) { sort.Sort(ProductSlice(p)) } func ProductSlicesAreSorted(p []Product) { sort.IsSorted(ProductSlice(p)) } func (products ProductSlice) Len() int { return len(products) } func (products ProductSlice) Less(i, j int) bool { return products[i].Price < products[j].Price } func (products ProductSlice) Swap(i, j int) { products[i], products[j] = products[j], products[i] } type ProductComparison func(p1, p2 Product) bool type ProductSliceFlex struct { ProductSlice ProductComparison } func (flex ProductSliceFlex) Less(i, j int) bool { return flex.ProductComparison(flex.ProductSlice[i], flex.ProductSlice[j]) } func SortWith(prods []Product, f ProductComparison) { sort.Sort(ProductSliceFlex{ prods, f}) } `, "243": `243.Putting Dates, Times, and Durations in Context What are they? The features provided by the time package are used to represent specific moments in time and intervals or durations. Why are they useful? These features are useful in any application that needs to deal with calendaring or alarm and for the development of any feature that requires delays or notifications in the future. How are they used? The time package defines data types for representing dates and individual units of time and functions for manipulating them. There are also features integrated into the Go channel system. Are there any pitfalls or limitations? Dates can be complex, and care must be taken to deal with calendar and time zone issues. Are there any alternatives? These are optional features, and their use is not required. `, "244": `244.The Functions in the time Package for Creating Time Values Name Description -------- ----------------------------- Now() This function creates a Time representing the current moment in time. Date(y, m, d, h, min, sec, nsec, loc) This function creates a Time representing a specified moment in time, which is expressed by the year, month, day, hour, minute, second, nanosecond, and Location arguments. (The Location type is described in the “Parsing Time Values from Strings” section.) Unix(sec, nsec) This function creates a Time value from the number of seconds and nanoseconds since January 1, 1970, UTC, commonly known as Unix time. `, "245": `245.The Methods for Accessing Time Components Name Description -------- ---------------------------- Date() This method returns the year, month, and day components. The year and day are expressed as int values and the month as a Month value. Clock() This method returns the hour, minutes, and seconds components of the Time. Year() This method returns the year component, expressed as an int. YearDay() This method returns the day of the year, expressed as an int between 1 and 366 (to accommodate leap years). Month() This method returns the month component, expressed using the Month type. Day() This method returns the day of the month, expressed as an int. Weekday() This method returns the day of the week, expressed as a Weekday. Hour() This method returns the hour of the day, expressed as an int between 0 and 23. Minute() This method returns the number of minutes elapsed into the hour of the day, expressed as an int between 0 and 59. Second() This method returns the number of seconds elapsed into the minute of the hour, expressed as an int between 0 and 59. Nanosecond() This method returns the number of nanoseconds elapsed into the second of the minute, expressed as an int between 0 and 999,999,999. `, "246": `246.The Types Used to Describe Time Components Name Description ------- ------------------------------------------------------------------------ Month This type represents a month, and the time package defines constant values for the English- language month names: January, February, etc. The Month type defines a String method that uses these names when formatting strings. Weekday This type represents a day of the week, and the time package defines constant values for the English-language weekday names: Sunday, Monday, etc. The Weekday type defines a String method that uses these names when formatting strings. `, "247": `247.Creating Time Values example: package main import "time" func PrintTime(label string, t *time.Time) { Printfln("%s: Day: %v: Month: %v Year: %v", label, t.Day(), t.Month(), t.Year()) } func main() { current := time.Now() specific := time.Date(1995, time.June, 9, 0, 0, 0, 0, time.Local) unix := time.Unix(1433228090, 0) PrintTime("Current", ¤t) PrintTime("Specific", &specific) PrintTime("UNIX", &unix) } Output: Current: Day: 15: Month: September Year: 2023 Specific: Day: 9: Month: June Year: 1995 UNIX: Day: 2: Month: June Year: 2015 example: package main import "time" func PrintTime(label string, t *time.Time) { Printfln("%s: Day: %v: Month: %v Year: %v", label, t.Day(), t.Month(), t.Year()) } func main() { current := time.Now() specific := time.Date(1993, time.June, 0, 0, 0, 0, 0, time.Local) unix := time.Unix(0, 0) PrintTime("Current", ¤t) PrintTime("Specific", &specific) PrintTime("UNIX", &unix) } Output: Current: Day: 15: Month: September Year: 2023 Specific: Day: 31: Month: May Year: 1993 UNIX: Day: 31: Month: December Year: 1969 `, "248": `248.The Time Method for Creating Formatted Strings Name Description -------------- -------------------- Format(layout) This method returns a formatted string, which is created using the specified layout. example: package main import ( "fmt" "time" ) func PrintTime(label string, t *time.Time) { layout := "Day: 02 Month: Jan Year: 2006" fmt.Println(label, t.Format(layout)) } func main() { current := time.Now() specific := time.Date(1993, time.June, 0, 0, 0, 0, 0, time.Local) unix := time.Unix(0, 0) PrintTime("Current", ¤t) PrintTime("Specific", &specific) PrintTime("UNIX", &unix) } Output: Current Day: 16 Month: Sep Year: 2023 Specific Day: 31 Month: May Year: 1993 UNIX Day: 31 Month: Dec Year: 1969 `, "249": `249.The Layout Constants Defined by the time Package Name Reference Date Format ----------- ---------------------------------- ANSIC Mon Jan _2 15:04:05 2006 UnixDate Mon Jan _2 15:04:05 MST 2006 RubyDate Mon Jan 02 15:04:05 -0700 2006 RFC822 02 Jan 06 15:04 MST RFC822Z 02 Jan 06 15:04 -0700 RFC850 Monday, 02-Jan-06 15:04:05 MST RFC1123 Mon, 02 Jan 2006 15:04:05 MST RFC1123Z Mon, 02 Jan 2006 15:04:05 -0700 RFC3339 2006-01-02T15:04:05Z07:00 RFC3339Nano 2006-01-02T15:04:05.999999999Z07:00 Kitchen 3:04PM Stamp Jan _2 15:04:05 StampMilli Jan _2 15:04:05.000 StampMicro Jan _2 15:04:05.000000 StampNano Jan _2 15:04:05.000000000 `, "250": `250.The time Package Functions for Parsing Strings into Time Values Name Description -------------------------------------- ----------------------------------- Parse(layout, str) This function parses a string using the specified layout to create a Time value. An error is returned to indicate problems parsing the string. ParseInLocation(layout, str, location) This function parses a string, using the specified layout and using the Location if no time zone is included in the string. An error is returned to indicate problems parsing the string. example: package main import ( "fmt" "time" ) func PrintTime(label string, t *time.Time) { fmt.Println(label, t.Format(time.RFC822Z)) } func main() { dates := []string{ "09 Jun 95 00:00 GMT", "02 Jun 15 00:00 GMT", } for _, d := range dates { time, err := time.Parse(time.RFC822, d) if err == nil { PrintTime("Parsed", &time) } else { Printfln("Error: %s", err.Error()) } } } Output: Parsed 09 Jun 95 00:00 +0000 Parsed 02 Jun 15 00:00 +0000 `, "251": `251.time.ParseInLocation() example: package main import ( "fmt" "time" ) func PrintTime(label string, t *time.Time) { //layout := "Day: 02 Month: Jan Year: 2006" fmt.Println(label, t.Format(time.RFC822Z)) } func main() { layout := "02 Jan 06 15:04" date := "09 Jun 95 19:30" london, lonerr := time.LoadLocation("Europe/London") newyork, nycerr := time.LoadLocation("America/New_York") Tehran, TehranErr := time.LoadLocation("Asia/Tehran") if lonerr == nil && nycerr == nil && TehranErr == nil{ TehranTime, _ := time.ParseInLocation(layout, date, Tehran) PrintTime("Tehran:",&TehranTime) nolocation, _ := time.Parse(layout, date) PrintTime("No location:", &nolocation) londonTime, _ := time.ParseInLocation(layout, date, london) PrintTime("London:", &londonTime) newyorkTime, _ := time.ParseInLocation(layout, date, newyork) PrintTime("New York:", &newyorkTime) } else { fmt.Println(lonerr.Error(), nycerr.Error()) } } Output: Tehran: 09 Jun 95 19:30 +0430 No location: 09 Jun 95 19:30 +0000 London: 09 Jun 95 19:30 +0100 New York: 09 Jun 95 19:30 -0400 `, "252": `252.The Functions for Creating Locations Name Description ------------------------- ----------------------------------------- LoadLocation(name) This function returns a *Location for the specified name and an error that indicates any problems. LoadLocationFromTZData(name,data) This function returns a *Location from a byte slice that contains a formatted time zone database. FixedZone(name, offset) This function returns a *Location that always uses the specified name and offset from UTC. The place names are defined in the IANA time zone database, https://www.iana.org/time-zones , and are listed by https://en.wikipedia.org/wiki/List_of_tz_database_time_zones `, "253": `253.Embedding The Time Zone Database example: package main import ( "fmt" "time" ) func PrintTime(label string, t *time.Time) { //layout := "Day: 02 Month: Jan Year: 2006" fmt.Println(label, t.Format(time.RFC822Z)) } func main() { layout := "02 Jan 06 15:04" date := "09 Jun 95 19:30" Tehran, TehranErr := time.LoadLocation("Asia/Tehran") local, _ := time.LoadLocation("Local") if TehranErr == nil{ TehranTime, _ := time.ParseInLocation(layout, date, Tehran) PrintTime("Tehran:",&TehranTime) localTime, _ := time.ParseInLocation(layout, date, local) PrintTime("Local:", &localTime) nolocation, _ := time.Parse(layout, date) PrintTime("No location:", &nolocation) } else { fmt.Println(TehranErr.Error()) } } Output: Tehran: 09 Jun 95 19:30 +0430 Local: 09 Jun 95 19:30 -0400 No location: 09 Jun 95 19:30 +0000 `, "254": `254.Specifying Time Zones The arguments to the FixedZone function are a name and the number of seconds offset from UTC. This example creates three fixed time zones, one of which is an hour ahead of UTC, one of which is four hours behind, and one of which has no offset. example: package main import ( "fmt" "time" ) func PrintTime(label string, t *time.Time) { //layout := "Day: 02 Month: Jan Year: 2006" fmt.Println(label, t.Format(time.RFC822Z)) } func main() { layout := "02 Jan 06 15:04" date := "09 Jun 95 19:30" london := time.FixedZone("BST", 1*60*60) newyork := time.FixedZone("EDT", -4*60*60) local := time.FixedZone("Local", 0) nolocation, _ := time.Parse(layout, date) londonTime, _ := time.ParseInLocation(layout, date, london) newyorkTime, _ := time.ParseInLocation(layout, date, newyork) localTime, _ := time.ParseInLocation(layout, date, local) PrintTime("No location:", &nolocation) PrintTime("London:", &londonTime) PrintTime("New York:", &newyorkTime) PrintTime("Local:", &localTime) } Output: No location: 09 Jun 95 19:30 +0000 London: 09 Jun 95 19:30 +0100 New York: 09 Jun 95 19:30 -0400 Local: 09 Jun 95 19:30 +0000 `, "255": `255.The Methods for Working with Time Values Name Description ---------------- ------------------------------------------------- Add(duration) This method adds the specified Duration to the Time and returns the result. Sub(time) This method returns a Duration that expresses the difference between the Time on which the method has been called and the Time provided as the argument. AddDate(y, m, d) This method adds the specified number of years, months, and days to the Time and returns the result. After(time) This method returns true if the Time on which the method has been called occurs after the Time provided as the argument. Before(time) This method returns true if the Time on which the method has been called occurs before the Time provided as the argument. Equal(time) This method returns true if the Time on which the method has been called is equal to the Time provided as the argument. IsZero() This method returns true if the Time on which the method has been called represents the zero-time instant, which is January 1, year 1, 00:00:00 UTC. In(loc) This method returns the Time value, expressed in the specified Location. Location() This method returns the Location that is associated with the Time, effectively allowing a time to be expressed in a different time zone. Round(duration) This method rounds the Time to the nearest interval represented by a Duration value. Truncate(duration) This method rounds the Time down to the nearest interval represented by a Duration value. `, "256": `256.time.Parse() example: package main import ( "fmt" "time" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { t, err := time.Parse(time.RFC822, "09 Jun 95 04:59 BST") if err == nil { Printfln("After: %v", t.After(time.Now())) Printfln("Round: %v", t.Round(time.Hour)) Printfln("Truncate: %v", t.Truncate(time.Hour)) } else { fmt.Println(err.Error()) } } Output: After: false Round: 1995-06-09 05:00:00 +0100 BST Truncate: 1995-06-09 04:00:00 +0100 BST `, "257": `257.Comparing Time Values example: package main import ( "fmt" "time" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { t1, _ := time.Parse(time.RFC822Z, "09 Jun 95 04:59 +0100") t2, _ := time.Parse(time.RFC822Z, "08 Jun 95 23:59 -0400") Printfln("Equal Method: %v", t1.Equal(t2)) Printfln("Equality Operator: %v", t1 == t2) } Output: Equal Method: true Equality Operator: false `, "258": `258.The Duration Constants in the time Package Name Description ------------ ---------------------------------------- Hour This constant represents 1 hour. Minute This constant represents 1 minute. Second This constant represents 1 second. Millisecond This constant represents 1 millisecond. Microsecond This constant represents 1 microsecond. Nanosecond This constant represents 1 nanosecond. `, "259": `259.The Duration Methods Name Description ---------------- --------------------------------------------- Hours() This method returns a float64 that represents the Duration in hours. Minutes() This method returns a float64 that represents the Duration in minutes. Seconds() This method returns a float64 that represents the Duration in seconds. Milliseconds() This method returns an int64 that represents the Duration in milliseconds. Microseconds() This method returns an int64 that represents the Duration in microseconds. Nanoseconds() This method returns an int64 that represents the Duration in nanoseconds. Round(duration) This method returns a Duration, which is rounded to the nearest multiple of the specified Duration. Truncate(duration) This method returns a Duration, which is rounded down to the nearest multiple of the specified Duration. `, "260": `260.Hours() - Minutes() - Seconds() - rounded.Hours() - rounded.Minutes() example: package main import ( "fmt" "time" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { var d time.Duration = time.Hour + (30 * time.Minute) Printfln("Hours: %v", d.Hours()) Printfln("Mins: %v", d.Minutes()) Printfln("Seconds: %v", d.Seconds()) Printfln("Millseconds: %v", d.Milliseconds()) rounded := d.Round(time.Hour) Printfln("Rounded Hours: %v", rounded.Hours()) Printfln("Rounded Mins: %v", rounded.Minutes()) trunc := d.Truncate(time.Hour) Printfln("Truncated Hours: %v", trunc.Hours()) Printfln("Rounded Mins: %v", trunc.Minutes()) } Output: Hours: 1.5 Mins: 90 Seconds: 5400 Millseconds: 5400000 Rounded Hours: 2 Rounded Mins: 120 Truncated Hours: 1 Rounded Mins: 60 `, "261": `261.The time Functions for Creating Duration Values relative to a Time Name Description ----------- ---------------------------------------- Since(time) This function returns a Duration expressing the elapsed time since the specified Time value. Until(time) This function returns a Duration expressing the elapsed time until the specified Time value. `, "262": `262.time.Until(time) - Since(time) example: package main import ( "fmt" "time" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { toYears := func(d time.Duration) int { return int(d.Hours() / (24 * 365)) } future := time.Date(2051, 0, 0, 0, 0, 0, 0, time.Local) past := time.Date(1965, 0, 0, 0, 0, 0, 0, time.Local) Printfln("this year is %v.",time.Now().Year()) Printfln("Future: %v is %v.", toYears(time.Until(future)), future.Year()) Printfln("Past: %v is %v.", toYears(time.Since(past)), past.Year()) } Output: this year is 2023. Future: 27 is 2050. Past: 58 is 1964. `, "263": `263.time.ParseDuration function This function returns a Duration and an error, indicating if there were problems parsing the specified string. The format of the strings supported by the ParseDuration function is a sequence of number values followed by the unit indicators: Unit Description ----- -------------------- h This unit denotes hours. m This unit denotes minutes. s This unit denotes seconds. ms This unit denotes milliseconds. us or μs These units denotes microseconds. ns This unit denotes nanoseconds. example: package main import ( "fmt" "time" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func main() { d, err := time.ParseDuration("1h30m") if err == nil { Printfln("Hours: %v", d.Hours()) Printfln("Mins: %v", d.Minutes()) Printfln("Seconds: %v", d.Seconds()) Printfln("Millseconds: %v", d.Milliseconds()) } else { fmt.Println(err.Error()) } } Output: Hours: 1.5 Mins: 90 Seconds: 5400 Millseconds: 5400000 `, "264": `264.Using the Time Features for Goroutines and Channels The time package provides a small set of functions that are useful for working with goroutines and channels. The time Package Functions: Name Description ---------------------- ---------------------------------------- Sleep(duration) This function pauses the current goroutine for at least the specified duration. AfterFunc(duration,func) This function executes the specified function in its own goroutine after the specified duration. The result is a *Timer, whose Stop method can be used to cancel the execution of the function before the duration elapses. After(duration) This function returns a channel that blocks for the specified duration and then yields a Time value. See the “Receiving Timed Notifications” section for details. Tick(duration) This function returns a channel that periodically sends a Time value, where the period is specified as a duration. `, "265": `265.time.Sleep(time.Second * 1) Pausing a Goroutine The Sleep function pauses execution of the current goroutine for a specified duration example: package main import ( "fmt" "time" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func writeToChannel(channel chan<- string) { names := []string{"Alice", "Bob", "Charlie", "Dora"} for _, name := range names { channel <- name time.Sleep(time.Second * 1) } close(channel) } func main() { nameChannel := make(chan string) go writeToChannel(nameChannel) for name := range nameChannel { Printfln("Read name: %v", name) } } Output: Read name: Alice // 1 second delaying Read name: Bob // 1 second delaying Read name: Charlie // 1 second delaying Read name: Dora `, "266": `266.time.AfterFunc() function The AfterFunc function is used to defer the execution of a function for a specified period Deferring a Function: example: package main import ( "fmt" "time" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func writeToChannel(channel chan<- string) { names := []string{"Alice", "Bob", "Charlie", "Dora"} for _, name := range names { channel <- name // time.Sleep(time.Second * 1) } close(channel) } func main() { nameChannel := make(chan string) time.AfterFunc(time.Second*5, func() { writeToChannel(nameChannel) }) for name := range nameChannel { Printfln("Read name: %v", name) } } Output: // It waits for 5 seconds and then continues the program. Read name: Alice Read name: Bob Read name: Charlie Read name: Dora `, "267": `267.time.After(time.Second * 2) The result from the After function is a channel that carries Time values. The channel blocks for the specified duration, when a Time value is sent, indicating the duration has passed. In this example, the value sent over the channel acts as a signal and is not used directly, which is why it is assigned to the blank identifier, like this: example: package main import ( "fmt" "time" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func writeToChannel(channel chan<- string) { Printfln("Waiting for initial duration...") _ = <-time.After(time.Second * 2) Printfln("Initial duration elapsed.") names := []string{"Alice", "Bob", "Charlie", "Dora"} for _, name := range names { channel <- name time.Sleep(time.Second * 1) } close(channel) } func main() { nameChannel := make(chan string) go writeToChannel(nameChannel) for name := range nameChannel { Printfln("Read name: %v", name) } } Output: Waiting for initial duration... // Wait for 2 seconds Initial duration elapsed. Read name: Alice // wait for 1 second Read name: Bob // wait for 1 second Read name: Charlie // wait for 1 second Read name: Dora // wait for 1 second `, "268": `268.time.Sleep(time.Second * 3) with select statement Using a Timeout in a Select Statement example: package main import ( "fmt" "time" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func writeToChannel(channel chan<- string) { Printfln("Waiting for initial duration...") _ = <-time.After(time.Second * 2) Printfln("Initial duration elapsed.") names := []string{"Alice", "Bob", "Charlie", "Dora"} for _, name := range names { channel <- name time.Sleep(time.Second * 3) } close(channel) } func main() { nameChannel := make(chan string) go writeToChannel(nameChannel) channelOpen := true for channelOpen { Printfln("Starting channel read") select { case name, ok := <-nameChannel: if !ok { channelOpen = false } else { Printfln("Read name: %v", name) } case <-time.After(time.Second * 2): Printfln("Timeout") } } } Output: Starting channel read Waiting for initial duration... Timeout Starting channel read Initial duration elapsed. Read name: Alice Starting channel read Timeout Starting channel read Read name: Bob Starting channel read Timeout Starting channel read Read name: Charlie Starting channel read Timeout Starting channel read Read name: Dora Starting channel read Timeout Starting channel read `, "269": `269.NewTimer(duration) This function returns a *Timer with the specified period. Caution Be careful when stopping a timer. The timer's channel is not closed, which means that reads from the channel will continue to block even after the timer has stopped. The Methods Defined by the Timer Struct: Name Description ------------ ------------------------------------------- C This field returns the channel over which the Time will send its Time value. Stop() This method stops the timer. The result is a bool that will be true if the timer has been stopped and false if the timer had already sent its message. Reset(duration) This method stops a timer and resets it so that its interval is the specified Duration. example: package main import ( "fmt" "time" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func writeToChannel(channel chan<- string) { timer := time.NewTimer(time.Minute * 10) go func() { time.Sleep(time.Second * 2) Printfln("Resetting timer") timer.Reset(time.Second) }() Printfln("Waiting for initial duration...") <-timer.C Printfln("Initial duration elapsed.") names := []string{"Alice", "Bob", "Charlie", "Dora"} for _, name := range names { channel <- name } close(channel) } func main() { nameChannel := make(chan string) go writeToChannel(nameChannel) for name := range nameChannel { Printfln("Read name: %v", name) } } Output: Waiting for initial duration... Resetting timer Initial duration elapsed. Read name: Alice Read name: Bob Read name: Charlie Read name: Dora `, "270": `270.time.Tick(time.Second) Receiving Recurring Notifications دریافت اعلان های مکرر The Tick function returns a channel over which Time values are sent at a specified interval Tick is a convenience wrapper for NewTicker providing access to the ticking channel only. While Tick is useful for clients that have no need to shut down the Ticker, be aware that without a way to shut it down the underlying Ticker cannot be recovered by the garbage collector; it "leaks". Unlike NewTicker, Tick will return nil if d <= 0. example: package main import ( "fmt" "time" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func writeToChannel(nameChannel chan<- string) { names := []string{"Alice", "Bob", "Charlie", "Dora"} tickChannel := time.Tick(time.Second) index := 0 for { <-tickChannel nameChannel <- names[index] index++ if index == len(names) { index = 0 } } } func main() { nameChannel := make(chan string) go writeToChannel(nameChannel) for name := range nameChannel { Printfln("Read name: %v", name) } } Output: Read name: Alice Read name: Bob Read name: Charlie Read name: Dora Read name: Alice Read name: Bob Read name: Charlie Read name: Dora Read name: Alice Read name: Bob Read name: Charlie Read name: Dora ... `, "271": `271.NewTicker(duration) The result of the NewTicker function is a pointer to a Ticker struct, which defines the field and methods The time Function for Creating a Ticker: Name Description --------------------- --------------------------------- NewTicker(duration) This function returns a *Ticker with the specified period. The Field and Methods Defined by the Ticker Struct: Name Description ---------------- -------------------------------- C This field returns the channel over which the Ticker will send its Time values. Stop() This method stops the ticker (but does not close the channel returned by the C field). Reset(duration) This method stops a ticker and resets it so that its interval is the specified Duration. `, "272": `272.Creating a Ticker in the main.go example: package main import ( "fmt" "time" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } func writeToChannel(nameChannel chan<- string) { names := []string{"Alice", "Bob", "Charlie", "Dora"} ticker := time.NewTicker(time.Second / 10) index := 0 for { <-ticker.C nameChannel <- names[index] index++ if index == len(names) { ticker.Stop() close(nameChannel) break } } } func main() { nameChannel := make(chan string) go writeToChannel(nameChannel) for name := range nameChannel { Printfln("Read name: %v", name) } } Output: // This is printed after milliseconds Read name: Alice Read name: Bob Read name: Charlie Read name: Dora `, "273": `273.Reading and Writing Data These interfaces are used wherever data is read or written, which means that any source or destination for data can be treated in much the same way so that writing data to a file, for example, is just the same as writing data to a network connection. What are they? These interfaces define the basic methods required to read and write data. Why are they useful? This approach means that just about any data source can be used in the same way, while still allowing specialized features to be defined using the composition features. How is it used? The io package defines these interfaces, but the implementations are available from a range of other packages Are there any pitfalls or limitations? These interfaces don't entirely hide the detail of sources or destinations for data and additional methods are often required, provided by interfaces that build on Reader and Writer. Are there any alternatives? The use of these interfaces is optional, but they are hard to avoid because they are used throughout the standard library. The Reader and Writer interfaces are defined by the io package and provide abstract ways to read and write data, without being tied to where the data is coming from or going to. Preparing for This Chapter: product.go: package main type Product struct { Name, Category string Price float64 } var Kayak = Product{ Name: "Kayak", Category: "Watersports", Price: 279, } var Products = []Product{ {"Kayak", "Watersports", 279}, {"Lifejacket", "Watersports", 49.95}, {"Soccer Ball", "Soccer", 19.50}, {"Corner Flags", "Soccer", 34.95}, {"Stadium", "Soccer", 79500}, {"Thinking Cap", "Chess", 16}, {"Unsteady Chair", "Chess", 75}, {"Bling-Bling King", "Chess", 1200}, } printer.go: package main import ( "fmt" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } main.go: package main func main() { Printfln("Product: %v, Price : %v", Kayak.Name, Kayak.Price) } `, "274": `274.The Reader interface The Reader interface doesn't include any detail about where data comes from or how it is obtained—it just defines the Read method. The details are left to the types that implement the interface, and there are reader implementations in the standard library for different data sources. defines a single method: Name Description -------------- ------------------------- Read(byteSlice) This method reads data into the specified []byte. The method returns the number of bytes that were read, expressed as an int, and an error. `, "275": `275.io.Reader package example: package main import ( "io" "strings" ) func processData(reader io.Reader) { b := make([]byte, 2) for { count, err := reader.Read(b) if count > 0 { Printfln("Read %v bytes: %v", count, string(b[0:count])) } if err == io.EOF { break } } } func main() { r := strings.NewReader("Kayak") processData(r) } Output: Read 2 bytes: Ka Read 2 bytes: ya Read 1 bytes: k `, "276": `276.Writer interface Write(byteSlice) This method writes the data from the specified byte slice. The method returns the number of bytes that were written and an error. The error will be non-nil if the number of bytes written is less than the length of the slice. The Writer interface doesn't include any details of how the written data is stored, transmitted, or processed, all of which is left to the types that implement the interface. `, "277": `277.io.Writer example: package main import( "io" "strings" "asd/asd" ) func processData(reader io.Reader, writer io.Writer) { b := make([]byte, 2) for { count, err := reader.Read(b) if count > 0 { writer.Write(b[0:count]) asd.Printfln("Read %v bytes: %v", count, string(b[0:count])) } if err == io.EOF { break } } } func main() { r := strings.NewReader("Kayak") var builder strings.Builder processData(r, &builder) asd.Printfln("String builder contents: %s", builder.String()) } Output: Read 2 bytes: Ka Read 2 bytes: ya Read 1 bytes: k String builder contents: Kayak `, "278": `278.io.EOF The io package defines a special error named EOF, which is used to signal when the Reader reaches the end of the data. If the error result from the Read function is equal to the EOF error, then I break out of the for loop that has been reading data from the Reader: ... if err == io.EOF { break } ... `, "279": `279.the Utility Functions for Readers and Writers Functions in the io Package for Readng and Writing Data Name Description ------------------------ ------------------------------------------------------- Copy(w, r) This function copies data from a Reader to a Writer until EOF is returned or another error is encountered. The results are the number of bytes copies and an error used to describe any problems. CopyBuffer(w, r, buffer) This function performs the same task as Copy but reads the data into the specified buffer before it is passed to the Writer. CopyN(w, r, count) This function copies count bytes from the Reader to the Writer. The results are the number of bytes copies and an error used to describe any problems. ReadAll(r) This function reads data from the specified Reader until EOF is reached. The results are a byte slice containing the read data and an error, which is used to describe any problems. ReadAtLeast(r, byteSlice, min) This function reads at least the specified number of bytes from the reader, placing them into the byte slice. An error is reported if fewer bytes than specified are read. ReadFull(r, byteSlice) This function fills the specified byte slice with data. The result is the number of bytes read and an error. An error will be reported if EOF was encountered before enough bytes to fill the slice were read. WriteString(w, str) This function writes the specified string to a writer. `, "280": `280.io.Copy(writer, reader) example: package main import( "io" "asd/asd" "strings" ) func processData(reader io.Reader, writer io.Writer) { count, err := io.Copy(writer, reader) if (err == nil) { asd.Printfln("Read %v bytes", count) } else { asd.Printfln("Error: %v", err.Error()) } } func main() { r := strings.NewReader("Kayak .") var builder strings.Builder processData(r, &builder) asd.Printfln("String builder contents: %s", builder.String()) } Output: Read 7 bytes String builder contents: Kayak . `, "281": `281.The io Package Functions for Specialized Readers and Writers Name Description ----------- ---------------------------------------- Pipe() This function returns a PipeReader and a PipeWriter, which can be used to connect functions that require a Reader and a Writer, as described in the “Using Pipes” section. MultiReader(...readers) This function defines a variadic parameter that allows an arbitrary number of Reader values to be specified. The result is a Reader that passes on the content from each of its parameters in the sequence they are defined, as described in the “Concatenating Multiple Readers” section. MultiWriter(...writers) This function defines a variadic parameter that allows an arbitrary number of Writer values to be specified. The result is a Writer that sends the same data to all the specified writers, as described in the “Combining Multiple Writers” section. LimitReader(r, limit) This function creates a Reader that will EOF after the specified number of bytes, as described in the “Limiting Read Data” section. `, "282": `282.Pipe Pipes are used to connect code that consumes data through a Reader and code that produces code through a Writer. The GenerateData function defines a Writer parameter, which it uses to write bytes from a string. example: data.go: package main import ( "io" "asd/asd" ) func GenerateData(writer io.Writer) { data := []byte("Kayak, Lifejacket") writeSize := 4 for i := 0; i < len(data); i += writeSize { end := i + writeSize; if (end > len(data)) { end = len(data) } count, err := writer.Write(data[i: end]) asd.Printfln("Wrote %v byte(s): %v", count, string(data[i: end])) if (err != nil) { asd.Printfln("Error: %v", err.Error()) } } } func ConsumeData(reader io.Reader) { data := make([]byte, 0, 10) slice := make([]byte, 2) for { count, err := reader.Read(slice) if (count > 0) { asd.Printfln("Read data: %v", string(slice[0:count])) data = append(data, slice[0:count]...) } if (err == io.EOF) { break } } asd.Printfln("Read data: %v", string(data)) } ===================================================================================== Notice the parentheses at the end of this statement. These are required when creating a goroutine for an anonymous function, but it is easy to forget them. main.go: package main import ( "io" ) func main() { pipeReader, pipeWriter := io.Pipe() go func() { GenerateData(pipeWriter) pipeWriter.Close() }() ConsumeData(pipeReader) } ===================================================================================== The output highlights the fact that pipes are synchronous. The GenerateData function calls the writer's Write method and then blocks until the data is read. This is why the first message in the output is from the reader: the reader is consuming the data two bytes at a time, which means that two read operations are required before the initial call to the Write method, which is used to send four bytes, completes, and the message from the GenerateData function is displayed. Output: Read data: Ka Read data: ya Wrote 4 byte(s): Kaya Read data: k, Read data: L Wrote 4 byte(s): k, L Read data: if Read data: ej Wrote 4 byte(s): ifej Read data: ac Read data: ke Wrote 4 byte(s): acke Read data: t Wrote 1 byte(s): t Read data: Kayak, Lifejacket`, "283": `283.PipeReader and a PipeWriter The io.Pipe function returns a PipeReader and a PipeWriter. The PipeReader and PipeWriter structs implement the Closer interface Name Description ------- ----------------------- Close() This method closes the reader or writer. The details are implementation specific, but, in general, any subsequent reads from a closed Reader will return zero bytes and the EOF error, while any subsequent writes to a closed Writer will return an error. The PipeReader struct implements the Reader interface, which means I can use it as the argument to the ConsumeData function. The ConsumeData function is executed in the main goroutine, which means that the application won't exit until the function completes. The effect is that data is written into the pipe using the PipeWriter and read from the pipe using the PipeReader. When the GenerateData function is complete, the Close method is called on the PipeWriter, which causes the next read by the PipeReader to produce EOF. `, "284": `284.io.MultiReader() example: main.go: package main import ( "io" "strings" ) func main() { r1 := strings.NewReader("Kayak") r2 := strings.NewReader("Lifejacket") r3 := strings.NewReader("Canoe") concatReader := io.MultiReader(r1, r2, r3) ConsumeData(concatReader) } Output: Read data: Ka Read data: ya Read data: k Read data: Li Read data: fe Read data: ja Read data: ck Read data: et Read data: Ca Read data: no Read data: e Read data: KayakLifejacketCanoe `, "285": `285.io.MultiWriter() example: package main import ( "io" "strings" "asd/asd" ) func main() { var w1 strings.Builder var w2 strings.Builder var w3 strings.Builder combinedWriter := io.MultiWriter(&w1, &w2, &w3) GenerateData(combinedWriter) asd.Printfln("Writer #1: %v", w1.String()) asd.Printfln("Writer #2: %v", w2.String()) asd.Printfln("Writer #3: %v", w3.String()) } Output: Wrote 4 byte(s): Kaya Wrote 4 byte(s): k, L Wrote 4 byte(s): ifej Wrote 4 byte(s): acke Wrote 1 byte(s): t Writer #1: Kayak, Lifejacket Writer #2: Kayak, Lifejacket Writer #3: Kayak, Lifejacket `, "286": `286.io.TeeReader(concatReader, &writer) Echoing Reads to a Writer The TeeReader function returns a Reader that echoes the data that it receives to a Writer. example: package main import ( "asd/asd" "io" "strings" ) func main() { r1 := strings.NewReader("Kayak") r2 := strings.NewReader("Lifejacket") r3 := strings.NewReader("Canoe") concatReader := io.MultiReader(r1, r2, r3) var writer strings.Builder teeReader := io.TeeReader(concatReader, &writer) ConsumeData(teeReader) asd.Printfln("Echo data: %v", writer.String()) } Output: Read data: Ka Read data: ya Read data: k Read data: Li Read data: fe Read data: ja Read data: ck Read data: et Read data: Ca Read data: no Read data: e Read data: KayakLifejacketCanoe Echo data: KayakLifejacketCanoe `, "287": `287.io.LimitReader(concatReader, 5) The LimitReader function is used to restrict the amount of data that can be obtained from a Reader example: package main import ( "io" "strings" ) func main() { r1 := strings.NewReader("Kayak") r2 := strings.NewReader("Lifejacket") r3 := strings.NewReader("Canoe") concatReader := io.MultiReader(r1, r2, r3) limited := io.LimitReader(concatReader, 5) ConsumeData(limited) } Output: Read data: Ka Read data: ya Read data: k Read data: Kayak `, "288": `288.bufio The bufio package provides support for adding buffers to readers and writers. example: This code defined a struct type named CustomReader that acts as a wrapper around a Reader. The implementation of the Read method generates output that reports how much data is read and how many read operations are performed overall. custom.go: package main import ( "io" "asd/asd" ) type CustomReader struct { reader io.Reader readCount int } func NewCustomReader(reader io.Reader) *CustomReader { return &CustomReader{reader, 0} } func (cr *CustomReader) Read(slice []byte) (count int, err error) { count, err = cr.reader.Read(slice) cr.readCount++ asd.Printfln("Custom Reader: %v bytes", count) if err == io.EOF { asd.Printfln("Total Reads: %v", cr.readCount) } return } main.go: package main import ( "asd/asd" "io" "strings" ) func main() { text := "It was a boat. A small boat." var reader io.Reader = NewCustomReader(strings.NewReader(text)) var writer strings.Builder slice := make([]byte, 5) for { count, err := reader.Read(slice) if count > 0 { writer.Write(slice[0:count]) } if err != nil { break } } asd.Printfln("Read data: %v", writer.String()) } The NewCustomreader function is used to create a CustomReader that reads from a string and uses a for loop to consume the data using a byte slice. Output: Custom Reader: 5 bytes Custom Reader: 5 bytes Custom Reader: 5 bytes Custom Reader: 5 bytes Custom Reader: 5 bytes Custom Reader: 3 bytes Custom Reader: 0 bytes Total Reads: 7 Read data: It was a boat. A small boat. `, "289": `289.bufio Functions for Creating Buffered Readers Name Description ---------------------- --------------------------------- NewReader(r) This function returns a buffered Reader with the default buffer size (which is 4,096 bytes at the time of writing). NewReaderSize(r, size) This function returns a buffered Reader with the specified buffer size. `, "290": `290.bufio.NewReader(reader) The default buffer size is 4,096 bytes, which means that the buffered reader was able to read all the data in a single read operation, plus an additional read to produce the EOF result. Introducing the buffer reduces the overhead associated with the read operations, albeit at the cost of the memory used to buffer the data. example: package main import ( "io" "strings" "bufio" ) func main() { text := "It was a boat. A small boat." var reader io.Reader = NewCustomReader(strings.NewReader(text)) var writer strings.Builder slice := make([]byte, 5) reader = bufio.NewReader(reader) for { count, err := reader.Read(slice) if (count > 0) { writer.Write(slice[0:count]) } if (err != nil) { break } } Printfln("Read data: %v", writer.String()) } Output: Custom Reader: 28 bytes Custom Reader: 0 bytes Total Reads: 2 Read data: It was a boat. A small boat. `, "291": `291.Additional Buffered Reader Methods The NewReader and NewReaderSize functions return bufio.Reader values, which implement the io. Reader interface and which can be used as drop-in wrappers for other types of Reader methods, seamlessly introducing a read buffer. `, "292": `292.The Methods Defined by the Buffered Reader Name Description -------------- ------------------------------------------ Buffered() This method returns an int that indicates the number of bytes that can be read from the buffer. Discard(count) This method discards the specified number of bytes. Peek(count) This method returns the specified number of bytes without removing them from the buffer, meaning they will be returned by subsequent calls to the Read method. Reset(reader) This method discards the data in the buffer and performs subsequent reads from the specified Reader. Size() This method returns the size of the buffer, expressed int. `, "293": `293.buffered.Read(slice) example: package main import ( "io" "strings" "bufio" ) func main() { text := "It was a boat. A small boat." var reader io.Reader = NewCustomReader(strings.NewReader(text)) var writer strings.Builder slice := make([]byte, 5) buffered := bufio.NewReader(reader) for { count, err := buffered.Read(slice) if (count > 0) { Printfln("Buffer size: %v, buffered: %v", buffered.Size(), buffered.Buffered()) writer.Write(slice[0:count]) } if (err != nil) { break } } Printfln("Read data: %v", writer.String()) } Output: Custom Reader: 28 bytes Buffer size: 4096, buffered: 23 Buffer size: 4096, buffered: 18 Buffer size: 4096, buffered: 13 Buffer size: 4096, buffered: 8 Buffer size: 4096, buffered: 3 Buffer size: 4096, buffered: 0 Custom Reader: 0 bytes Total Reads: 2 Read data: It was a boat. A small boat. `, "294": `294.bufio Functions for Creating Buffered Writers Name Description ----------------------- -------------------------------------- NewWriter(w) This function returns a buffered Writer with the default buffer size (which is 4,096 bytes at the time of writing). NewWriterSize(w, size) This function returns a buffered Writer with the specified buffer size. `, "295": `295.Methods Defined by the bufio.Writer Struct Name Description -------------- ----------------------------------- Available() This method returns the number of available bytes in the buffer. Buffered() This method returns the number of bytes that have been written to the buffer. Flush() This method writes the contents of the buffer to the underlying Writer. Reset(writer) This method discards the data in the buffer and performs subsequent writes to the specified Writer. Size() This method returns the capacity of the buffer in bytes. `, "296": `296.Defining a Custom Writer example The NewCustomWriter constructor wraps a Writer with a CustomWriter struct, which reports on its write operations. custom.go: package main import ( "asd/asd" "io" ) type CustomReader struct { reader io.Reader readCount int } func NewCustomReader(reader io.Reader) *CustomReader { return &CustomReader{reader, 0} } func (cr *CustomReader) Read(slice []byte) (count int, err error) { count, err = cr.reader.Read(slice) cr.readCount++ asd.Printfln("Custom Reader: %v bytes", count) if err == io.EOF { asd.Printfln("Total Reads: %v", cr.readCount) } return } type CustomWriter struct { writer io.Writer writeCount int } func NewCustomWriter(writer io.Writer) *CustomWriter { return &CustomWriter{writer, 0} } func (cw *CustomWriter) Write(slice []byte) (count int, err error) { count, err = cw.writer.Write(slice) cw.writeCount++ asd.Printfln("Custom Writer: %v bytes", count) return } func (cw *CustomWriter) Close() (err error) { if closer, ok := cw.writer.(io.Closer); ok { closer.Close() } asd.Printfln("Total Writes: %v", cw.writeCount) return } main.go: package main import ( "strings" "asd/asd" ) func main() { text := "It was a boat. A small boat." var builder strings.Builder var writer = NewCustomWriter(&builder) for i := 0; true; { end := i + 5 if end >= len(text) { writer.Write([]byte(text[i:])) break } writer.Write([]byte(text[i:end])) i = end } asd.Printfln("Written data: %v", builder.String()) } Output: Custom Writer: 5 bytes Custom Writer: 5 bytes Custom Writer: 5 bytes Custom Writer: 5 bytes Custom Writer: 5 bytes Custom Writer: 3 bytes Written data: It was a boat. A small boat.`, "297": `297.Using a Buffered Writer in the main.go example example: main.go: package main import ( "strings" "asd/asd" "bufio" ) func main() { text := "It was a boat. A small boat." var builder strings.Builder var writer = bufio.NewWriterSize(NewCustomWriter(&builder), 20) for i := 0; true; { end := i + 5 if end >= len(text) { writer.Write([]byte(text[i:])) writer.Flush() break } writer.Write([]byte(text[i:end])) i = end } asd.Printfln("Written data: %v", builder.String()) } Output: Custom Writer: 20 bytes Custom Writer: 8 bytes Written data: It was a boat. A small boat. `, "298": `298.Scanning from a Reader example: main.go package main import ( "io" "strings" "asd/asd" "fmt" ) func scanFromReader(reader io.Reader, template string, vals ...interface{}) (int, error) { return fmt.Fscanf(reader, template, vals...) } func main() { reader := strings.NewReader("Kayak Watersports $279.00") var name, category string var price float64 scanTemplate := "%s %s $%f" _, err := scanFromReader(reader, scanTemplate, &name, &category, &price) if err != nil { asd.Printfln("Error: %v", err.Error()) } else { asd.Printfln("Name: %v", name) asd.Printfln("Category: %v", category) asd.Printfln("Price: %.2f", price) } } Output: Name: Kayak Category: Watersports Price: 279.00 `, "299": `299.Scanning Gradually اسکن به تدریج example: main.go package main import ( "io" "strings" "asd/asd" "fmt" ) func scanSingle(reader io.Reader, val interface{}) (int, error) { return fmt.Fscan(reader, val) } func main() { reader := strings.NewReader("Kayak Watersports $279.00") for { var str string _, err := scanSingle(reader, &str) if err != nil { if err != io.EOF { asd.Printfln("Error: %v", err.Error()) } break } asd.Printfln("Value: %v", str) } } Output: Value: Kayak Value: Watersports Value: $279.00 `, "300": `300.Writing Formatted Strings to a Writer The fmt package also provides functions for writing formatted strings to a Writer The writeFormatted function uses the fmt.Fprintf function to write a string formatted with a template to a Writer. example: main.go: package main import ( "io" "strings" "fmt" ) func writeFormatted(writer io.Writer, template string, vals ...interface{}) { fmt.Fprintf(writer, template, vals...) } func main() { var writer strings.Builder template := "Name: %s, Category: %s, Price: $%.2f" writeFormatted(&writer, template, "Kayak", "Watersports", float64(279)) fmt.Println(writer.String()) } Output: Name: Kayak, Category: Watersports, Price: $279.00 `, "301": `301.strings.Replacer struct The strings.Replacer struct can be used to perform replacements on a string and output the modified result to a Writer. example: package main import ( "fmt" "io" "strings" ) func writeReplaced(writer io.Writer, str string, subs ...string) { replacer := strings.NewReplacer(subs...) replacer.WriteString(writer, str) } func main() { text := "It was a boat. A small boat." subs := []string{"boat", "kayak", "small", "huge"} var writer strings.Builder writeReplaced(&writer, text, subs...) fmt.Println(writer.String()) } Output: It was a kayak. A huge kayak. `, "302": `302.Working with JSON Data Putting Working with JSON Data in Context What is it? JSON data is the de facto standard for exchanging data, especially in HTTP applications. Why is it useful? JSON is simple enough to be supported by any language but can represent relatively complex data. How is it used? The encoding/json package provides support for encoding and decoding JSON data. Are there any pitfalls or limitations? Not all Go data types can be represented in JSON, which requires the developer to be mindful of how Go data types will be expressed. Are there any alternatives? There are many other data encodings available, some of which are supported by the Go standard library. The safest approach is to define a map with string keys and empty interface values, which ensures that all the key-value pairs in the JSON data can be decoded into the map Problem Solution ----------- ------------------------ Encode JSON dataCreate an Encoder with a Writer and invoke the Encode method Control struct encoding Use JSON struct tags or implement the Mashaler interface Decode JSON data Create a Decoder with a Reader and invoke the Decode method Control struct decoding Use JSON struct tags or implement the Unmarshaler interface `, "303": `303.The encoding/json Constructor Functions for JSON Data Name Description ------------------- -------------------------------------------- NewEncoder(writer) This function returns an Encoder, which can be used to encode JSON data and write it to the specified Writer. NewDecoder(reader) This function returns a Decoder, which can be used to read JSON data from the specified Reader and decode it. `, "304": `304.The Functions for Creating and Parsing JSON Data Name Description ------------------------ ----------------------------------------------- Marshal(value) This function encodes the specified value as JSON. The results are the JSON content expressed in a byte slice and an error, which indicates any encoding problems. Unmarshal(byteSlice, val) This function parses JSON data contained in the specified slice of bytes and assigns the result to the specified value. `, "305": `305.The Encoder Methods The NewEncoder constructor function is used to create an Encoder, which can be used to write JSON data to a Writer. Name Description ------------------------- -------------------------------------------------- Encode(val) This method encodes the specified value as JSON and writes it to the Writer. SetEscapeHTML(on) This method accepts a bool argument that, when true, encodes characters that would be dangerous in HTML to be escaped. The default behavior is to escape these characters. SetIndent(prefix, indent) This method specifies a prefix and indentation that is applied to the name of each field in the JSON output. `, "306": `306.Expressing the Basic Go Data Types in JSON Data TypeDescription ---------------- ------------------------------------ bool Go bool values are expressed as JSON true or false. string Go string values are expressed as JSON strings. By default, unsafe HTML characters are escaped. float32, float64 Go floating-point values are expressed as JSON numbers. int, int<size> Go integer values are expressed as JSON numbers. uint, uint<size> Go integer values are expressed as JSON numbers. byte Go bytes are expressed as JSON numbers. rune Go runes are expressed as JSON numbers. nil The Go nil value is expressed as the JSON null value. Pointers The JSON encoder follows pointers and encodes the value at the pointer's location. `, "307": `307.encoding/json example: package main import ( "encoding/json" "fmt" "strings" ) func main() { var b bool = true var str string = "Hello" var fval float64 = 99.99 var ival int = 200 var pointer *int = &ival var writer strings.Builder encoder := json.NewEncoder(&writer) for _, val := range []interface{}{b, str, fval, ival, pointer} { encoder.Encode(val) } fmt.Print(writer.String()) } Output: true "Hello" 99.99 200 200 `, "308": `308.Encoding Slices and Arrays Example: package main import ( "encoding/json" "fmt" "strings" ) func main() { names := []string{"Kayak", "Lifejacket", "Soccer Ball"} numbers := [3]int{10, 20, 30} var byteArray [5]byte copy(byteArray[0:], []byte(names[0])) byteSlice := []byte(names[0]) var writer strings.Builder encoder := json.NewEncoder(&writer) encoder.Encode(names) encoder.Encode(numbers) encoder.Encode(byteArray) encoder.Encode(byteSlice) fmt.Print(writer.String()) } Output: ["Kayak","Lifejacket","Soccer Ball"] [10,20,30] [75,97,121,97,107] "S2F5YWs=" `, "309": `309.Encoding Maps Go maps are encoded as JSON objects, with the map keys used as the object keys. The values contained in the map are encoded based on their type. Maps can also be useful for creating custom JSON representations of Go data. encoder.Encode() example: package main import ( "encoding/json" "fmt" "strings" ) func main() { m := map[string]float64{ "Kayak": 279, "Lifejacket": 49.95, } var writer strings.Builder encoder := json.NewEncoder(&writer) encoder.Encode(m) fmt.Print(writer.String()) } Output: {"Kayak":279,"Lifejacket":49.95} `, "310": `310.Encoding Structs The Encoder expresses struct values as JSON objects, using the exported struct field names as the object's keys and the field values as the object's values example: package main import ( "encoding/json" "fmt" "strings" ) func main() { var writer strings.Builder encoder := json.NewEncoder(&writer) encoder.Encode(Kayak) fmt.Print(writer.String()) } Output: {"Name":"Kayak","Category":"Watersports","Price":279} `, "311": `311.Effect of Promotion in JSON in Encoding When a struct defines an embedded field that is also a struct, the fields of the embedded struct are promoted and encoded as though they are defined by the enclosing type. discount.go: package main type DiscountedProduct struct { *Product Discount float64 } main.go: package main import ( "encoding/json" "fmt" "strings" ) func main() { var writer strings.Builder encoder := json.NewEncoder(&writer) dp := DiscountedProduct { Product: &Kayak, Discount: 10.50, } encoder.Encode(&dp) fmt.Print(writer.String()) } Output: {"Name":"Kayak","Category":"Watersports","Price":279,"Discount":10.5} `, "312": `312.Customizing the JSON Encoding of Structs How a struct is encoded can be customized using struct tags, which are string literals that follow fields. Struct tags are part of the Go support for reflection, that tags follow fields and can be used to alter two aspects of how a field is encoded in JSON. example: discount.go: package main type DiscountedProduct struct { *Product ^json:"product"^ // -------------------------> Use the symbol ^ above the Tab button instead Discount float64 } =============================================================== Output: {"product":{"Name":"Kayak","Category":"Watersports","Price":279},"Discount":10.5} `, "313": `313.Omitting a Field The Encoder skips fields decorated with a tag that specifies a hyphen (the - character) for the name The new tag tells the Encoder to skip the Discount field when creating the JSON representation of a DIscountedProduct value. exampe: discount.go: package main type DiscountedProduct struct { *Product ^json:"product"^ // -------------------------> Use the symbol ^ above the Tab button instead Discount float64 ^json:"-"^ // -------------------------> Use the symbol ^ above the Tab button instead } Output: {"product":{"Name":"Kayak","Category":"Watersports","Price":279}} `, "314": `314.Omitting Unassigned Fields By default, the JSON Encoder includes struct fields, even when they have not been assigned a value example: main.go: package main import ( "encoding/json" "fmt" "strings" ) func main() { var writer strings.Builder encoder := json.NewEncoder(&writer) dp := DiscountedProduct{ Product: &Kayak, Discount: 10.50, } encoder.Encode(&dp) dp2 := DiscountedProduct { Discount: 10.50 } encoder.Encode(&dp2) fmt.Print(writer.String()) } ======================================================= Output: {"product":{"Name":"Kayak","Category":"Watersports","Price":279}} {"product":null} ======================================================= To omit a nil field, the omitempty keyword is added to the tag for the field discount.go: package main type DiscountedProduct struct { // *Product ^json:"product"^ // -------------------------> Use the symbol ^ above the Tab button instead *Product ^json:"product,omitempty"^ // -------------------------> Use the symbol ^ above the Tab button instead Discount float64 ^json:"-"^ } ======================================================= Output: {"product":{"Name":"Kayak","Category":"Watersports","Price":279}} {} ======================================================= To skip a nil field without changing the name or field promotion, specify the omitempty keyword without a name discount.go: package main type DiscountedProduct struct { // *Product ^json:"product"^ // *Product ^json:"product,omitempty"^ *Product ^json:",omitempty"^ // -------------------------> Use the symbol ^ above the Tab button instead Discount float64 ^json:"-"^ // -------------------------> Use the symbol ^ above the Tab button instead } ====================================================== Output: {"Name":"Kayak","Category":"Watersports","Price":279} {} `, "315": `315.Forcing Fields to be Encoded as Strings Struct tags can be used to force a field value to be encoded as a string, overriding the normal encoding for the field type example: discount.go: package main type DiscountedProduct struct { *Product ^json:",omitempty"^ // Discount float64 ^json:"-"^ // -------------------------> Use the symbol ^ above the Tab button Discount float64 ^json:",string"^ // -------------------------> Use the symbol ^ above the Tab button } Output: {"Name":"Kayak","Category":"Watersports","Price":279,"Discount":"10.5"} {"Discount":"10.5"} `, "316": `316.Encoding Interfaces The JSON encoder can be used on values assigned to interface variables, but it is the dynamic type that is encoded. No aspect of the interface is used to adapt the JSON, and all the exported fields of each value in the slice are included in the JSON. This can be a useful feature, but care must be taken when decoding this kind of JSON, because each value can have a different set of fields example: interface.go: package main type Named interface{ GetName() string } type Person struct{ PersonName string } func (p *Person) GetName() string { return p.PersonName } func (p *DiscountedProduct) GetName() string { return p.Name } ======================================================= main.go: package main import ( "encoding/json" "fmt" "strings" ) func main() { var writer strings.Builder encoder := json.NewEncoder(&writer) dp := DiscountedProduct{ Product: &Kayak, Discount: 10.50, } namedItems := []Named { &dp, &Person{ PersonName: "Alice"}} encoder.Encode(namedItems) fmt.Print(writer.String()) } ======================================================= Output: [{"Name":"Kayak","Category":"Watersports","Price":279,"Discount":"10.5"},{"PersonName":"Alice"}] `, "317": `317.The Marshaler Method Creating Completely Custom JSON Encodings The Encoder checks to see whether a struct implements the Marshaler interface, which denotes a type that has a custom encoding and which defines the method. Name Description -------------- ------------------------------------------ MarshalJSON() This method is invoked to create a JSON representation of a value and returns a byte slice containing the JSON and an error indicating encoding problems. `, "318": `318.json.Marshal() The MarshalJSON method can generate JSON in any way that suits the project. I define a map with string keys and use the empty interface for the values. This allows me to build the JSON by adding key-value pairs to the map and then pass the map to the Marshal function, which uses the built-in support to encode each of the values contained in the map. example: discount.go: package main import "encoding/json" type DiscountedProduct struct { *Product ^json:",omitempty"^ Discount float64 ^json:",string"^ } func (dp *DiscountedProduct) MarshalJSON() (jsn []byte, err error) { if dp.Product != nil { m := map[string]interface{}{ "product": dp.Name, "cost": dp.Price - dp.Discount, } jsn, err = json.Marshal(m) } return } =========================================================== main.go: package main import ( "encoding/json" "fmt" "strings" ) func main() { var writer strings.Builder encoder := json.NewEncoder(&writer) dp := DiscountedProduct{ Product: &Kayak, Discount: 10.50, } namedItems := []Named { &dp, &Person{ PersonName: "Alice"}} encoder.Encode(namedItems) fmt.Print(writer.String()) } =========================================================== Output: [{"cost":268.5,"product":"Kayak"},{"PersonName":"Alice"}]`, "319": `319.Decoding JSON Data The NewDecoder constructor function creates a Decoder, which can be used to decode JSON data obtained from a Reader. The Decoder Methods: Name Description --------------------- -------------------------------------------- Decode(value) This method reads and decodes data, which is used to create the specified value. The method returns an error that indicates problems decoding the data to the required type or EOF. DisallowUnknownFields() By default, when decoding a struct type, the Decoder ignores any key in the JSON data for which there is no corresponding struct field. Calling this method causes the Decode to return an error, rather than ignoring the key. UseNumber() By default, JSON number values are decoded into float64 values. Calling this method uses the Number type instead, as described in the “Decoding Number Values” section. `, "320": `320.Decoding Basic Data Types example: main.go: package main import ( "encoding/json" "io" "strings" "asd/asd" ) func main() { reader := strings.NewReader(^true "Hello" 99.99 200^) // -------------------------> Use the symbol ^ above the Tab button vals := []interface{}{} decoder := json.NewDecoder(reader) for { var decodedVal interface{} err := decoder.Decode(&decodedVal) if err != nil { if err != io.EOF { asd.Printfln("Error: %v", err.Error()) } break } vals = append(vals, decodedVal) } for _, val := range vals { asd.Printfln("Decoded (%T): %v", val, val) } } Output: Decoded (bool): true Decoded (string): Hello Decoded (float64): 99.99 Decoded (float64): 200 `, "321": `321.Decoding Number Values The Methods Defined by the Number Type Name Description --------- ------------------------------------ Int64() This method returns the decoded value as a int64 and an error that indicates if the value cannot be converted. Float64() This method returns the decoded value as a float64 and an error that indicates if the value cannot be converted. String() This method returns the unconverted string from the JSON data. Not all JSON number values can be expressed as Go int64 values, so this is the method that is typically called first. If attempting to convert to an integer fails, then the Float64 method can be called. If a number cannot be converted to either Go type, then the String method can be used to get the unconverted string from the JSON data. `, "322": `322.Decoding Numbers example: main.go: package main import ( "encoding/json" "io" "strings" ) func main() { reader := strings.NewReader(^true "Hello" 99.99 200^) // -------------------------> Use the symbol ^ above the Tab button vals := []interface{}{} decoder := json.NewDecoder(reader) for { var decodedVal interface{} err := decoder.Decode(&decodedVal) if err != nil { if err != io.EOF { Printfln("Error: %v", err.Error()) } break } vals = append(vals, decodedVal) } for _, val := range vals { if num, ok := val.(json.Number); ok { if ival, err := num.Int64(); err == nil { Printfln("Decoded Integer: %v", ival) } else if fpval, err := num.Float64(); err == nil { Printfln("Decoded Floating Point: %v", fpval) } else { Printfln("Decoded String: %v", num.String()) } } else { Printfln("Decoded (%T): %v", val, val) } } } Output: Decoded (bool): true Decoded (string): Hello Decoded (float64): 99.99 Decoded (float64): 200 `, "323": `323.Specifying Types for Decoding example: main.go: package main import ( "encoding/json" "strings" ) func main() { reader := strings.NewReader(^true "Hello" 99.99 200^) // -------------------------> Use the symbol ^ above the Tab button var bval bool var sval string var fpval float64 var ival int vals := []interface{}{&bval, &sval, &fpval, &ival} decoder := json.NewDecoder(reader) for i := 0; i < len(vals); i++ { err := decoder.Decode(vals[i]) if err != nil { Printfln("Error: %v", err.Error()) break } } Printfln("Decoded (%T): %v", bval, bval) Printfln("Decoded (%T): %v", sval, sval) Printfln("Decoded (%T): %v", fpval, fpval) Printfln("Decoded (%T): %v", ival, ival) } Output: Decoded (bool): true Decoded (string): Hello Decoded (float64): 99.99 Decoded (int): 200 `, "324": `324.Decoding Arrays The Decoder processes arrays automatically, but care must be taken because JSON allows arrays to contain values of different types, which conflicts with the strict type rules enforced by Go. The source JSON data contains two arrays, one of which contains only numbers and one of which mixes numbers and strings. The Decoder doesn't try to figure out if a JSON array can be represented using a single Go type and decodes every array into an empty interface slice: example: main.go: package main import ( "encoding/json" "io" "strings" ) func main() { reader := strings.NewReader(^[10,20,30]["Kayak","Lifejacket",279]^) // -------------------------> Use the symbol ^ above the Tab button vals := []interface{}{} decoder := json.NewDecoder(reader) for { var decodedVal interface{} err := decoder.Decode(&decodedVal) if err != nil { if err != io.EOF { Printfln("Error: %v", err.Error()) } break } vals = append(vals, decodedVal) } for _, val := range vals { Printfln("Decoded (%T): %v", val, val) } } Output: Decoded ([]interface {}): [10 20 30] Decoded ([]interface {}): [Kayak Lifejacket 279] `, "325": `325.Specifying the Decoded Array Type The second array contains a mix of values, which means that I have to specify the empty interface as the target type. The literal slice syntax is awkward when using the empty interface because two sets of braces are required: ... mixed := []interface{} {} example: main.go: package main import ( "encoding/json" "strings" ) func main() { reader := strings.NewReader(^[10,20,30]["Kayak","Lifejacket",279]^) // -------------------------> Use the symbol ^ above the Tab button ints := []int {} mixed := []interface{} {} vals := []interface{} { &ints, &mixed} decoder := json.NewDecoder(reader) for i := 0; i < len(vals); i++ { err := decoder.Decode(vals[i]) if err != nil { Printfln("Error: %v", err.Error()) break } } Printfln("Decoded (%T): %v", ints, ints) Printfln("Decoded (%T): %v", mixed, mixed) } Output: Decoded ([]int): [10 20 30] Decoded ([]interface {}): [Kayak Lifejacket 279] `, "326": `326.Decoding Maps The safest approach is to define a map with string keys and empty interface values, which ensures that all the key-value pairs in the JSON data can be decoded into the map JavaScript objects are expressed as key-value pairs, which makes it easy to decode them into Go maps example: main.go: package main import ( "encoding/json" "strings" ) func main() { reader := strings.NewReader(^{"Kayak" : 279, "Lifejacket" : 49.95}^) // -------------------------> Use the symbol ^ above the Tab button m := map[string]interface{}{} decoder := json.NewDecoder(reader) err := decoder.Decode(&m) if err != nil { Printfln("Error: %v", err.Error()) } else { Printfln("Map: %T, %v", m, m) for k, v := range m { Printfln("Key: %v, Value: %v", k, v) } } } Output: Map: map[string]interface {}, map[Kayak:279 Lifejacket:49.95] Key: Kayak, Value: 279 Key: Lifejacket, Value: 49.95 `, "327": `327.a Specific Value Type A single JSON object can be used for multiple data types as values, but if you know in advance that you will be decoding a JSON object that has a single value type, then you can be more specific when defining the map into which the data will be decoded. example: main.go: package main import ( "encoding/json" "strings" ) func main() { reader := strings.NewReader(^{"Kayak" : 279, "Lifejacket" : 49.95}^) // -------------------------> Use the symbol ^ above the Tab button m := map[string]float64 {} decoder := json.NewDecoder(reader) err := decoder.Decode(&m) if err != nil { Printfln("Error: %v", err.Error()) } else { Printfln("Map: %T, %v", m, m) for k, v := range m { Printfln("Key: %v, Value: %v", k, v) } } } Output: Map: map[string]float64, map[Kayak:279 Lifejacket:49.95] Key: Kayak, Value: 279 Key: Lifejacket, Value: 49.95 `, "328": `328.Decoding Structs The Decoder decodes the JSON object and uses the keys to set the values of the exported struct fields. The capitalization of the fields and JSON keys don't have to match, and the Decoder will ignore any JSON key for which there isn't a struct field and ignore any struct field for which there is no JSON key. The JSON objectscontain different capitalization and have more or fewer keys than the Product struct fields. The Decoder processes the data as best as it can. example: main.go: package main import ( "strings" "encoding/json" "io" ) func main() { reader := strings.NewReader(^ // -------------------------> Use the symbol ^ above the Tab button {"Name":"Kayak","Category":"Watersports","Price":279} {"Name":"Lifejacket","Category":"Watersports" } {"name":"Canoe","category":"Watersports", "price": 100, "inStock": true } ^) // -------------------------> Use the symbol ^ above the Tab button decoder := json.NewDecoder(reader) for { var val Product err := decoder.Decode(&val) if err != nil { if err != io.EOF { Printfln("Error: %v", err.Error()) } break } else { Printfln("Name: %v, Category: %v, Price: %v", val.Name, val.Category, val.Price) } } } Output: Name: Kayak, Category: Watersports, Price: 279 Name: Lifejacket, Category: Watersports, Price: 0 Name: Canoe, Category: Watersports, Price: 100 `, "329": `329.Decoding to Interface Types As I explained earlier in the chapter, the JSON encoder deals with interfaces by encoding the value using the exported fields of the dynamic type. This is because JSON deals with key-value pairs and has no way to express methods. As a consequence, you cannot decode directly to an interface variable from JSON. Instead, you must decode to a struct or map and then assign the value that is created to an interface variable. `, "330": `330.Disallowing Unused Keys By default, the Decoder will ignore JSON keys for which there is no corresponding struct field. This behavior can be changed by calling the DisallowUnknownFields method example: Disallowing Unused Keys in the main.go ... decoder := json.NewDecoder(reader) decoder.DisallowUnknownFields() ... output: Name: Kayak, Category: Watersports, Price: 279 Name: Lifejacket, Category: Watersports, Price: 0 Error: json: unknown field "inStock" `, "331": `331.Struct Tags The tag applied to the Discount field tells the Decoder that the value for this field should be obtained from the JSON key named offer and that the value will be parsed from a string, instead of the JSON number that would usually be expected for a Go float64 value. example: discount.go: package main import "encoding/json" type DiscountedProduct struct { *Product ^json:",omitempty"^ // -------------------------> Use the symbol ^ above the Tab button Discount float64 ^json:"offer,string"^ // -------------------------> Use the symbol ^ above the Tab button } func (dp *DiscountedProduct) MarshalJSON() (jsn []byte, err error) { if (dp.Product != nil) { m := map[string]interface{} { "product": dp.Name, "cost": dp.Price - dp.Discount, } jsn, err = json.Marshal(m) } return } main.go: package main import ( "strings" "encoding/json" "io" ) func main() { reader := strings.NewReader(^ {"Name":"Kayak","Category":"Watersports","Price":279, "Offer": "10"}^) // -------------------------> Use the symbol ^ above the Tab button decoder := json.NewDecoder(reader) for { var val DiscountedProduct err := decoder.Decode(&val) if err != nil { if err != io.EOF { Printfln("Error: %v", err.Error()) } break } else { Printfln("Name: %v, Category: %v, Price: %v, Discount: %v", val.Name, val.Category, val.Price, val.Discount) } } } Output: Name: Kayak, Category: Watersports, Price: 279, Discount: 10 `, "332": `332.Creating Completely Custom JSON Decoders The Unmarshaler Method Name Description ------------------------ ------------------------------------------ UnmarshalJSON(byteSlice) This method is invoked to decode JSON data contained in the specified byte slice. The result is an error indicating encoding problems. `, "333": `333.Defining a Custom Decoder This implementation of the UnmarshalJSON method uses the Unmarshal method to decode the JSON data into a map and then checks the type of each value required for the DiscountedProduct struct. example: discount.go: package main import ( "encoding/json" "strconv" ) type DiscountedProduct struct { *Product ^json:",omitempty"^ // -------------------------> Use the symbol ^ above the Tab button Discount float64 ^json:"offer,string"^ // -------------------------> Use the symbol ^ above the Tab button } func (dp *DiscountedProduct) MarshalJSON() (jsn []byte, err error) { if dp.Product != nil { m := map[string]interface{}{ "product": dp.Name, "cost": dp.Price - dp.Discount, } jsn, err = json.Marshal(m) } return } func (dp *DiscountedProduct) UnmarshalJSON(data []byte) (err error) { mdata := map[string]interface{}{} err = json.Unmarshal(data, &mdata) if dp.Product == nil { dp.Product = &Product{} } if err == nil { if name, ok := mdata["Name"].(string); ok { dp.Name = name } if category, ok := mdata["Category"].(string); ok { dp.Category = category } if price, ok := mdata["Price"].(float64); ok { dp.Price = price } if discount, ok := mdata["Offer"].(string); ok { fpval, fperr := strconv.ParseFloat(discount, 64) if fperr == nil { dp.Discount = fpval } } } return } Output: Name: Kayak, Category: Watersports, Price: 279, Discount: 10 `, "334": `334.Working with Files Putting Working with Files in Context Answer Question --------------------------- ------------------------------------------------------- What are they? These features provide access to the file system so that files can be read and written. Why are they useful? Files are used for everything from logging to configuration files. How are they used? These features are accessed through the os package, which provides platform- neutral access to the file system. Are there any pitfallsor limitations? Some consideration of the underlying file system must be made, especially when dealing with paths. Are there any alternatives? Go supports alternative ways of storing data, such as databases, but there are no alternative mechanisms for accessing files. Preparing printer.go: package main import ( "fmt" ) func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } product.go: package main type Product struct { Name, Category string Price float64 } var Kayak = Product{ Name: "Kayak", Category: "Watersports", Price: 279, } var Products = []Product{ {"Kayak", "Watersports", 279}, {"Lifejacket", "Watersports", 49.95}, {"Soccer Ball", "Soccer", 19.50}, {"Corner Flags", "Soccer", 34.95}, {"Stadium", "Soccer", 79500}, {"Thinking Cap", "Chess", 16}, {"Unsteady Chair", "Chess", 75}, {"Bling-Bling King", "Chess", 1200}, } main.go: package main func main() { for _, p := range Products { Printfln("Product: %v, Category: %v, Price: $%.2f", p.Name, p.Category, p.Price) } } Output: Product: Kayak, Category: Watersports, Price: $279.00 Product: Lifejacket, Category: Watersports, Price: $49.95 Product: Soccer Ball, Category: Soccer, Price: $19.50 Product: Corner Flags, Category: Soccer, Price: $34.95 Product: Stadium, Category: Soccer, Price: $79500.00 Product: Thinking Cap, Category: Chess, Price: $16.00 Product: Unsteady Chair, Category: Chess, Price: $75.00 Product: Bling-Bling King, Category: Chess, Price: $1200.00 `, "335": `335.The os Package Functions for Reading Files Name Description ------------- ------------------------------------- ReadFile(name) This function opens the specified file and reads its contents. The results are a byte slice containing the file content and an error indicating problems opening or reading the file. Open(name) This function opens the specified file for reading. The result is a File struct and an error that indicates problems opening the file. One of the most common reasons to read a file is to load configuration data. The JSON format is well- suited for configuration files because it is simple to process, has good support in the Go standard library The Contents of the config.json File in the files Folder: { "Username": "Alice", "AdditionalProducts": [ {"name": "Hat", "category": "Skiing", "price": 10}, {"name": "Boots", "category":"Skiing", "price": 220.51 }, {"name": "Gloves", "category":"Skiing", "price": 40.20 } ] } `, "336": `336.os.ReadFile() The LoadConfig function uses the ReadFile function to read the contents of the config.json file. The file will be read from the current working directory when the application is executed, which means that I can open the file just with its name. The contents of the file are returned as a byte slice, which is converted to a string and written out. The LoadConfig function is invoked by an initialization function, which ensures the configuration file is read. example: readconfig.go: package main import "os" func LoadConfig() (err error) { data, err := os.ReadFile("config.json") if err == nil { Printfln(string(data)) } return } func init() { err := LoadConfig() if err != nil { Printfln("Error Loading Config: %v", err.Error()) } } Output: { "Username": "Alice", "AdditionalProducts": [ {"name": "Hat", "category": "Skiing", "price": 10}, {"name": "Boots", "category":"Skiing", "price": 220.51 }, {"name": "Gloves", "category":"Skiing", "price": 40.20 } ] } Product: Kayak, Category: Watersports, Price: $279.00 Product: Lifejacket, Category: Watersports, Price: $49.95 Product: Soccer Ball, Category: Soccer, Price: $19.50 Product: Corner Flags, Category: Soccer, Price: $34.95 Product: Stadium, Category: Soccer, Price: $79500.00 Product: Thinking Cap, Category: Chess, Price: $16.00 Product: Unsteady Chair, Category: Chess, Price: $75.00 Product: Bling-Bling King, Category: Chess, Price: $1200.00 `, "337": `337.Decoding the JSON Data example: readconfig.go: package main import ( "encoding/json" "os" "strings" ) type ConfigData struct { UserName string AdditionalProducts []Product } var Config ConfigData func LoadConfig() (err error) { data, err := os.ReadFile("config.json") if err == nil { decoder := json.NewDecoder(strings.NewReader(string(data))) err = decoder.Decode(&Config) } return } func init() { err := LoadConfig() if err != nil { Printfln("Error Loading Config: %v", err.Error()) } else { Printfln("Username: %v", Config.UserName) Products = append(Products, Config.AdditionalProducts...) } } Output: Username: Alice Product: Kayak, Category: Watersports, Price: $279.00 Product: Lifejacket, Category: Watersports, Price: $49.95 Product: Soccer Ball, Category: Soccer, Price: $19.50 Product: Corner Flags, Category: Soccer, Price: $34.95 Product: Stadium, Category: Soccer, Price: $79500.00 Product: Thinking Cap, Category: Chess, Price: $16.00 Product: Unsteady Chair, Category: Chess, Price: $75.00 Product: Bling-Bling King, Category: Chess, Price: $1200.00 Product: Hat, Category: Skiing, Price: $10.00 Product: Boots, Category: Skiing, Price: $220.51 Product: Gloves, Category: Skiing, Price: $40.20 `, "338": `338.os.Open("config.json") Using the File Struct to Read a File The Open function opens a file for reading and returns a File value, which represents the open file, and an error, which is used to indicate problems opening the file. The File struct implements the Reader interface, which makes it simple to read and process the example JSON data, without reading the entire file into a byte slice. The File struct also implements the Closer interface, which defines a Close method. The defer keyword can be used to call the Close method when the enclosing function completes, like this: defer file.Close() using the defer keyword ensures that the file is closed even when a function returns early. example: readconfig.go: package main import ( "encoding/json" "os" "time" ) type ConfigData struct { UserName string AdditionalProducts []Product } var Config ConfigData func LoadConfig() (err error) { file, err := os.Open("config.json") if (err == nil) { defer file.Close() decoder := json.NewDecoder(file) err = decoder.Decode(&Config) } return } func init() { time.Sleep(time.Second) } func init() { err := LoadConfig() if err != nil { Printfln("Error Loading Config: %v", err.Error()) } else { Printfln("Username: %v", Config.UserName) Products = append(Products, Config.AdditionalProducts...) } } Output: Username: Alice Product: Kayak, Category: Watersports, Price: $279.00 Product: Lifejacket, Category: Watersports, Price: $49.95 Product: Soccer Ball, Category: Soccer, Price: $19.50 Product: Corner Flags, Category: Soccer, Price: $34.95 Product: Stadium, Category: Soccer, Price: $79500.00 Product: Thinking Cap, Category: Chess, Price: $16.00 Product: Unsteady Chair, Category: Chess, Price: $75.00 Product: Bling-Bling King, Category: Chess, Price: $1200.00 Product: Hat, Category: Skiing, Price: $10.00 Product: Boots, Category: Skiing, Price: $220.51 Product: Gloves, Category: Skiing, Price: $40.20 `, "339": `339.Methods Defined by the File Struct for Reading at a Specific Location Name Description -------------------- ------------------------------------------ ReadAt(slice, offset) This method is defined by the ReaderAt interface and performs a read into the specific slice at the specified position offset in the file. Seek(offset, how) This method is defined by the Seeker interface and moves the offset into جستجو کنید the file for the next read. The offset is determined by the combination of the two arguments: the first argument specifies the number of bytes to offset, and the second argument determines how the offset is applied—a value of 0 means the offset is relative to the start of the file, a value of 1 means the offset is relative to the current read position, and a value of 2 means the offset is relative to the end of the file. Reading from specific locations requires knowledge of the file structure. In this example, I know the location of the data I want to read, which allows me to use the ReadAt method to read the username value and the Seek method to jump to the start of the product data. example: readconfig.go: package main import ( "os" "encoding/json" //"strings" ) type ConfigData struct { UserName string AdditionalProducts []Product } var Config ConfigData func LoadConfig() (err error) { file, err := os.Open("config.json") if (err == nil) { defer file.Close() nameSlice := make([]byte, 5) file.ReadAt(nameSlice, 19) Config.UserName = string(nameSlice) file.Seek(55, 0) decoder := json.NewDecoder(file) err = decoder.Decode(&Config.AdditionalProducts) } return } func init() { err := LoadConfig() if err != nil { Printfln("Username: %v", Config.UserName) Products = append(Products, Config.AdditionalProducts...) } else { Printfln("Error Loading Config: %v", err.Error()) } } Output: Username: Alice Product: Kayak, Category: Watersports, Price: $279.00 Product: Lifejacket, Category: Watersports, Price: $49.95 Product: Soccer Ball, Category: Soccer, Price: $19.50 Product: Corner Flags, Category: Soccer, Price: $34.95 Product: Stadium, Category: Soccer, Price: $79500.00 Product: Thinking Cap, Category: Chess, Price: $16.00 Product: Unsteady Chair, Category: Chess, Price: $75.00 Product: Bling-Bling King, Category: Chess, Price: $1200.00 `, "340": `340.The os Package Function for Writing Files Name Description ------------------------------- ---------------------------------------- WriteFile(name,slice, modePerms) This function creates a file with the specified name, mode, and permissions and writes the content of the specified byte slice. If the file already exists, its contents will be replaced with the byte slice. The result is an error that reports any problems creating the file or writing the data. OpenFile(name, flag, modePerms) The function opens the file with the specified name, using the flags to control how the file is opened. If a new file is created, then the specified mode and permissions are applied. The result is a File value that provides access to the file contents and an error that indicates problems opening the file. `, "341": `341.the Write Convenience Function The file mode is used to specify special characteristics for the file, but a value of zero is used for regular files, as in the example. You can find a list of the file mode values and their settings at https://golang.org/pkg/io/fs/#FileMode https://cs.opensource.google/go/go/+/go1.21.1:src/io/fs/fs.go;l=165 type FileMode type FileMode uint32 A FileMode represents a file's mode and permission bits. The bits have the same definition on all systems, so that information about files can be moved from one system to another portably. Not all bits apply to all systems. The only required bit is ModeDir for directories. const ( // The single letters are the abbreviations // used by the String method's formatting. ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory ModeAppend // a: append-only ModeExclusive // l: exclusive use ModeTemporary // T: temporary file; Plan 9 only ModeSymlink // L: symbolic link ModeDevice // D: device file ModeNamedPipe // p: named pipe (FIFO) ModeSocket // S: Unix domain socket ModeSetuid // u: setuid ModeSetgid // g: setgid ModeCharDevice // c: Unix character device, when ModeDevice is set ModeSticky // t: sticky ModeIrregular // ?: non-regular file; nothing else is known about this file // Mask for the type bits. For regular files, none will be set. ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular ModePerm FileMode = 0777 // Unix permission bits ) The defined file mode bits are the most significant bits of the FileMode. The nine least-significant bits are the standard Unix rwxrwxrwx permissions. The values of these bits should be considered part of the public API and may be used in wire protocols or disk representations: they must not be changed, although new bits might be added. example: main.go: package main import ( "fmt" "os" "time" ) func main() { total := 0.0 for _, p := range Products { total += p.Price } dataStr := fmt.Sprintf("Time: %v, Total: $%.2f\n",time.Now().Format("Mon 15:04:05"), total) err := os.WriteFile("output.txt", []byte(dataStr), 0666) if err == nil { fmt.Println("Output file created") } else { Printfln("Error: %v", err.Error()) } } Output: a file create with this name= output.txt Time: Thu 07:27:34, Total: $279.00 `, "342": `342.Using the File Struct to Write to a File The OpenFile function opens a file and returns a File value. Unlike the Open function, the OpenFile function accepts one or more flags that specify how the file should be opened. The flags are defined as constants in the os package, Care must be taken with these flags, not all of which are supported by every operating system. `, "343": `343.The File Opening Flags Name Description -------- ------------------------------------------ O_RDONLY This flag opens the file read-only so that it can be read from but not written to. O_WRONLY This flag opens the file write-only so that it can be written to but not read from. O_RDWR This flag opens the file read-write so that it can be written to and read from. O_APPEND This flag will append writes to the end of the file. O_CREATE This flag will create the file if it doesn't exist. O_EXCL This flag is used in conjunction with O_CREATE to ensure that a new file is created. If the file already exists, this flag will trigger an error. O_SYNC This flag enables synchronous writes, such that data is written to the storage device before the write function/method returns. O_TRUNC This flag truncates the existing content in the file. `, "344": `344.Writing to a File example: main.go: package main import ( "fmt" "time" "os" ) func main() { total := 0.0 for _, p := range Products { total += p.Price } dataStr := fmt.Sprintf("Time: %v, Total: $%.2f\n", time.Now().Format("Mon 15:04:05"), total) file, err := os.OpenFile("output.txt",os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0666) if (err == nil) { defer file.Close() file.WriteString(dataStr) } else { Printfln("Error: %v", err.Error()) } } I combined the O_WRONLY flag to open the file for writing, the O_CREATE file to create if it doesn't already exist, and the O_APPEND flag to append any written data to the end of the file. Output: appended to file exist: Time: Thu 07:27:34, Total: $279.00 Time: Thu 08:17:05, Total: $81174.40 Time: Thu 08:17:09, Total: $81174.40 `, "345": `345.The File Methods for Writing Data Name Description ----------------------- --------------------------------------------------- Seek(offset, how) This method sets the location for subsequent operations. Write(slice) This method writes the contents of the specified byte slice to the file. The results are the number of bytes written and an error that indicates problems writing the data. WriteAt(slice, offset) This method writes the data in the slice at the specified location and is the counterpart to the ReadAt method. WriteString(str) This method writes a string to the file. This is a convenience method that converts the string to a byte slice, invokes the Write method, and returns the results it receives. `, "346": `346.Writing JSON Data to a File example: main.go: package main import ( // "fmt" // "time" "encoding/json" "os" ) func main() { cheapProducts := []Product{} for _, p := range Products { if p.Price < 100 { cheapProducts = append(cheapProducts, p) } } file, err := os.OpenFile("cheap.json", os.O_WRONLY|os.O_CREATE, 0666) if err == nil { defer file.Close() encoder := json.NewEncoder(file) encoder.Encode(cheapProducts) } else { Printfln("Error: %v", err.Error()) } } Output: create file = cheap.json content of this: [{"Name":"Lifejacket","Category":"Watersports","Price":49.95},{"Name":"Soccer Ball","Category":"Soccer","Price":19.5},{"Name":"Corner Flags","Category":"Soccer","Price":34.95},{"Name":"Thinking Cap","Category":"Chess","Price":16},{"Name":"Unsteady Chair","Category":"Chess","Price":75}] `, "347": `347.the Convenience Functions to Create New Files The os Package Functions for Creating Files The CreateTemp function can be useful, but it is important to understand that the purpose of this function is to generate a random filename and that in all other respects, the file that is created is just a regular file. The file that is created isn't removed automatically and will remain on the storage device after the application has been executed. Name Description ------------------------- -------------------------------------------- Create(name) This function is equivalent to calling OpenFile with the O_RDWR, O_CREATE, and O_TRUNC flags. The results are the File, which can be used for reading and writing, and an error that is used to indicate problems creating the file. Note that this combination of flags means that if a file exists with the specified name, it will be opened, and its contents will be deleted. CreateTemp(dirName, fileName) This function creates a new file in the directory with the specified name. If the name is the empty string, then the system temporary directory is used, obtained using the TempDir function. The file is created with a name that contains a random sequence of characters, as demonstrated in the text after the table. The file is opened with the O_RDWR, O_CREATE, and O_EXCL flags. The file isn't removed when it is closed. `, "348": `348.Creating a Temporary File The location of the temporary file is specified with a period, meaning the current working directory. if the empty string is used, then the file will be created in the default temporary directory, which is obtained using the TempDir function described. The name of the file can include an asterisk (the * character), and if this is present, the random part of the filename will replace it. If the filename does not contain an asterisk, then the random part of the filename will be added to the end of the name. ompile and execute the project, and once execution is complete, you will see a new file in the files folder. The file in my project is named tempfile-1732419518.json, but your filename will be different, and you will see a new file and a unique name each time the program is executed. example: main.go: package main import ( "os" "encoding/json" ) func main() { cheapProducts := []Product {} for _, p := range Products { if (p.Price < 100) { cheapProducts = append(cheapProducts, p) } } file, err := os.CreateTemp(".", "tempfile-*.json") if (err == nil) { defer file.Close() encoder := json.NewEncoder(file) encoder.Encode(cheapProducts) } else { Printfln("Error: %v", err.Error()) } } Output: tempfile-1982129407.json: [{"Name":"Lifejacket","Category":"Watersports","Price":49.95},{"Name":"Soccer Ball","Category":"Soccer","Price":19.5},{"Name":"Corner Flags","Category":"Soccer","Price":34.95},{"Name":"Thinking Cap","Category":"Chess","Price":16},{"Name":"Unsteady Chair","Category":"Chess","Price":75}] `, "349": `349.Working with File Paths If you want to read and write files in other locations, then you must specify file paths. The issue is that not all of the operating systems that Go supports express file paths in the same way. For example, the path to a file named mydata.json in my home directory on a Linux system might be expressed like this: /home/adam/mydata.json where the path to the same in my home directory is expressed like this: C:\Users\adam\mydata.json `, "350": `350.The Common Location Functions Defined by the os Package Name Description -------------- ------------------------------------ Getwd() This function returns the current working directory, expressed as a string, and an error that indicates problems obtaining the value. UserHomeDir() This function returns the user's home directory and an error that indicates problems obtaining the path. UserCacheDir() This function returns the default directory for user-specific cached data and an error that indicates problems obtaining the path. UserConfigDir() This function returns the default directory for user-specific configuration data and an error that indicates problems obtaining the path. TempDir() This function returns the default directory for temporary files and an error that indicates problems obtaining the path. `, "351": `351.The path/filepath Functions for Paths Name Description ------------ ------------------------------------------ Abs(path) This function returns an absolute path, which is useful if you have a relative path, such as a filename. IsAbs(path) This function returns true if the specified path is absolute. Base(path) This function returns the last element from the path. Clean(path) This function tidies up path strings by removing duplicate separators and relative references. Dir(path) This function returns all but the last element of the path. EvalSymlinks(path) This function evaluates a symbolic link and returns the resulting path. Ext(path) This function returns the file extension from the specified path, which is assumed to be the suffix following the final period in the path string. FromSlash(path) This function replaces each forward slash with the platform's file separator character. ToSlash(path) This function replaces the platform's file separator with forward slashes. Join(...elements) This function combines multiple elements using the platform's file separator. Match(pattern, path) This function returns true if the path is matched by the specified pattern. Split(path) This function returns the components on either side of the final path separator in the specified path. SplitList(path) This function splits a path into its components, which are returned as a string slice. VolumeName(path) This function returns the volume component of the specified path or the empty string if the path does not contain a volume. `, "352": `352.Working with a Path example: main.go package main import ( // "fmt" // "time" "os" //"encoding/json" "path/filepath" ) func main() { path, err := os.UserHomeDir() if (err == nil) { path = filepath.Join(path, "MyApp", "MyTempFile.json") } Printfln("Full path: %v", path) Printfln("Volume name: %v", filepath.VolumeName(path)) Printfln("Dir component: %v", filepath.Dir(path)) Printfln("File component: %v", filepath.Base(path)) Printfln("File extension: %v", filepath.Ext(path)) } Output: Username: Alice Full path: /home/sina/MyApp/MyTempFile.json Volume name: Dir component: /home/sina/MyApp File component: MyTempFile.json File extension: .json received on the Windows machine: Username: Alice Full path: C:\Users\adam\MyApp\MyTempFile.json Volume name: C: Dir component: C:\Users\adam\MyApp File component: MyTempFile.json File extension: .json `, "353": `353.The os Package Functions for Managing Files and Directories Name Description ----------------- ------------------------------------------ Chdir(dir) This function changes the current working directory to the specified directory. The result is an error that indicates problems making the change. Mkdir(name, modePerms) This function creates a directory with the specified name and mode/ permissions. The result is an error that is nil if the directory is created or that describes a problem if one arises. MkdirAll(name, modePerms) This function performs the same task as Mkdir but creates any parent directories in the specified path. MkdirTemp(parentDir, name) This function is similar to CreateTemp but creates a directory rather than a file. A random string is added to the end of the specified name or in place of an asterisk, and the new directory is created within the specified parent. The results are the name of the directory and an error indicating problems. Remove(name) This function removes the specified file or directory. The result is an error that describes any problems that arise. RemoveAll(name) This function removes the specified file or directory. If the name specifies a directory, then any children it contains are also removed. The result is an error that describes any problems that arise. Rename(old, new) This function renames the specified file or folder. The result is an error that describes any problems that arise. Symlink(old, new) This function creates a symbolic link to the specified file. The result is an error that describes any problems that arise. `, "354": `354.Creating Directories example: main.go: package main import ( // "fmt" // "time" "encoding/json" "os" "path/filepath" ) func main() { path, err := os.UserHomeDir() if err == nil { path = filepath.Join(path, "0-Repo/TEST-2/MyApp", "MyTempFile.json") } Printfln("Full path: %v", path) err = os.MkdirAll(filepath.Dir(path), 0766) if err == nil { file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666) if err == nil { defer file.Close() encoder := json.NewEncoder(file) encoder.Encode(Products) } } if err != nil { Printfln("Error %v", err.Error()) } } Output: print this: Username: Alice Full path: /home/sina/0-Repo/TEST-2/MyApp/MyTempFile.json Create this: MyTempFile.json in that directory with content: [{"Name":"Kayak","Category":"Watersports","Price":279},{"Name":"Lifejacket","Category":"Watersports","Price":49.95},{"Name":"Soccer Ball","Category":"Soccer","Price":19.5},{"Name":"Corner Flags","Category":"Soccer","Price":34.95},{"Name":"Stadium","Category":"Soccer","Price":79500},{"Name":"Thinking Cap","Category":"Chess","Price":16},{"Name":"Unsteady Chair","Category":"Chess","Price":75},{"Name":"Bling-Bling King","Category":"Chess","Price":1200}] `, "355": `355.ReadDir(name) The os Package Function for Listing Directories This function reads the specified directory and returns a DirEntry slice, each of which describes an item in the directory. The result of the ReadDir function is a slice of values that implement the DirEntry interface, which defines the methods. `, "356": `356.The Methods Defined by the DirEntry Interface Name Description -------- -------------------------- Name() This method returns the name of the file or directory described by the DirEntry value. IsDir() This method returns true if the DirEntry value represents a directory. Type() This method returns a FileMode value, which is an alias to uint32, which describes the file more and the permissions of the file or directory represented by the DirEntry value. Info() This method returns a FileInfo value that provides additional details about the file or directory represented by the DirEntry value. `, "357": `357.Useful Methods Defined by the FileInfo Interface Name Description ---------- ------------------------------------ Name() This method returns a string containing the name of the file or directory. Size() This method returns the size of the file, expressed as an int64 value. Mode() This method returns the file mode and permission settings for the file or directory. ModTime() This method returns the last modified time of the file or directory. `, "358": `358.Stat(path) The os Package Function for Inspecting a File This function accepts a path string. It returns a FileInfo value that describes the file and an error, which indicates problems inspecting the file. `, "359": `359.Enumerating Files example: main.go: package main import ( "os" ) func main() { path, err := os.Getwd() if err == nil { dirEntries, err := os.ReadDir(path) if err == nil { for _, dentry := range dirEntries { Printfln("Entry name: %v, IsDir: %v", dentry.Name(), dentry.IsDir()) } } } if err != nil { Printfln("Error %v", err.Error()) } } Output: Username: Alice Entry name: .git, IsDir: true Entry name: .vscode, IsDir: true Entry name: README.md, IsDir: false Entry name: U.sh, IsDir: false Entry name: cheap.json, IsDir: false Entry name: config.json, IsDir: false Entry name: go.mod, IsDir: false Entry name: main.go, IsDir: false Entry name: output.txt, IsDir: false Entry name: printer.go, IsDir: false Entry name: product.go, IsDir: false Entry name: readconfig.go, IsDir: false `, "360": `360.Determining Whether a File Exists Checking Whether a File Exists: main.go: package main import ( "os" ) func main() { targetFiles := []string { "no_such_file.txt", "config.json" } for _, name := range targetFiles { info, err := os.Stat(name) if os.IsNotExist(err) { Printfln("File does not exist: %v", name) } else if err != nil { Printfln("Other error: %v", err.Error()) } else { Printfln("File %v, Size: %v", info.Name(), info.Size()) } } } ============================= Output: Username: Alice File does not exist: no_such_file.txt File config.json, Size: 253 `, "361": `361.The path/filepath Function for Locating Files with a Pattern Name Description -------------------- ------------------------------- Match(pattern, name) This function matches a single path against a pattern. The results are a bool, which indicates if there is a match, and an error, which indicates problems with the pattern or with performing the match. Glob(pathPatten) This function finds all the files that match the specified pattern. The results are a string slice containing the matched paths and an error that indicates problems with performing the search. `, "362": `362.The Search Pattern Syntax for the path/filepath Functions Term Description -------- ------------------- * This term matches any sequence of characters, excluding the path separator. ? This term matches any single character, excluding the path separator. [a-Z] This term matches any character in the specified range. `, "363": `363.Locating Files example: main.go: package main import ( "os" "path/filepath" ) func main() { path, err := os.Getwd() if err == nil { matches, err := filepath.Glob(filepath.Join(path, "*.json")) if err == nil { for _, m := range matches { Printfln("Match: %v", m) } } } if err != nil { Printfln("Error %v", err.Error()) } } ============================= Output: Username: Alice Match: /home/sina/0-Repo/TEST-2/cheap.json Match: /home/sina/0-Repo/TEST-2/config.json `, "364": `364.The Function Provided by the path/filepath Package Name Description ----------------------- ---------------------------------- WalkDir(directory, func) This function calls the specified function for each file and directory in the specified directory. `, "365": `365.Walking a Directory example: main.go: package main import ( "os" "path/filepath" ) func callback(path string, dir os.DirEntry, dirErr error) (err error) { info, _ := dir.Info() Printfln("Path %v, Size: %v", path, info.Size()) return } func main() { path, err := os.Getwd() if err == nil { err = filepath.WalkDir(path, callback) } else { Printfln("Error %v", err.Error()) } } ============================= Output: Username: Alice Path /home/sina/0-Repo/TEST-2, Size: 4096 Path /home/sina/0-Repo/TEST-2/.git, Size: 4096 Path /home/sina/0-Repo/TEST-2/.git/COMMIT_EDITMSG, Size: 4 Path /home/sina/0-Repo/TEST-2/.git/FETCH_HEAD, Size: 92 Path /home/sina/0-Repo/TEST-2/.git/refs/remotes/origin/main, Size: 41 Path /home/sina/0-Repo/TEST-2/.git/refs/tags, Size: 4096 Path /home/sina/0-Repo/TEST-2/.vscode, Size: 4096 Path /home/sina/0-Repo/TEST-2/.vscode/extensions.json, Size: 79 Path /home/sina/0-Repo/TEST-2/.vscode/settings.json, Size: 405 Path /home/sina/0-Repo/TEST-2/README.md, Size: 9 Path /home/sina/0-Repo/TEST-2/U.sh, Size: 68 Path /home/sina/0-Repo/TEST-2/cheap.json, Size: 487 Path /home/sina/0-Repo/TEST-2/config.json, Size: 253 Path /home/sina/0-Repo/TEST-2/go.mod, Size: 22 Path /home/sina/0-Repo/TEST-2/main.go, Size: 8579 Path /home/sina/0-Repo/TEST-2/output.txt, Size: 109 Path /home/sina/0-Repo/TEST-2/printer.go, Size: 129 Path /home/sina/0-Repo/TEST-2/product.go, Size: 474 Path /home/sina/0-Repo/TEST-2/readconfig.go, Size: 704 `, "366": `366.Putting HTML and Text Templates in Context Question Answer: What are they? These templates allow HTML and text content to be generated dynamically from Go data values. Why are they useful? Templates are useful when large amounts of content are required, such that defining the content as strings would be unmanageable. How are they used? The templates are HTML or text files, which are annotated with instructions for the template processing engine. When a template is rendered, the instructions are processed to generate HTML or text content. Are there any pitfalls or limitations? The template syntax is counterintuitive and is not checked by the Go compiler. This means that care must be taken to use the correct syntax, which can be a frustrating process. Are there any alternatives? Templates are optional, and smaller amounts of content can be produced using strings. Summary: Problem Solution ------------------ ----------------------------------- Generate an HTML document Define an HTML template with actions that incorporate data values into the output. Load and execute the templates, providing data for the actions. Enumerate loaded templates Enumerate the results of the Templates method. Locate a specific template Use the Lookup method. Produce dynamic content Use a template action. Format a data value Use the formatting functions. Suppress whitespace Add hyphens to the template. Process a slice Use the slice functions. Conditionally execute template content Use the conditional actions and functions. Create a nested template Use the define and template actions. Define a default template Use the block and template actions. Create functions for use in a template Define template functions. Disable encoding for function results Return one of the type aliases defined by the html/template package. Store data values for later use in a template Define template variables. Generate a text document Use the text/template package. Preparing for This Chapter: 1- go mod init htmltext 2- printer.go: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } 3- product.go: package main type Product struct { Name, Category string Price float64 } var Kayak = Product { Name: "Kayak", Category: "Watersports", Price: 279, } var Products = []Product { { "Kayak", "Watersports", 279 }, { "Lifejacket", "Watersports", 49.95 }, { "Soccer Ball", "Soccer", 19.50 }, { "Corner Flags", "Soccer", 34.95 }, { "Stadium", "Soccer", 79500 }, { "Thinking Cap", "Chess", 16 }, { "Unsteady Chair", "Chess", 75 }, { "Bling-Bling King", "Chess", 1200 }, } func (p *Product) AddTax() float64 { return p.Price * 1.2 } func (p * Product) ApplyDiscount(amount float64) float64 { return p.Price - amount } 4- main.go: package main func main() { for _, p := range Products { Printfln("Product: %v, Category: %v, Price: $%.2f", p.Name, p.Category, p.Price) } } `, "367": `367.Creating HTML Templates The html/template package provides support for creating templates that are processed using a data structure to generate dynamic HTML output. Templates contain static content mixed with expressions that are enclosed in double curly braces, known as actions. The template uses the simplest action, which is a period (the . character) and which prints out the data used to execute the template, which I explain in the next section. example: template.html: <h1>Template Value: {{ . }}</h1> A project can contain multiple templates files. extras.html: <h1>Extras Template Value: {{ . }}</h1> The new template uses the same action as the previous example but has different static content to make it clear which template has been executed in the next section. Once I have described the basic techniques for using templates, I'll introduce more complex template actions. `, "368": `368.Loading and Executing Templates Using templates is a two-step process. First, the templates files are loaded and processed to create Template values. The html/template Functions for Loading Template Files: Name Description --------------------- -------------------------------------------- ParseFiles(...files) This function loads one or more files, which are specified by name. The result is a Template that can be used to generate content and an error that reports problems loading the templates. ParseGlob(pattern) This function loads one or more files, which are selected with a pattern. The result is a Template that can be used to generate content and an error that reports problems loading the templates. If you name your template files consistently, then you can use the ParseGlob function to load them with a simple pattern. If you want specific files—or the files are not named consistently—then you can specify individual files using the ParseFiles function. `, "369": `369.The Template Methods for Selecting and Executing Templates Name Description ---------------------------- ------------------------------------------- Templates() This function returns a slice containing pointers to the Template values that have been loaded. Lookup(name) This function returns a *Template for the specified loaded template. Name() This method returns the name of the Template. Execute(writer, data) This function executes the Template, using the specified data and writes the output to the specified Writer. ExecuteTemplate(writer, templateName, data) This function executes the template with the specified name and data and writes the output to the specified Writer. `, "370": `370.Loading and Executing a Template example: main.go: package main import ( "fmt" "html/template" "os" ) func main() { t, err := template.ParseFiles("templates/template.html") if (err == nil) { t.Execute(os.Stdout, &Kayak) fmt.Println() } else { Printfln("Error: %v", err.Error()) } } ============================= Output: <h1>Template Value: {Kayak Watersports 279}</h1> `, "371": `371.Loading Multiple Templates There are two approaches to working with multiple templates. The first is to create a separate Template value for each of them and execute them separately. example: Using Separate Templates: main.go: package main import ( "fmt" "html/template" "os" ) func main() { t1, err1 := template.ParseFiles("templates/template.html") t2, err2 := template.ParseFiles("templates/extras.html") if (err1 == nil && err2 == nil) { t1.Execute(os.Stdout, &Kayak) os.Stdout.WriteString("\n") t2.Execute(os.Stdout, &Kayak) os.Stdout.WriteString("\n") } else { Printfln("Error: %v %v", err1.Error(), err2.Error()) } } ============================= Output: <h1>Template Value: {Kayak Watersports 279}</h1> <h1>Extras Template Value: {Kayak Watersports 279}</h1> `, "372": `372.Using a Combined Template When multiple files are loaded with the ParseFiles, the result is a Template value on which the ExecuteTemplate method can be called to execute a specified template. The filename is used as the template name, which means that the templates in this example are named template.html and extras.html. You can call the Execute method on the Template returned by the ParseFiles or ParseGlob function, and the first template that was loaded will be selected and used to produce the output. Take care when using the ParseGlob function because the first template loaded—and therefore the template that will be executed—may not be the file you expect. example: main.go: package main import ( "html/template" "os" ) func main() { allTemplates, err1 := template.ParseFiles("templates/template.html", "templates/extras.html") if (err1 == nil) { allTemplates.ExecuteTemplate(os.Stdout, "template.html", &Kayak) os.Stdout.WriteString("\n") allTemplates.ExecuteTemplate(os.Stdout, "extras.html", &Kayak) } else { Printfln("Error: %v %v", err1.Error()) } } ============================= Output: <h1>Template Value: {Kayak Watersports 279}</h1> <h1>Extras Template Value: {Kayak Watersports 279}</h1> `, "373": `373.Enumerating Loaded Templates It can be useful to enumerate the templates that have been loaded, especially when using the ParseGlob function, to make sure that all the expected files have been discovered. example: main.go: package main import ( "html/template" ) func main() { allTemplates, err := template.ParseGlob("templates/*.html") if (err == nil) { for _, t := range allTemplates.Templates() { Printfln("Template name: %v", t.Name()) } } else { Printfln("Error: %v %v", err.Error()) } } ============================= Output: Template name: extras.html Template name: template.html `, "374": `374.Looking Up a Specific Template An alternative to specifying a name is to use the Lookup method to select a template, which is useful when you want to pass a template as an argument to a function example: main.go: package main import ( "html/template" "os" ) func Exec(t *template.Template) error { return t.Execute(os.Stdout, &Kayak) } func main() { allTemplates, err := template.ParseGlob("templates/*.html") if err == nil { selectedTemplated := allTemplates.Lookup("template.html") err = Exec(selectedTemplated) } if err != nil { Printfln("Error: %v %v", err.Error()) } } ============================= Output: <h1>Template Value: {Kayak Watersports 279}</h1> `, "375": `375.The Template Actions Action Description ------------ ----------------------------------------- {{ value }} This action inserts a data value or the result of an expression into the {{ expr }} template. A period is used to refer to the data value passed to the Execute or ExecuteTemplate function. See the “Inserting Data Values” section for details. {{ value.fieldname }} This action inserts the value of a struct field. See the “Inserting Data Values” section for details. {{ value.method arg }} This action invokes a method and inserts the result into the template output. Parentheses are not used, and arguments are separated by spaces. See the “Inserting Data Values” section for details. {{ func arg }} This action invokes a function and inserts the result into the output. There are built-in functions for common tasks, such as formatting data values, and custom functions can be defined, as described in the “Defining Template Functions” section. {{ expr | value.method }} Expressions can be chained together using a vertical bar so that the result {{ expr | func of the first expression is used as the last argument in the second expression. {{ range value }} This action iterates through the specified slice and adds the content ... between the range and end keyword for each element. The actions within {{ end }} the nested content are executed, with the current element accessible through the period. See the “Using Slices in Templates” section for details. {{ range value }} This action is similar to the range/end combination but defines a section ... of nested content that is used if the slice contains no elements. {{ else }} ... {{ end }} {{ if expr }} This action evaluates an expression and executes the nested template ... content if the result is true, as demonstrated in the “Conditionally {{ end }} Executing Template Content” section. This action can be used with optional else and else if clauses. {{ with expr }} This action evaluates an expression and executes the nested template ... content if the result isn't nil or the empty string. This action can be used {{ end }} with optional clauses. {{ define "name" }} This action defines a template with the specified name ... {{ end }} {{ template "name" expr }} This action executes the template with the specified name and data and inserts the result in the output. {{ block "name" expr }} This action defines a template with the specified name and invokes it ... with the specified data. This is typically used to define a template that {{ end }} can be replaced by one loaded from another file, as demonstrated in the “Defining Template Blocks” section. `, "376": `376.The Template Expressions for Inserting Values into Templates Inserting Data Values Expression Description ------------ ----------------------------------------------- . This expression inserts the value passed to the Execute or ExecuteTemplate method into the template output. .Field This expression inserts the value of the specified field into the template output. .Method This expression calls the specified method without arguments and inserts the result into the template output. .Method This expression calls the specified method with the specified argument and inserts the result arg into the template output. call This expression invokes a struct function field, using the specified arguments, which are .Field arg separated by spaces. The result from the function is inserted into the template output. `, "377": `377.Inserting Data Values in the template.html Unlike Go code, methods are not invoked with parentheses, and arguments are simply specified after the name, separated by spaces. It is the responsibility of the developer to ensure that arguments are of a type that can be used by the method or function. example: templates/template.html: <h1>Template Value: {{ . }}</h1> <h1>Name: {{ .Name }}</h1> <h1>Category: {{ .Category }}</h1> <h1>Price: {{ .Price }}</h1> <h1>Tax: {{ .AddTax }}</h1> <h1>Discount Price: {{ .ApplyDiscount 10 }}</h1> ============================= Output: <h1>Template Value: {Kayak Watersports 279}</h1> <h1>Name: Kayak</h1> <h1>Category: Watersports</h1> <h1>Price: 279</h1> <h1>Tax: 334.8</h1> <h1>Discount Price: 269</h1>`, "378": `378.Understanding Contextual Escaping Values are automatically escaped to make them safe for inclusion in HTML, CSS, and JavaScript code, with the appropriate escaping rules applied based on context. For example, a string value such as "It was a <big> boat" used as the text content of an HTML element would be inserted into the template as "It was a <big> boat" but as "It was a \u003cbig\u003e boat" when used as a string literal value in JavaScript code. Full details of how values are escaped can be found at https://golang.org/pkg/html/template.`, "379": `379.The Built-in Templates Functions for Formatting Data Name Description ------- -------------------------------- print This is an alias to the fmt.Sprint function. printf This is an alias to the fmt.Sprintf function. println This is an alias to the fmt.Sprintln function. html This function encodes a value for safe inclusion in an HTML document. js This function encodes a value for safe inclusion in a JavaScript document. urlquery This function encodes a value for use in a URL query string. example: template.html: <h1>Template Value: {{ . }}</h1> <h1>Name: {{ .Name }}</h1> <h1>Category: {{ .Category }}</h1> <h1>Price: {{ printf "$%.3f" .Price }}</h1> <h1>Tax: {{ printf "$%.2f" .AddTax }}</h1> <h1>Discount Price: {{ .ApplyDiscount 10 }}</h1> ============================= Output: <h1>Template Value: {Kayak Watersports 279}</h1> <h1>Name: Kayak</h1> <h1>Category: Watersports</h1> <h1>Price: $279.000</h1> <h1>Tax: $334.80</h1> <h1>Discount Price: 269</h1> `, "380": `380.Chaining Expressions Chaining expressions creates a pipeline for values, which allows the output from one method or function to be used as the input for another. example: template.html: <h1>Template Value: {{ . }}</h1> <h1>Name: {{ .Name }}</h1> <h1>Category: {{ .Category }}</h1> <h1>Price: {{ printf "$%.2f" .Price }}</h1> <h1>Tax: {{ printf "$%.2f" .AddTax }}</h1> <h1>Discount Price: {{ .ApplyDiscount 10 | printf "$%.2f" }}</h1> ============================= Output: <h1>Template Value: {Kayak Watersports 279}</h1> <h1>Name: Kayak</h1> <h1>Category: Watersports</h1> <h1>Price: $279.00</h1> <h1>Tax: $334.80</h1> <h1>Discount Price: $269.00</h1> `, "381": `381.Using Parentheses in html Chaining can be used only for the last argument provided to a function. An alternative approach—and one that can be used to set other function arguments is to use parentheses example: template.html: <h1>Template Value: {{ . }}</h1> <h1>Name: {{ .Name }}</h1> <h1>Category: {{ .Category }}</h1> <h1>Price: {{ printf "$%.2f" .Price }}</h1> <h1>Tax: {{ printf "$%.2f" .AddTax }}</h1> <h1>Discount Price: {{ printf "$%.2f" (.ApplyDiscount 10) }}</h1> ============================= Output: <h1>Template Value: {Kayak Watersports 279}</h1> <h1>Name: Kayak</h1> <h1>Category: Watersports</h1> <h1>Price: $279.00</h1> <h1>Tax: $334.80</h1> <h1>Discount Price: $269.00</h1> `, "382": `382.Trimming Whitespace HTML isn't sensitive to the whitespace between elements, but whitespace can still cause problems for text content and attribute values, especially when you want to structure the content of a template to make it easy to read. example: template.html <h1> Name: {{ .Name }}, Category: {{ .Category }}, Price, {{ printf "$%.2f" .Price }} </h1> ============================= Output: <h1> Name: Kayak, Category: Watersports, Price, $279.00 </h1> `, "383": `383.The minus sign must The effect is to remove all of the whitespace to before or after the action. The minus sign can be used to trim whitespace, applied immediately after or before the braces that open or close an action. example: template.html: <h1> Name: {{ .Name }}, Category: {{ .Category }}, Price, {{ printf "$%.2f" .Price }} </h1> <h1> Name: {{ .Name }}, Category: {{ .Category }}, Price, {{- printf "$%.2f" .Price -}} </h1> ============================= Output: <h1> Name: Kayak, Category: Watersports, Price, $279.00 </h1> <h1> Name: Kayak, Category: Watersports, Price,$279.00</h1> `, "384": `384.Trimming Additional Whitespace The whitespace around the final action has been removed, but there is still a newline character after the opening h1 tag because the whitespace trimming applies only to actions. example: template.html: <h1> {{- "" -}} Name: {{ .Name }}, Category: {{ .Category }}, Price, {{- printf "$%.2f" .Price -}} </h1> ============================= Output: <h1>Name: Kayak, Category: Watersports, Price,$279.00</h1> `, "385": `385.Slices in Templates Template actions can be used to generate content for slices example: Processing a Slice in the template.html {{ range . -}} <h1>Name: {{ .Name }}, Category: {{ .Category }}, Price, {{- printf "$%.2f" .Price }}</h1> {{ end }} main.go: package main import ( "html/template" "os" ) func Exec(t *template.Template) error { return t.Execute(os.Stdout, Products) } func main() { allTemplates, err := template.ParseGlob("templates/*.html") if err == nil { selectedTemplated := allTemplates.Lookup("template.html") err = Exec(selectedTemplated) } if err != nil { Printfln("Error: %v %v", err.Error()) } } ============================= Output: <h1>Name: Kayak, Category: Watersports, Price,$279.00</h1> <h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1> <h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1> <h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1> <h1>Name: Stadium, Category: Soccer, Price,$79500.00</h1> <h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1> <h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1> <h1>Name: Bling-Bling King, Category: Chess, Price,$1200.00</h1> `, "386": `386.The Built-in Template Functions for Slices Go text templates support the built-in functions Name Description ------ -------------- slice This function creates a new slice. Its arguments are the original slice, the start index, and the end index. index This function returns the element at the specified index. len This function returns the length of the specified slice. example: Built-in Functions in the template.html: <h1>There are {{ len . }} products in the source data.</h1> <h1>First product: {{ index . 0 }}</h1> {{ range slice . 3 6 -}} <h1>Name: {{ .Name }}, Category: {{ .Category }}, Price, {{- printf "$%.2f" .Price }}</h1> {{ end }} ============================= Output: <h1>There are 8 products in the source data.</h1> <h1>First product: {Kayak Watersports 279}</h1> <h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1> <h1>Name: Stadium, Category: Soccer, Price,$79500.00</h1> <h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1> `, "387": `387.Conditionally Executing Template Content Actions can be used to conditionally insert content into the output based on the evaluation of their expressions. The Template Conditional Functions: Function Description ------------ ----------------------------------------------- eq arg1 arg2 This function returns true if arg1 == arg2. ne arg1 arg2 This function returns true if arg1 != arg2. lt arg1 arg2 This function returns true if arg1 < arg2. le arg1 arg2 This function returns true if arg1 <= arg2. gt arg1 arg2 This function returns true if arg1 > arg2. ge arg1 arg2 This function returns true if arg1 >= arg2. and arg1 arg2 This function returns true if both arg1 and arg2 are true. not arg1 This function returns true if arg1 is false, and false if it is true. example: a Conditional Action in the template.html: <h1>There are {{ len . }} products in the source data.</h1> <h1>First product: {{ index . 0 }}</h1> {{ range . -}} {{ if lt .Price 100.00 -}} <h1>Name: {{ .Name }}, Category: {{ .Category }}, Price, {{- printf "$%.2f" .Price }}</h1> {{ end -}} {{ end }} ============================= Output: <h1>There are 8 products in the source data.</h1> <h1>First product: {Kayak Watersports 279}</h1> <h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1> <h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1> <h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1> <h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1> <h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1> Despite the use of the minus sign to trim whitespace, the output is oddly formatted because of the way I chose to structure the template. `, "388": `388.Using the Optional Conditional Actions The if action can be used with optional else and else if keywords example: template.html: <h1>There are {{ len . }} products in the source data.</h1> <h1>First product: {{ index . 0 }}</h1> {{ range . -}} {{ if lt .Price 100.00 -}} <h1>Name: {{ .Name }}, Category: {{ .Category }}, Price, {{- printf "$%.2f" .Price }}</h1> {{ else if gt .Price 1500.00 -}} <h1>Expensive Product {{ .Name }} ({{ printf "$%.2f" .Price}})</h1> {{ else -}} <h1>Midrange Product: {{ .Name }} ({{ printf "$%.2f" .Price}})</h1> {{ end -}} {{ end }} main.go: package main import ( "html/template" "os" ) func Exec(t *template.Template) error { return t.Execute(os.Stdout, Products) } func main() { allTemplates, err := template.ParseGlob("templates/*.html") if err == nil { selectedTemplated := allTemplates.Lookup("template.html") err = Exec(selectedTemplated) } if err != nil { Printfln("Error: %v %v", err.Error()) } } ============================= Output: <h1>There are 8 products in the source data.</h1> <h1>First product: {Kayak Watersports 279}</h1> <h1>Midrange Product: Kayak ($279.00)</h1> <h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1> <h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1> <h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1> <h1>Expensive Product Stadium ($79500.00)</h1> <h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1> <h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1> <h1>Midrange Product: Bling-Bling King ($1200.00)</h1> `, "389": `389.Creating Named Nested Templates The define action is used to create a nested template that can be executed by name, which allows content to be defined once and used repeatedly with the template action example: template.html: {{ define "currency" }}{{ printf "$%.2f" . }}{{ end }} {{ define "basicProduct" -}} Name: {{ .Name }}, Category: {{ .Category }}, Price, {{- template "currency" .Price }} {{- end }} {{ define "expensiveProduct" -}} Expensive Product {{ .Name }} ({{ template "currency" .Price }}) {{- end }} <h1>There are {{ len . }} products in the source data.</h1> <h1>First product: {{ index . 0 }}</h1> {{ range . -}} {{ if lt .Price 100.00 -}} <h1>{{ template "basicProduct" . }}</h1> {{ else if gt .Price 1500.00 -}} <h1>{{ template "expensiveProduct" . }}</h1> {{ else -}} <h1>Midrange Product: {{ .Name }} ({{ printf "$%.2f" .Price}})</h1> {{ end -}} {{ end }} ============================= Output: <h1>There are 8 products in the source data.</h1> <h1>First product: {Kayak Watersports 279}</h1> <h1>Midrange Product: Kayak ($279.00)</h1> <h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1> <h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1> <h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1> <h1>Expensive Product Stadium ($79500.00)</h1> <h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1> <h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1> <h1>Midrange Product: Bling-Bling King ($1200.00)</h1> The define keyword is followed by the template name in quotes, and the template is terminated by the end keyword. The template keyword is used to execute a named template, specifying the template name and a data value: ... {{- template "currency" .Price }} ... `, "390": `390.Selecting a Named Template in the main.go Using the define and end keywords for the main template content excludes the whitespace used to separate the other named templates. Adding a Named Template in the template.html: {{ define "currency" }}{{ printf "$%.2f" . }}{{ end }} {{ define "basicProduct" -}} Name: {{ .Name }}, Category: {{ .Category }}, Price, {{- template "currency" .Price }} {{- end }} {{ define "expensiveProduct" -}} Expensive Product {{ .Name }} ({{ template "currency" .Price }}) {{- end }} {{ define "mainTemplate" -}} <h1>There are {{ len . }} products in the source data.</h1> <h1>First product: {{ index . 0 }}</h1> {{ range . -}} {{ if lt .Price 100.00 -}} <h1>{{ template "basicProduct" . }}</h1> {{ else if gt .Price 1500.00 -}} <h1>{{ template "expensiveProduct" . }}</h1> {{ else -}} <h1>Midrange Product: {{ .Name }} ({{ printf "$%.2f" .Price}})</h1> {{ end -}} {{ end }} {{- end}} ============================= main.go: package main import ( "html/template" "os" ) func Exec(t *template.Template) error { return t.Execute(os.Stdout, Products) } func main() { allTemplates, err := template.ParseGlob("templates/*.html") if (err == nil) { selectedTemplated := allTemplates.Lookup("mainTemplate") err = Exec(selectedTemplated) } if (err != nil) { Printfln("Error: %v %v", err.Error()) } } ============================= Any of the named templates can be executed directly, but I have selected the mainTemplate Output: <h1>There are 8 products in the source data.</h1> <h1>First product: {Kayak Watersports 279}</h1> <h1>Midrange Product: Kayak ($279.00)</h1> <h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1> <h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1> <h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1> <h1>Expensive Product Stadium ($79500.00)</h1> <h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1> <h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1> <h1>Midrange Product: Bling-Bling King ($1200.00)</h1> `, "391": `391.Defining Template Blocks Template blocks are used to define a template with default content that can be overridden in another template file, which requires multiple templates to be loaded and executed together. This is often used to common content, such as a layout. The templates must be loaded so that the file that contains the block action is loaded before the file that contains the define action that redefines the template. When the templates are loaded, the template defined in the list.html file redefines the template named body so that the content in the list.html file replaces the content in the template.html file. example: template.html File in the templates Folder {{ define "mainTemplate" -}} <h1>This is the layout header</h1> {{ block "body" . }} <h2>There are {{ len . }} products in the source data.</h2> {{ end }} <h1>This is the layout footer</h1> {{ end }} ============================================== Output: <h1>This is the layout header</h1> <h2>There are 8 products in the source data.</h2> <h1>This is the layout footer</h1> When used alone, the output from the template file includes the content in the block. But this content can be redefined by another template file. example: list.html: {{ define "body" }} {{ range . }} <h2>Product: {{ .Name }} ({{ printf "$%.2f" .Price}})</h2> {{ end -}} {{ end }} ======================== main.go: package main import ( "html/template" "os" ) func Exec(t *template.Template) error { return t.Execute(os.Stdout, Products) } func main() { allTemplates, err := template.ParseFiles("templates/template.html","templates/list.html") if err == nil { selectedTemplated := allTemplates.Lookup("mainTemplate") err = Exec(selectedTemplated) } if err != nil { Printfln("Error: %v %v", err.Error()) } } ============================= Output: <h1>This is the layout header</h1> <h2>Product: Kayak ($279.00)</h2> <h2>Product: Lifejacket ($49.95)</h2> <h2>Product: Soccer Ball ($19.50)</h2> <h2>Product: Corner Flags ($34.95)</h2> <h2>Product: Stadium ($79500.00)</h2> <h2>Product: Thinking Cap ($16.00)</h2> <h2>Product: Unsteady Chair ($75.00)</h2> <h2>Product: Bling-Bling King ($1200.00)</h2> <h1>This is the layout footer</h1> `, "392": `392.Defining Template Functions example: main.go File in the htmltext Folder: package main import ( "html/template" "os" ) func GetCategories(products []Product) (categories []string) { catMap := map[string]string {} for _, p := range products { if (catMap[p.Category] == "") { catMap[p.Category] = p.Category categories = append(categories, p.Category) } } return } func Exec(t *template.Template) error { return t.Execute(os.Stdout, Products) } func main() { allTemplates := template.New("allTemplates") allTemplates.Funcs(map[string]interface{} { "getCats": GetCategories, }) allTemplates, err := allTemplates.ParseGlob("templates/*.html") if (err == nil) { selectedTemplated := allTemplates.Lookup("mainTemplate") err = Exec(selectedTemplated) } if (err != nil) { Printfln("Error: %v %v", err.Error()) } } ============================= Output: <h1>This is the layout header</h1> <h2>There are 8 products in the source data.</h2> <h1>This is the layout footer</h1> `, "393": `393.Using a Custom Function in the template.html example: template.html: {{ define "mainTemplate" -}} <h1>There are {{ len . }} products in the source data.</h1> {{ range getCats . -}} <h1>Category: {{ . }}</h1> {{ end }} {{- end }} ============================================= Output: <h1>There are 8 products in the source data.</h1> <h1>Category: Watersports</h1> <h1>Category: Soccer</h1> <h1>Category: Chess</h1> `, "394": `394.Creating an HTML Fragment in the main.go example: main.go: ... func GetCategories(products []Product) (categories []string) { catMap := map[string]string {} for _, p := range products { if (catMap[p.Category] == "") { catMap[p.Category] = p.Category categories = append(categories, "<b>p.Category</b>") } } return } ... =============================================================== Output: <h1>There are 8 products in the source data.</h1> <h1>Category: <b>p.Category</b></h1> <h1>Category: <b>p.Category</b></h1> <h1>Category: <b>p.Category</b></h1> `, "395": `395.The Types Aliases Used to Denote Content Types Name Description -------- ----------- CSS This type denotes CSS content. HTML This type denotes a fragment of HTML. HTMLAttr This type denotes a value that will be used as the value for an HTML attribute. JS This type denotes a fragment of JavaScript code. JSStr This type denotes a value that is intended to appear between quotes in a JavaScript expression. Srcset This type denotes a value that can be used in the srcset attribute of an img element. URL This type denotes a URL. `, "396": `396.Returning HTML Content in the main.go example: main.go: ... func GetCategories(products []Product) (categories []template.HTML) { catMap := map[string]string {} for _, p := range products { if (catMap[p.Category] == "") { catMap[p.Category] = p.Category categories = append(categories, "<b>p.Category</b>") } } return } ... ======================================================= Output: <h1>There are 8 products in the source data.</h1> <h1>Category: <b>p.Category</b></h1> <h1>Category: <b>p.Category</b></h1> <h1>Category: <b>p.Category</b></h1> `, "397": `397.Providing Access to Standard Library Functions Adding a Function Mapping in the main.go example: main.go: package main import ( "html/template" "os" "strings" ) func GetCategories(products []Product) (categories []string) { catMap := map[string]string{} for _, p := range products { if catMap[p.Category] == "" { catMap[p.Category] = p.Category categories = append(categories, p.Category) } } return } func Exec(t *template.Template) error { return t.Execute(os.Stdout, Products) } func main() { allTemplates := template.New("allTemplates") allTemplates.Funcs(map[string]interface{}{ "getCats": GetCategories, "lower": strings.ToLower, }) allTemplates, err := allTemplates.ParseGlob("templates/*.html") if err == nil { selectedTemplated := allTemplates.Lookup("mainTemplate") err = Exec(selectedTemplated) } if err != nil { Printfln("Error: %v %v", err.Error()) } } ============================================= Output: <h1>There are 8 products in the source data.</h1> <h1>Category: Watersports</h1> <h1>Category: Soccer</h1> <h1>Category: Chess</h1> `, "398": `398.Using a Template Function in the template.html example: template.html: {{ define "mainTemplate" -}} <h1>There are {{ len . }} products in the source data.</h1> {{ range getCats . -}} <h1>Category: {{ lower . }}</h1> {{ end }} {{- end }} ============================================================= Output: <h1>There are 8 products in the source data.</h1> <h1>Category: watersports</h1> <h1>Category: soccer</h1> <h1>Category: chess</h1> `, "399": `399.Defining Template Variables Defining and Using a Template Variable in the template.html example: template.html: {{ define "mainTemplate" -}} {{ $length := len . }} <h1>There are {{ $length }} products in the source data.</h1> {{ range getCats . -}} <h1>Category: {{ lower . }}</h1> {{ end }} {{- end }} ============================================================= Output: <h1>There are 8 products in the source data.</h1> <h1>Category: watersports</h1> <h1>Category: soccer</h1> <h1>Category: chess</h1> `, "400": `400.Defining and Using a Template Variable in the template.html example: template.html: {{ define "mainTemplate" -}} <h1>There are {{ len . }} products in the source data.</h1> {{- range getCats . -}} {{ if ne ($char := slice (lower .) 0 1) "s" }} <h1>{{$char}}: {{.}}</h1> {{- end }} {{- end }} {{- end }} ============================================== Output: <h1>There are 8 products in the source data.</h1> <h1>w: Watersports</h1> <h1>c: Chess</h1> `, "401": `401.Using Template Variables in Range Actions Enumerating a Map in the template.html example: main.go: ... func Exec(t *template.Template) error { productMap := map[string]Product {} for _, p := range Products { productMap[p.Name] = p } return t.Execute(os.Stdout, &productMap) } ... template.html: {{ define "mainTemplate" -}} {{ range $key, $value := . -}} <h1>{{ $key }}: {{ printf "$%.2f" $value.Price }}</h1> {{ end }} {{- end }} Output: <h1>Bling-Bling King: $1200.00</h1> <h1>Corner Flags: $34.95</h1> <h1>Kayak: $279.00</h1> <h1>Lifejacket: $49.95</h1> <h1>Soccer Ball: $19.50</h1> <h1>Stadium: $79500.00</h1> <h1>Thinking Cap: $16.00</h1> <h1>Unsteady Chair: $75.00</h1> `, "402": `402.Creating Text Templates Loading and Executing a Text Template in the main.go The Contents of the template.txt example: templates/template.txt: {{ define "mainTemplate" -}} {{ range $key, $value := . -}} {{ $key }}: {{ printf "$%.2f" $value.Price }} {{ end }} {{- end }} --------------------------------------- main.go: package main import ( "text/template" "os" "strings" ) func GetCategories(products []Product) (categories []string) { catMap := map[string]string {} for _, p := range products { if (catMap[p.Category] == "") { catMap[p.Category] = p.Category categories = append(categories, p.Category) } } return } func Exec(t *template.Template) error { productMap := map[string]Product {} for _, p := range Products { productMap[p.Name] = p } return t.Execute(os.Stdout, &productMap) } func main() { allTemplates := template.New("allTemplates") allTemplates.Funcs(map[string]interface{} { "getCats": GetCategories, "lower": strings.ToLower, }) allTemplates, err := allTemplates.ParseGlob("templates/*.txt") if (err == nil) { selectedTemplated := allTemplates.Lookup("mainTemplate") err = Exec(selectedTemplated) } if (err != nil) { Printfln("Error: %v %v", err.Error()) } } Output: Bling-Bling King: $1200.00 Corner Flags: $34.95 Kayak: $279.00 Lifejacket: $49.95 Soccer Ball: $19.50 Stadium: $79500.00 Thinking Cap: $16.00 Unsteady Chair: $75.00 `, "403": `403.Creating HTTP Servers What are they? The features described in this chapter make it easy for Go applications to create HTTP servers. Why are they useful? HTTP is one of the most widely used protocols and is useful for both user-facing applications and web services. How is it used? The features of the net/http package are used to create a server and handle requests. Are there any pitfalls or limitations? These features are well-designed and easy to use. Are there any alternatives? The standard library includes support for other network protocols and also for opening and using lower-level network connections. See https://pkg.go.dev/net@go1.17.1 for details of the net package and its subpackages, such as net/smtp, for example, which implements the SMTP protocol. Problem Solution -------- ------------- Create an HTTP or HTTPS server Use the ListenAndServe or ListenAndServeTLS functions Inspect an HTTP request Use the features of the Request struct Produce a response Use the ResponseWriter interface or the convenience functions Handle requests to specific URLs Use the integrated router Serve static content Use the FileServer and StripPrefix function Use a template to produce a response Write the content to the ResponseWriter or produce a JSON response Handle form data Use the Request methods Set or read cookies Use the Cookie, Cookies, and SetCookie methods Preparing for This Chapter: 1- go mod init httpserver 2- printer.go: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } 3- product.go: package main type Product struct { Name, Category string Price float64 var Products = []Product{ {"Kayak", "Watersports", 279}, {"Lifejacket", "Watersports", 49.95}, {"Soccer Ball", "Soccer", 19.50}, {"Corner Flags", "Soccer", 34.95}, {"Stadium", "Soccer", 79500}, {"Thinking Cap", "Chess", 16}, {"Unsteady Chair", "Chess", 75}, {"Bling-Bling King", "Chess", 1200}, } 4- main.go: package main func main() { for _, p := range Products { Printfln("Product: %v, Category: %v, Price: $%.2f", p.Name, p.Category, p.Price) } } ================================= Output: Product: Kayak, Category: Watersports, Price: $279.00 Product: Lifejacket, Category: Watersports, Price: $49.95 Product: Soccer Ball, Category: Soccer, Price: $19.50 Product: Corner Flags, Category: Soccer, Price: $34.95 Product: Stadium, Category: Soccer, Price: $79500.00 Product: Thinking Cap, Category: Chess, Price: $16.00 Product: Unsteady Chair, Category: Chess, Price: $75.00 Product: Bling-Bling King, Category: Chess, Price: $1200.00 `, "404": `404.Creating a Simple HTTP Server example: main.go: package main import ( "net/http" "io" ) type StringHandler struct { message string } func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { io.WriteString(writer, sh.message) } func main() { err := http.ListenAndServe(":5000", StringHandler{ message: "Hello, World"}) if (err != nil) { Printfln("Error: %v", err.Error()) } } ================================= Output: go run . in Web Browser, search localhost:5000/ Hello, World `, "405": `405.Creating the HTTP Listener and Handler The net/http Convenience Functions The ListenAndServe function starts listening for HTTP requests on a specified network address. The ListenAndServeTLS function does the same for HTTP requests. The addresses accepted by the functions can be used to restrict the HTTP server so that it only accepts requests on a specific interface or to listen for requests on any interface. Name Description --------- ---------------------------------------- ListenAndServe(addr, handler) This function starts listening for HTTP requests on a specified address and passes requests onto the specified handler. ListenAndServeTLS(addr, cert, key, handler) This function starts listening for HTTPS requests. The arguments are the address `, "406": `406.The Method Defined by the Handler Interface Name Description ----------------------- ------------------- ServeHTTP(writer, request) This method is invoked to process a HTTP request. The request is described by a Request value, and the response is written using a ResponseWriter, both of which are received as parameters. `, "407": `407.Inspecting the Request The Basic Fields Defined by the Request Struct Name Description ------- ------------------------ Method This field provides the HTTP method (GET, POST, etc.) as a string. The net/http package defines constants for the HTTP methods, such as MethodGet and MethodPost. URL This field returns the requested URL, expressed as a URL value. Proto This field returns a string that indicates the version of HTTP used for the request. Host This field returns a string containing the requested hos. Header This field returns a Header value, which is an alias to map[string][]string and contains the request headers. The map keys are the names of the headers, and the values are string slices containing the header values. Trailer This field returns a map[string]string that contains any additional headers that are included in the request after the body. Body This filed returns a ReadCloser, which is an interface that combines the Read method of the Reader interface with the Close method of the Closer interface `, "408": `408.Writing Request Fields in the main.go example: main.go: package main import ( "io" "net/http" ) type StringHandler struct { message string } func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { Printfln("Method: %v", request.Method) Printfln("URL: %v", request.URL) Printfln("HTTP Version: %v", request.Proto) Printfln("Host: %v", request.Host) for name, val := range request.Header { Printfln("Header: %v, Value: %v", name, val) } Printfln("---") io.WriteString(writer, sh.message) } func main() { err := http.ListenAndServe(":5000", StringHandler{message: "Hello, World"}) if err != nil { Printfln("Error: %v", err.Error()) } } =================================================================================== Output: Compile and execute the project and request http://localhost:5000 Method: GET URL: / HTTP Version: HTTP/1.1 Host: localhost:5000 Header: Accept, Value: [text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8] Header: Accept-Language, Value: [en-US,en;q=0.5] Header: Accept-Encoding, Value: [gzip, deflate, br] Header: Sec-Fetch-Mode, Value: [navigate] Header: Sec-Fetch-Site, Value: [none] Header: User-Agent, Value: [Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0] Header: Connection, Value: [keep-alive] Header: Upgrade-Insecure-Requests, Value: [1] Header: Sec-Fetch-Dest, Value: [document] Header: Sec-Fetch-User, Value: [?1] --- Method: GET URL: /favicon.ico HTTP Version: HTTP/1.1 Host: localhost:5000 Header: Accept-Encoding, Value: [gzip, deflate, br] Header: Connection, Value: [keep-alive] Header: Sec-Fetch-Dest, Value: [image] Header: Sec-Fetch-Mode, Value: [no-cors] Header: Sec-Fetch-Site, Value: [same-origin] Header: User-Agent, Value: [Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0] Header: Accept, Value: [image/avif,image/webp,*/*] Header: Accept-Language, Value: [en-US,en;q=0.5] Header: Referer, Value: [http://localhost:5000/] --- `, "409": `409.Save All Logs in a txt file: Server.go: package server import ( "io" "log" "net/http" "os" ) type StringHandler struct { Message string } func MyServer() { logFile, err := os.OpenFile("Server/LogFile/logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Fatal(err) } defer logFile.Close() log.SetOutput(logFile) err = http.ListenAndServe(":5000", StringHandler{Message: "Hello, World"}) if err != nil { log.Printf("Error: %v", err.Error()) } } func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { log.Printf("Method: %v", request.Method) log.Printf("URL: %v", request.URL) log.Printf("HTTP Version: %v", request.Proto) log.Printf("Host: %v", request.Host) for name, val := range request.Header { log.Printf("Header: %v, Value: %v", name, val) } io.WriteString(writer, sh.Message) log.Printf("============================================◉🧭🧭🧭🧭🧭🧭🧭◉==========================================") } Output: Create a TXT file in Server/LogFile/logs.txt and save appendation in to it. `, "410": `410.Filtering Requests and Generating Responses Useful Fields and Methods Defined by the URL Struct The ResponseWriter interface defines the methods that are available when creating a response. Name Description ------- ----------------------------- Scheme This field returns the scheme component of the URL. Host This field returns the host component of the URL, which may include the port. RawQuery This field returns the query string from the URL. Use the Query method to process the query string into a map. Path This field returns the path component of the URL. Fragment This field returns the fragment component of the URL, without the # character. Hostname() This method returns the hostname component of the URL as a string. Port()T his method returns the port component of the URL as a string. Query() This method returns a map[string][]string (a map with string keys and string slice values), containing the query string fields. User() This method returns the user information associated with the request. String() This method returns a string representation of the URL. `, "411": `411.The ResponseWriter Methods Name Description ------------ ------------------------ Header() This method returns a Header, which is an alias to map[string][]string, that can be used to set the response headers. WriteHeader(code) This method sets the status code for the response, specified as an int. The net/http package defines constants for most status codes. Write(data) This method writes data to the response body and implements the Writer interface. `, "412": `412.Producing Difference Responses in the main.go example: main.go: package main import ( "net/http" "io" ) type StringHandler struct { message string } func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { if (request.URL.Path == "/favicon.ico") { Printfln("Request for icon detected - returning 404") writer.WriteHeader(http.StatusNotFound) return } Printfln("Request for %v", request.URL.Path) io.WriteString(writer, sh.message) } func main() { err := http.ListenAndServe(":5000", StringHandler{ message: "Hello, World"}) if (err != nil) { Printfln("Error: %v", err.Error()) } } Output: in Terminal Request for / Request for icon detected - returning 404 `, "413": `413.Get Logs and Handle request if URL not Exist: example: main.go: package main import ( "io" "log" "net/http" "os" ) type StringHandler struct { Message string } func main() { logFile, err := os.OpenFile("LogFile/logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Fatal(err) } defer logFile.Close() log.SetOutput(logFile) err = http.ListenAndServe(":5000", StringHandler{Message: "Hello, World"}) if err != nil { log.Printf("Error: %v", err.Error()) } } func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { if (request.URL.Path != "/") { Printfln("Request for icon detected - returning 404") writer.WriteHeader(http.StatusNotFound) return } Printfln("Request for %v", request.URL.Path) io.WriteString(writer, sh.Message) log.Printf("Method: %v", request.Method) log.Printf("URL: %v", request.URL) log.Printf("HTTP Version: %v", request.Proto) log.Printf("Host: %v", request.Host) for name, val := range request.Header { log.Printf("Header: %v, Value: %v", name, val) } io.WriteString(writer, sh.Message) log.Printf("============================================◉🧭🧭🧭🧭🧭🧭🧭◉==========================================") } ======================================================================================= Output: so we have to logs, one of that is logs about user request, another that is about path URL in Terminal `, "414": `414.Using the Response Convenience Functions Name Description ----------------------- ---------------------------- Error(writer, message, code) This function sets the header to the specified code, sets the Content-Type header to text/plain, and writes the error message to the response. The X-Content- Type-Options header is also set to stop browsers from interpreting the response as anything other than text. NotFound(writer, request) This function calls Error and specifies a 404 error code. Redirect(writer, request, url, code) This function sends a redirection response to the specified URL and with the specified status code. ServeFile(writer, request, fileName) This function sends a response containing the contents of the specified file. The Content-Type header is set based on the file name but can be overridden by explicitly setting the header before calling the function. See the “Creating a Static HTTP Server” section for an example that serves files. `, "415": `415.Convenience Functions in the main.go example: main.go: package main import ( "io" "log" "net/http" "os" ) type StringHandler struct { Message string } func main() { logFile, err := os.OpenFile("LogFile/logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Fatal(err) } defer logFile.Close() log.SetOutput(logFile) err = http.ListenAndServe(":5000", StringHandler{Message: "Hello, World"}) if err != nil { log.Printf("Error: %v", err.Error()) } } func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { Printfln("Request for %v", request.URL.Path) switch request.URL.Path { case "/favicon.ico": http.NotFound(writer, request) case "/message": io.WriteString(writer, sh.Message) default: http.Redirect(writer, request, "/message", http.StatusTemporaryRedirect) } log.Printf("Method: %v", request.Method) log.Printf("URL: %v", request.URL) log.Printf("HTTP Version: %v", request.Proto) log.Printf("Host: %v", request.Host) for name, val := range request.Header { log.Printf("Header: %v, Value: %v", name, val) } io.WriteString(writer, sh.Message) log.Printf("============================================◉🧭🧭🧭🧭🧭🧭🧭◉==========================================") } ============================================================== Output: Will write in Terminal and File for feture analyzing. `, "416": `416.Using the Convenience Functions in the main.go example: main.go: package main import ( "io" "net/http" ) type StringHandler struct { message string } func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { Printfln("Request for %v", request.URL.Path) switch request.URL.Path { case "/favicon.ico": http.NotFound(writer, request) case "/message": io.WriteString(writer, sh.message) default: http.Redirect(writer, request, "/message", http.StatusTemporaryRedirect) } } func main() { err := http.ListenAndServe(":5000", StringHandler{message: "Hello, World"}) if err != nil { Printfln("Error: %v", err.Error()) } } ================================================ Output: every wrong links redirect to specific link. uses a switch statement to decide how to respond to a request. `, "417": `417.Using the Convenience Routing Handler The process of inspecting the URL and selecting a response can produce complex code that is difficult to read and maintain. To simplify the process, the net/http package provides a Handler implementation that allows matching the URL to be separated from producing a request. example: main.go: package main import ( "io" "net/http" ) type StringHandler struct { message string } func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { Printfln("Request for %v", request.URL.Path) io.WriteString(writer, sh.message) } func main() { http.Handle("/message", StringHandler{"Hello, World"}) http.Handle("/favicon.ico", http.NotFoundHandler()) http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect)) err := http.ListenAndServe(":5000", nil) if err != nil { Printfln("Error: %v", err.Error()) } } ====================================================== Output: don't show wrong search. `, "418": `418.The net/http Functions for Creating Routing Rules Name Description ----------------- --------------------------------- Handle(pattern, handler) This function creates a rule that invokes the specified ServeHTTP method of the specified Hander for requests that match the pattern. HandleFunc(pattern, handlerFunc) This function creates a rule that invokes the specified function for requests that match the pattern. The function is invoked with ResponseWriter and Request arguments. `, "419": `419.he net/http Functions for Creating Request Handlers Name Description --------------------------- -------------------------- FileServer(root) This function creates a Handler that produces responses using the ServeFile function. See the “Creating a Static HTTP Server” section for an example that serves files. NotFoundHandler() This function creates a Handler that produces responses using the NotFound function. RedirectHandler(url, code) This function creates a Handler that produces responses using the Redirect function. StripPrefix(prefix, handler) This function creates a Handler that removes the specified prefix from the request URL and passes on the request to the specified Handler. See the “Creating a Static HTTP Server” section for details. TimeoutHandler(handler, duration, message) This function passes on the request to the specified Handler but generates an error response if the response hasn't been produced within the specified duration. `, "420": `420.Supporting HTTPS Requests The net/http package provides integrated support for HTTPS. To prepare for HTTPS, you will need to add two files to the httpserver folder: a certificate file and a private key file. `, "421": `421.Getting Certificates for HTTPS A good way to get started with HTTPS is with a self-signed certificate, which can be used for development and testing. If you don't already have a self-signed certificate, then you can create one online using sites such as https://getacert.com or https://www.selfsignedcertificate.com both of which will let you create a self-signed certificate easily and without charge. Two files are required to use HTTPS, regardless of whether your certificate is self-signed or not. The first is the certificate file, which usually has a cer or cert file extension. The second is the private key file, which usually has a key file extension. after ready to deploy: https://letsencrypt.org The ListenAndServeTLS function is used to enable HTTPS, where the additional arguments specify the certificate and private key files, which are named certificate.cer and certificate.key in my project `, "422": `422.Enabling HTTPS in the main.go example: main.go: package main import ( "io" "net/http" ) type StringHandler struct { message string } func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { Printfln("Request for %v", request.URL.Path) io.WriteString(writer, sh.message) } func main() { http.Handle("/message", StringHandler{"Hello, World"}) http.Handle("/favicon.ico", http.NotFoundHandler()) http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect)) go func() { err := http.ListenAndServeTLS(":5500", "certificate.cer", "certificate.key", nil) if err != nil { Printfln("HTTPS Error: %v", err.Error()) } }() err := http.ListenAndServe(":5000", nil) if err != nil { Printfln("Error: %v", err.Error()) } } ========================================================= Output: HTTPS Error: open certificate.cer: no such file or directory Request for /message `, "423": `423.Redirecting HTTP Requests to HTTPS A common requirement when creating web servers is to redirect HTTP requests to the HTTPS port. This can be done by creating a custom handler example: Redirecting to HTTPS in the main.go: package main import ( "net/http" "io" "strings" ) type StringHandler struct { message string } func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { Printfln("Request for %v", request.URL.Path) io.WriteString(writer, sh.message) } func HTTPSRedirect(writer http.ResponseWriter, request *http.Request) { host := strings.Split(request.Host, ":")[0] target := "https://" + host + ":5500" + request.URL.Path if len(request.URL.RawQuery) > 0 { target += "?" + request.URL.RawQuery } http.Redirect(writer, request, target, http.StatusTemporaryRedirect) } func main() { http.Handle("/message", StringHandler{ "Hello, World"}) http.Handle("/favicon.ico", http.NotFoundHandler()) http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect)) go func () { err := http.ListenAndServeTLS(":5520", "certificate.cer", "certificate.key", nil) if (err != nil) { Printfln("HTTPS Error: %v", err.Error()) } }() err := http.ListenAndServe(":5000", http.HandlerFunc(HTTPSRedirect)) if (err != nil) { Printfln("Error: %v", err.Error()) } } ======================================================== Output: Not work for my Browser but you have to try, maybe work it for you. `, "424": `424.Creating a Static HTTP Server The net/http package includes built-in support for responding to requests with the contents of files. To prepare for the static HTTP server, create the httpserver/static folder and add to it a file named index.html example: static/index.html: <!DOCTYPE html> <html> <head> <title>Pro Go</title> <meta name="viewport" content="width=device-width" /> <link href="bootstrap.min.css" rel="stylesheet" /> </head> <body> <div class="m-1 p-2 bg-primary text-white h2"> Hello, World </div> </body> </html> store.html: <!DOCTYPE html> <html> <head> <title>Pro Go</title> <meta name="viewport" content="width=device-width" /> <link href="bootstrap.min.css" rel="stylesheet" /> </head> <body> <div class="m-1 p-2 bg-primary text-white h2 text-center"> Products </div> <table class="table table-sm table-bordered table-striped"> <thead> <tr> <th>Name</th> <th>Category</th> <th>Price</th> </tr> </thead> <tbody> <tr> <td>Kayak</td> <td>Watersports</td> <td>$279.00</td> </tr> <tr> <td>Lifejacket</td> <td>Watersports</td> <td>$49.95</td> </tr> </tbody> </table> </body> </html> ======================================================= The HTML files depend on the Bootstrap CSS package to style the HTML content. Run the command shown in here in the httpserver folder to download the Bootstrap CSS file into the static folder. (You may have to install the curl command.): curl https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css --output static/bootstrap.min.css =========================================== Output: ootstrap.min.css % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 152k 0 152k 0 0 163k 0 --:--:-- --:--:-- --:--:-- 163k `, "425": `425.Creating the Static File Route example: Defining a Route in the main.go: package main import ( "net/http" "io" ) type StringHandler struct { message string } func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { Printfln("Request for %v", request.URL.Path) io.WriteString(writer, sh.message) } func main() { http.Handle("/message", StringHandler{ "Hello, World"}) http.Handle("/favicon.ico", http.NotFoundHandler()) http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect)) fsHandler := http.FileServer(http.Dir("./static")) http.Handle("/files/", http.StripPrefix("/files", fsHandler)) err := http.ListenAndServe(":5000", nil) if (err != nil) { Printfln("Error: %v", err.Error()) } } ========================================================= Output: redirect from => https://localhost:5500/files/store.html to => static/store.html `, "426": `426.Using Templates to Generate Responses example: templates/products.html: <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Pro Go</title> <link rel="stylesheet" href="/files/bootstrap.min.css"> </head> <body> <h3 class="bg-primary text-white text-center p-2 m-2">Products</h3> <div class="p-2"> <table class="table table-sm table-striped table-bordered"> <thead> <tr> <th>Index</th> <th>Name</th> <th>Category</th> <th class="text-end">Price</th> </tr> </thead> <tbody> {{ range $index, $product := .Data }} <tr> <td>{{ $index }}</td> <td>{{ $product.Name }}</td> <td>{{ $product.Category }}</td> <td class="text-end"> {{ printf "$%.2f" $product.Price }} </td> </tr> {{ end }} </tbody> </table> </div> </body> </html> dynamic.go: package main import ( "html/template" "net/http" "strconv" ) type Context struct { Request *http.Request Data []Product } var htmlTemplates *template.Template func HandleTemplateRequest(writer http.ResponseWriter, request *http.Request) { path := request.URL.Path if (path == "") { path = "products.html" } t := htmlTemplates.Lookup(path) if (t == nil) { http.NotFound(writer, request) } else { err := t.Execute(writer, Context{ request, Products}) if (err != nil) { http.Error(writer, err.Error(), http.StatusInternalServerError) } } } func init() { var err error htmlTemplates = template.New("all") htmlTemplates.Funcs(map[string]interface{} { "intVal": strconv.Atoi, }) htmlTemplates, err = htmlTemplates.ParseGlob("templates/*.html") if (err == nil) { http.Handle("/templates/", http.StripPrefix("/templates/", http.HandlerFunc(HandleTemplateRequest))) } else { panic(err) } } ======================================================== Output: The initialization function loads all the templates with the html extension in the templates folder and sets up a route so that requests that start with /templates/ are processed by the HandleTemplateRequest function. This function looks up the template, falling back to the products.html file if no file path is specified, executes the template, and writes the response. `, "427": `427.Understanding Content Type Sniffing which implements the MIME Sniffing algorithm defined by: https://mimesniff.spec.whatwg.org The sniffing process can't detect every content type, but it does well with standard web types, such as HTML, CSS, and JavaScript. The DetectContentType function returns a MIME type, which is used as the value for the Content-Type header. `, "428": `428.Responding with JSON Data JSON responses are widely used in web services, which provide access to an application's data for clients that don't want to receive HTML, such as Angular or React JavaScript clients. example: json.go: package main import ( "net/http" "encoding/json" ) func HandleJsonRequest(writer http.ResponseWriter, request *http.Request) { writer.Header().Set("Content-Type", "application/json") json.NewEncoder(writer).Encode(Products) } func init() { http.HandleFunc("/json", HandleJsonRequest) } ========================================================================= main.go: package main import ( "net/http" "io" ) type StringHandler struct { message string } func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) { Printfln("Request for %v", request.URL.Path) io.WriteString(writer, sh.message) } func main() { http.Handle("/message", StringHandler{ "Hello, World"}) http.Handle("/favicon.ico", http.NotFoundHandler()) http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect)) fsHandler := http.FileServer(http.Dir("./static")) http.Handle("/files/", http.StripPrefix("/files", fsHandler)) err := http.ListenAndServe(":5000", nil) if (err != nil) { Printfln("Error: %v", err.Error()) } } =================================================================== Output: The initialization function creates a route, which means that requests for /json will be processed by the HandleJsonRequest function. `, "429": `429.Handling Form Data The net/http package provides support for easily receiving and processing form data. This template makes use of template variables, expressions, and functions to get the query string from the request and select the first index value, which is converted to an int and used to retrieve a Product value from the data provided to the template. example: edit.html: <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Pro Go</title> <link rel="stylesheet" href="/files/bootstrap.min.css"> </head> <body> {{ $index := intVal (index (index .Request.URL.Query "index") 0) }} {{ if lt $index (len .Data)}} {{ with index .Data $index}} <h3 class="bg-primary text-white text-center p-2 m-2">Product</h3> <form method="POST" action="/forms/edit" class="m-2"> <div class="form-group"> <label>Index</label> <input name="index" value="{{$index}}" class="form-control" disabled /> <input name="index" value="{{$index}}" type="hidden" /> </div> <div class="form-group"> <label>Name</label> <input name="name" value="{{.Name}}" class="form-control" /> </div> <div class="form-group"> <label>Category</label> <input name="category" value="{{.Category}}" class="form-control" /> </div> <div class="form-group"> <label>Price</label> <input name="price" value="{{.Price}}" class="form-control" /> </div> <div class="mt-2"> <button type="submit" class="btn btn-primary">Save</button> <a href="/templates/" class="btn btn-secondary">Cancel</a> </div> </form> {{ end }} {{ else }} <h3 class="bg-danger text-white text-center p-2"> No Product At Specified Index </h3> {{end }} </body> </html> `, "430": `430.Reading Form Data from Requests The Request Form Data Fields and Methods Name Description -------- ------------------------------ Form This field returns a map[string][]string containing the parsed form data and the query string parameters. The ParseForm method must be called before this field is read. PostForm This field is similar to Form but excludes the query string parameters so that only data from the request body is contained in the map. The ParseForm method must be called before this field is read. MultipartForm This field returns a multipart form represented using the Form struct defined in the mime/multipart package. The ParseMultipartForm method must be called before this field is read. FormValue(key) This method returns the first value for the specified form key and returns the empty string if there is no value. The source of data for this method is the Form field, and calling the FormValue method automatically calls ParseForm or ParseMultipartForm to parse the form. PostFormValue(key) This method returns the first value for the specified form key and returns the empty string if there is no value. The source of data for this method is the PostForm field, and calling the PostFormValue method automatically calls ParseForm or ParseMultipartForm to parse the form. FormFile(key) This method provides access to the first file with the specified key in the form. The results are a File and FileHeader, both of which are defined in the mime/ multipart package, and an error. Calling this function causes the ParseForm or ParseMultipartForm functions to be invoked to parse the form. ParseForm() This method parses a form and populates the Form and PostForm fields. The result is an error that describes any parsing problems. ParseMultipart This method parses a MIME multipart form and populates the MultipartForm field. Form(max) The argument specifies the maximum number of bytes to allocate to the form data, and the result is an error that describes any problems processing the form. The init function sets up a new route so that the ProcessFormData function handles requests whose path is /forms/edit. Within the ProcessFormData function, the request method is checked, and the form data in the request is used to create a Product struct and replace the existing data value. In a real project, validating the data submitted in the form is essential, but for this chapter I trust that the form contains valid data. Processing form data: https://localhost:5500/templates/edit.html?index=2 example: The Contents of the forms.go File in the httpserver Folder: package main import ( "net/http" "strconv" ) func ProcessFormData(writer http.ResponseWriter, request *http.Request) { if (request.Method == http.MethodPost) { index, _ := strconv.Atoi(request.PostFormValue("index")) p := Product {} p.Name = request.PostFormValue("name") p.Category = request.PostFormValue("category") p.Price, _ = strconv.ParseFloat(request.PostFormValue("price"), 64) Products[index] = p } http.Redirect(writer, request, "/templates", http.StatusTemporaryRedirect) } func init() { http.HandleFunc("/forms/edit", ProcessFormData) } ============================================================== Output: without view of upload you can add data to templates URL. `, "431": `431.Reading Multipart Forms example: upload.html: <!DOCTYPE html> <html> <head> <title>Pro Go</title> <meta name="viewport" content="width=device-width" /> <link href="bootstrap.min.css" rel="stylesheet" /> </head> <body> <div class="m-1 p-2 bg-primary text-white h2 text-center"> Upload File </div> <form method="POST" action="/forms/upload" class="p-2" enctype="multipart/form-data"> <div class="form-group"> <label class="form-label">Name</label> <input class="form-control" type="text" name="name"> </div> <div class="form-group"> <label class="form-label">City</label> <input class="form-control" type="text" name="city"> </div> <div class="form-group"> <label class="form-label">Choose Files</label> <input class="form-control" type="file" name="files" multiple> </div> <button type="submit" class="btn btn-primary mt-2">Upload</button> </form> </body> </html> ==================================================================================== upload.go: package main import ( "fmt" "io" "net/http" ) func HandleMultipartForm(writer http.ResponseWriter, request *http.Request) { fmt.Fprintf(writer, "Name: %v, City: %v\n", request.FormValue("name"), request.FormValue("city")) fmt.Fprintln(writer, "------") file, header, err := request.FormFile("files") if err == nil { defer file.Close() fmt.Fprintf(writer, "Name: %v, Size: %v\n", header.Filename, header.Size) for k, v := range header.Header { fmt.Fprintf(writer, "Key: %v, Value: %v\n", k, v) } fmt.Fprintln(writer, "------") io.Copy(writer, file) } else { http.Error(writer, err.Error(), http.StatusInternalServerError) } } func init() { http.HandleFunc("/forms/upload", HandleMultipartForm) } Output: Search in Browser: http://localhost:5000/files/upload.html You Can Upload `, "432": `432.The FileHeader Fields and Method Name Description ------- ------------------------------ Name This field returns a string containing the name of the file. Size This field returns an int64 containing the size of the file. Header This field returns a map[string][]string, which contains the headers for the MIME part that contains the file. Open() This method returns a File that can be used to read the content associated with the header, as demonstrated in the next section. `, "433": `433.Reading and Setting Cookies The net/http Function for Setting Cookies Name Description --------------- ------------------------------------ SetCookie(writer, cookie) This function adds a Set-Cookie header to the specified ResponseWriter. The cookie is described using a pointer to a Cookie struct, which is described next. Cookies can be complex, and care must be taken to configure them correctly. The detail of how cookies work is beyond the scope of this book, but there is a good description available at https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies and a detailed breakdown of the cookie fields at: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie. `, "434": `434.The Fields Defined by the Cookie Struct Name Description ------- -------------------------------- Name This field represents the name of the cookie, expressed as a string. Value This field represents the cookie value, expressed as a string. Path This optional field specifies the cookie path. Domain This optional field specifies the host/domain to which the cookie will be set. Expires This field specifies the cookie expiry, expressed as a time.Time value. MaxAge This field specifies the number of seconds until the cookie expires, expressed as an int. Secure When this bool field is true, the client will only send the cookie over HTTPS connections. HttpOnly When this bool field is true, the client will prevent JavaScript code from accessing the cookie. SameSite This field specifies the cross-origin policy for the cookie using the SameSite constants, which defines SameSiteDefaultMode, SameSiteLaxMode, SameSiteStrictMode, and SameSiteNoneMode. `, "435": `435.The Request Methods for Cookies Name Description ----------- ---------------- Cookie(name) This method returns a pointer to the Cookie value with the specified name and an error that indicates when there is no matching cookie. Cookies() This method returns a slice of Cookie pointers. example: cookies.go: package main import ( "net/http" "fmt" "strconv" ) func GetAndSetCookie(writer http.ResponseWriter, request *http.Request) { counterVal := 1 counterCookie, err := request.Cookie("counter") if (err == nil) { counterVal, _ = strconv.Atoi(counterCookie.Value) counterVal++ } http.SetCookie(writer, &http.Cookie{ Name: "counter", Value: strconv.Itoa(counterVal), }) if (len(request.Cookies()) > 0) { for _, c := range request.Cookies() { fmt.Fprintf(writer, "Cookie Name: %v, Value: %v", c.Name, c.Value) } } else { fmt.Fprintln(writer, "Request contains no cookies") } } func init() { http.HandleFunc("/cookies", GetAndSetCookie) } ====================================================================================== Compile and execute the project and use a browser to request http://localhost:5000/cookies Output: search-1: Request contains no cookies search-2: Cookie Name: counter, Value: 1 search-3: Cookie Name: counter, Value: 2 ... `, "436": `436.Creating HTTP Clients Putting HTTP Clients in Context What are they? HTTP requests are used to retrieve data from HTTP servers Why are they useful? HTTP is one of the most widely used protocols and is commonly used to provide access to content that can be presented to the user as well as data that is consumed programmatically. How is it used? The features of the net/http package are used to create and send requests and process responses. Are there any pitfalls or limitations? These features are well-designed and easy to use, although some features require a specific sequence to use. Are there any alternatives? The standard library includes support for other network protocols and also for opening and using lower-level network connections. See the https://pkg.go.dev/net@go1.17.1 https://pkg.go.dev/net for details of the net package and its subpackages, such as net/smtp, for example, which implements the SMTP protocol. Chapter Summary: Problem Solution -------- ------------ Send HTTP requests Use the convenience methods for specific HTTP methods Configure HTTP requests Use the fields and methods defined by the Client struct Create a preconfigured request Use the NewRequest convenience functions Use cookies in a request Use a cookie jar Configure how redirections are processed Use the CheckRedirect field to register a function that is invoked to deal with a redirection Send multipart forms Use the mime/multipart package Preparing for This Chapter 1- go mod init httpclient 2- printer.go package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } 3- httpclient folder -> file named product.go package main type Product struct { Name, Category string Price float64 } var Products = []Product { { "Kayak", "Watersports", 279 }, { "Lifejacket", "Watersports", 49.95 }, { "Soccer Ball", "Soccer", 19.50 }, { "Corner Flags", "Soccer", 34.95 }, { "Stadium", "Soccer", 79500 }, { "Thinking Cap", "Chess", 16 }, { "Unsteady Chair", "Chess", 75 }, { "Bling-Bling King", "Chess", 1200 }, } 4- The Contents of the index.html File in the httpclient Folder <!DOCTYPE html> <html> <head> <title>Pro Go</title> <meta name="viewport" content="width=device-width" /> </head> <body> <h1>Hello, World</div> </body> </html> 5- The Contents of the server.go File in the httpclient Folder package main import ( "encoding/json" "fmt" "io" "net/http" "os" ) func init() { http.HandleFunc("/html", func(writer http.ResponseWriter, request *http.Request) { http.ServeFile(writer, request, "./index.html") }) http.HandleFunc("/json", func(writer http.ResponseWriter, request *http.Request) { writer.Header().Set("Content-Type", "application/json") json.NewEncoder(writer).Encode(Products) }) http.HandleFunc("/echo", func(writer http.ResponseWriter, request *http.Request) { writer.Header().Set("Content-Type", "text/plain") fmt.Fprintf(writer, "Method: %v\n", request.Method) for header, vals := range request.Header { fmt.Fprintf(writer, "Header: %v: %v\n", header, vals) } fmt.Fprintln(writer, "----") data, err := io.ReadAll(request.Body) if err == nil { if len(data) == 0 { fmt.Fprintln(writer, "No body") } else { writer.Write(data) } } else { fmt.Fprintf(os.Stdout, "Error reading body: %v\n", err.Error()) } }) } 6- The Contents of the main.go File in the httpclient Folder package main import ( "net/http" ) func main() { Printfln("Starting HTTP Server") http.ListenAndServe(":5000", nil) } ======================================================================================= Output: The code in httpclient folder will be compiled and executed. Use a web browser to request http://localhost:5000/html and http://localhost:5000/json, To see the echo result, request http://localhost:5000/echo `, "437": `437.Sending Simple HTTP Requests The net/http package provides a set of convenience functions that make basic HTTP requests. The functions are named after the HTTP method of the request they created. The Convenience Methods for HTTP Requests Name Description ----------------- ----------------------------- Get(url) This function sends a GET request to the specified HTTP or HTTPS URL. The results are a Response and an error that reports problems with the request. Head(url) This function sends a HEAD request to the specified HTTP or HTTPS URL. A HEAD request returns the headers that would be returned for a GET request. The results are a Response and an error that reports problems with the request. Post(url, contentType, reader) This function sends a POST request to the specified HTTP or HTTPS URL, with the specified Content-Type header value. The content for the form is provided by the specified Reader. The results are a Response and an error that reports problems with the request. PostForm(url, data) This function sends a POST request to the specified HTTP or HTTPS URL, with the Content-Type header set to application/x-www-form-urlencoded. The content for the form is provided by a map[string][]string. The results are a Response and an error that reports problems with the request. `, "438": `438.Sending a GET Request in the main.go main.go: package main import ( "net/http" "os" "time" ) func main() { go http.ListenAndServe(":5000", nil) time.Sleep(time.Second) response, err := http.Get("http://localhost:5000/html") if (err == nil) { response.Write(os.Stdout) } else { Printfln("Error: %v", err.Error()) } } =================================================================================================== The argument to the Get function is a string that contains the URL to request. The results are a Response value and an error that reports any problems sending the request. Output: HTTP/1.1 200 OK Content-Length: 171 Accept-Ranges: bytes Content-Type: text/html; charset=utf-8 Date: Thu, 12 Oct 2023 16:17:10 GMT Last-Modified: Wed, 11 Oct 2023 12:44:50 GMT <!DOCTYPE html> <html> <head> <title>Pro Go</title> <meta name="viewport" content="width=device-width" /> </head> <body> <h1>Hello, World</div> </body> </html> ======================================================================================================== example-2: main.go: package main import ( "net/http" "os" // "time" ) func main() { // go http.ListenAndServe(":5000", nil) // time.Sleep(time.Second) response, err := http.Get("https://www.google.com") if (err == nil) { response.Write(os.Stdout) } else { Printfln("Error: %v", err.Error()) } } Output: HTTP/2.0 403 Forbidden Content-Length: 1579 Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 Content-Type: text/html; charset=UTF-8 Date: Thu, 12 Oct 2023 16:15:23 GMT Referrer-Policy: no-referrer <!DOCTYPE html> <html lang=en> <meta charset=utf-8> <meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width"> <title>Error 403 (Forbidden)!!1</title> <style> *{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px} </style> <a href=//www.google.com/><span id=logo aria-label=Google></span></a> <p><b>403.</b> <ins>That's an error.</ins> <p>Your client does not have permission to get URL <code>/</code> from this server. <ins>That's all we know.</ins> `, "439": `439.The Fields and Methods Defined by the Response Struct Name Description ---------- ------------------------------------- StatusCode This field returns the response status code, expressed as an int. Status This field returns a string containing the status description. Proto This field returns a string containing the response HTTP protocol. Header This field returns a map[string][]string that contains the response headers. Body This field returns a ReadCloser, which is a Reader that defines a Close method and which provides access to the response body. Trailer This field returns a map[string][]string that contains the response trailers. ContentLength This field returns the value of the Content-Length header, parsed into an int64 value. TransferEncoding This field returns the set of Transfer-Encoding header values. Close This bool field returns true if the response contains a Connection header set to close, which indicates that the HTTP connection should be closed. Uncompressed This field returns true if the server sent a compressed response that was decompressed by the net/http package. Request This field returns the Request that was used to obtain the response. The Request struct is described in Chapter 24. TLS This field provides details of the HTTPS connection. Cookies() This method returns a []*Cookie, which contains the Set-Cookie headers in the response. The Cookie struct is described in Chapter 24. Location() This method returns the URL from the response Location header and an error that indicates when the response does not contain this header. Write(writer) This method writes a summary of the response to the specified Writer. `, "440": `440.Reading the Response Body in the main.go main.go: package main import ( "net/http" "os" "time" "io" ) func main() { go http.ListenAndServe(":5000", nil) time.Sleep(time.Second) response, err := http.Get("http://localhost:5000/html") if (err == nil && response.StatusCode == http.StatusOK) { data, err := io.ReadAll(response.Body) if (err == nil) { defer response.Body.Close() os.Stdout.Write(data) } } else { Printfln("Error: %v, Status Code: %v", err.Error(), response.StatusCode) } } ==================================================================== Output: in Terminal <!DOCTYPE html> <html> <head> <title>Pro Go</title> <meta name="viewport" content="width=device-width" /> </head> <body> <h1>Hello, World</div> </body> </html> `, "441": `441.Reading and Parsing Data in the main.go main.go: package main import ( "net/http" //"os" "time" //"io" "encoding/json" ) func main() { go http.ListenAndServe(":5000", nil) time.Sleep(time.Second) response, err := http.Get("http://localhost:5000/json") if (err == nil && response.StatusCode == http.StatusOK) { defer response.Body.Close() data := []Product {} err = json.NewDecoder(response.Body).Decode(&data) if (err == nil) { for _, p := range data { Printfln("Name: %v, Price: $%.2f", p.Name, p.Price) } } else { Printfln("Decode error: %v", err.Error()) } } else { Printfln("Error: %v, Status Code: %v", err.Error(), response.StatusCode) } } ==================================================================== Output: in Terminal Name: Kayak,Price: $279.00 Name: Lifejacket,Price: $49.95 Name: Soccer Ball,Price: $19.50 Name: Corner Flags,Price: $34.95 Name: Stadium,Price: $79500.00 Name: Thinking Cap,Price: $16.00 Name: Unsteady Chair,Price: $75.00 Name: Bling-Bling King,Price: $1200.00 `, "442": `442.Sending POST Requests The Post and PostForm functions are used to send POST requests. The PostForm function encodes a map of values as form data. Sending a Form in the main.go main.go: package main import ( "net/http" "os" "time" "io" //"encoding/json" ) func main() { go http.ListenAndServe(":5000", nil) time.Sleep(time.Second) formData := map[string][]string { "name": { "Kayak "}, "category": { "Watersports"}, "price": { "279"}, } response, err := http.PostForm("http://localhost:5000/echo", formData) if (err == nil && response.StatusCode == http.StatusOK) { io.Copy(os.Stdout, response.Body) defer response.Body.Close() } else { Printfln("Error: %v, Status Code: %v", err.Error(), response.StatusCode) } } ==================================================================== Output: Method: POST Header: Accept-Encoding: [gzip] Header: User-Agent: [Go-http-client/1.1] Header: Content-Length: [42] Header: Content-Type: [application/x-www-form-urlencoded] ---- category=Watersports&name=Kayak+&price=279 `, "443": `443.Posting a Form Using a Reader The Post function sends a POST request to the server and creates the request body by reading content from a Reader. Posting from a Reader in the main.go: package main import ( "net/http" "os" "time" "io" "encoding/json" "strings" ) func main() { go http.ListenAndServe(":5000", nil) time.Sleep(time.Second) var builder strings.Builder err := json.NewEncoder(&builder).Encode(Products[0]) if (err == nil) { response, err := http.Post("http://localhost:5000/echo","application/json",strings.NewReader(builder.String())) if (err == nil && response.StatusCode == http.StatusOK) { io.Copy(os.Stdout, response.Body) defer response.Body.Close() } else { Printfln("Error: %v", err.Error()) } } else { Printfln("Error: %v", err.Error()) } } ==================================================================== Output: Method: POST Header: User-Agent: [Go-http-client/1.1] Header: Content-Length: [54] Header: Content-Type: [application/json] Header: Accept-Encoding: [gzip] ---- {"Name":"Kayak","Category":"Watersports","Price":279} `, "444": `444.Understanting The Content-Length Header This header is set automatically but is included in requests only when it is possible to determine how much data will be included in the body in advance. This is done by inspecting the Reader to determine the dynamic type. When the data is stored in memory using the strings. Reader, bytes.Reader, or bytes.Buffer type, the built-in len function is used to determine the amount of data, and the result is used to set the Content-Length header. For all other types, the Content-Type head is not set, and chunked encoding is used instead, which means that the body is written in blocks of data whose size is declared as part of the request body. This approach allows requests to be sent without needing to read all the data from the Reader just to work out how many bytes there are. Chunked encoding is described at https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding `, "445": `445.Configuring HTTP Client Requests The Client struct is used when control is required over an HTTP request and defines the fields and methods described: The Client Fields and Methods Name Description ----------------- ------------------------- Transport This field is used to select the transport that will be used to send the HTTP request. The net/http package provides a default transport. CheckRedirect This field is used to specify a custom policy for dealing with repeated redirections, as described in the “Managing Redirections” section. Jar This field returns a CookieJar, which is used to manage cookies, as described in the “Working with Cookies” section. Timeout This field is used to set a timeout for the request, specified as a time.Duration Do(request) This method sends the specified Request, returning a Response and an error that indicates problems sending the request. CloseIdleConnections() This method closes any idle HTTP requests that are currently open and unused. Get(url) This method is called by the Get function described Head(url) This method is called by the Head function described Post(url, contentType,reader) This method is called by the Post function described PostForm(url, data) This method is called by the PostForm function described `, "446": `446.Useful Request Fields and Methods Name Description ------ ------------------------------ Method This string field specifies the HTTP method that will be used for the request. The net/ http package defines constants for HTTP methods, such as MethodGet and MethodPost. URL This URL field specifies the URL to which the request will be sent. The URL struct is Header This field is used to specify the headers for the request. The headers are specified in a map[string][]string, and the field will be nil when a Request value is created using the literal struct syntax. ContentLength This field is used to set the Content-Length header using an int64 value. TransferEncoding This field is used to set the Transfer-Encoding header using a slice of strings. Body This ReadCloser field specifies the source for the request body. If you have a Reader that doesn't define a Close method, then the io.NopCloser function can be used to create a ReadCloser whose Close method does nothing. `, "447": `447.The Function for Parsing URL Values Name Description --------- --------------------- Parse(string) This method parses a string into a URL. The results are the URL value and an error that indicates problems parsing the string. `, "448": `448.Sending a Request in the main.go main.go: package main import ( "net/http" "os" "time" "io" "encoding/json" "strings" "net/url" ) func main() { go http.ListenAndServe(":5000", nil) time.Sleep(time.Second) var builder strings.Builder err := json.NewEncoder(&builder).Encode(Products[0]) if (err == nil) { reqURL, err := url.Parse("http://localhost:5000/echo") if (err == nil) { req := http.Request { Method: http.MethodPost, URL: reqURL, Header: map[string][]string { "Content-Type": { "application.json" }, }, Body: io.NopCloser(strings.NewReader(builder.String())), } response, err := http.DefaultClient.Do(&req) if (err == nil && response.StatusCode == http.StatusOK) { io.Copy(os.Stdout, response.Body) defer response.Body.Close() } else { Printfln("Request Error: %v", err.Error()) } } else { Printfln("Parse Error: %v", err.Error()) } } else { Printfln("Encoder Error: %v", err.Error()) } } ==================================================================== Output: Method: POST Header: User-Agent: [Go-http-client/1.1] Header: Content-Type: [application.json] Header: Accept-Encoding: [gzip] ---- {"Name":"Kayak","Category":"Watersports","Price":279} `, "449": `449.Using the Convenience Functions to Create a Request The net/http Convenience Functions for Creating Requests Name Description ------------------------- ---------------------------- NewRequest(method, url,reader) This function creates a new Reader, configured with the specified method, URL, and body. The function also returns an error that indicates problems creating the value, including parsing the URL, which is expressed as a string. NewRequestWithContext(context, method, url, reader) This function creates a new Reader that will be sent in the specified context. `, "450": `450.Using the Convenience Function in the main.go example: main.go: package main import ( "encoding/json" "io" "net/http" "os" "strings" "time" //"net/url" ) func main() { go http.ListenAndServe(":5000", nil) time.Sleep(time.Second) var builder strings.Builder err := json.NewEncoder(&builder).Encode(Products[0]) if err == nil { req, err := http.NewRequest(http.MethodPost, "http://localhost:5000/echo", io.NopCloser(strings.NewReader(builder.String()))) if err == nil { req.Header["Content-Type"] = []string{"application/json"} response, err := http.DefaultClient.Do(req) if err == nil && response.StatusCode == http.StatusOK { io.Copy(os.Stdout, response.Body) defer response.Body.Close() } else { Printfln("Request Error: %v", err.Error()) } } else { Printfln("Request Init Error: %v", err.Error()) } } else { Printfln("Encoder Error: %v", err.Error()) } } ==================================================================== Output: Method: POST Header: User-Agent: [Go-http-client/1.1] Header: Content-Type: [application/json] Header: Accept-Encoding: [gzip] ---- {"Name":"Kayak","Category":"Watersports","Price":279} `, "451": `451.Working with Cookies The Client keeps track of the cookies it receives from the server and automatically includes them in subsequent requests. example: The Contents of the server_cookie.go: package main import ( "fmt" "net/http" "strconv" ) func init() { http.HandleFunc("/cookie", func(writer http.ResponseWriter, request *http.Request) { counterVal := 1 counterCookie, err := request.Cookie("counter") if err == nil { counterVal, _ = strconv.Atoi(counterCookie.Value) counterVal++ } http.SetCookie(writer, &http.Cookie{ Name: "counter", Value: strconv.Itoa(counterVal), }) if len(request.Cookies()) > 0 { for _, c := range request.Cookies() { fmt.Fprintf(writer, "Cookie Name: %v, Value: %v\n", c.Name, c.Value) } } else { fmt.Fprintln(writer, "Request contains no cookies") } }) } ----------------------------------- Changing URL in the main.go: package main import ( "io" "net/http" "os" "time" // "encoding/json" // "strings" //"net/url" "net/http/cookiejar" ) func main() { go http.ListenAndServe(":5000", nil) time.Sleep(time.Second) jar, err := cookiejar.New(nil) if err == nil { http.DefaultClient.Jar = jar } for i := 0; i < 3; i++ { req, err := http.NewRequest(http.MethodGet, "http://localhost:5000/cookie", nil) if err == nil { response, err := http.DefaultClient.Do(req) if err == nil && response.StatusCode == http.StatusOK { io.Copy(os.Stdout, response.Body) defer response.Body.Close() } else { Printfln("Request Error: %v", err.Error()) } } else { Printfln("Request Init Error: %v", err.Error()) } } } ==================================================================== Output: in Terminal Request contains no cookies Cookie Name: counter, Value: 1 Cookie Name: counter, Value: 2 `, "452": `452.The Methods Defined by the CookieJar Interface Name Description ------------------------ --------------------------------- SetCookies(url, cookies) This method stores a *Cookie slice for the specified URL. Cookes(url) This method returns a *Cookie slice containing the cookies that should be included in a request for the specified URL. The net/http/cookiejar package contains an implementation of the CookieJar interface that stores cookies in memory. Cookie jars are created with a constructor function `, "453": `453.The Cookie Jar Constructor Function in the net/http/cookiejar Package Name Description ------------ ------------------------- New(options) This function creates a new CookieJar, configured with an Options struct, described next. The function also returns an error that reports problems creating the jar. The New function accepts a net/http/cookiejar/Options struct, which is used to configure the cookie jar. There is only one Options field, PublicSuffixList, which is used to specify an implementation of the interface with the same name, which provides support for preventing cookies from being set too widely, which can cause privacy violations. The standard library doesn't contain an implementation of the PublicSuffixList interface, but there is an implementation available at https://pkg.go.dev/golang.org/x/net/publicsuffix `, "454": `454.Creating Separate Clients and Cookie Jars A consequence of using the DefaultClient is that all requests share the same cookies, which can be useful, especially since the cookie jar will ensure that each request only includes the cookies that are required for each URL. `, "455": `455.Creating Separate Clients in the main.go File example: main.go: package main import ( "net/http" "os" "time" "io" //"encoding/json" //"strings" //"net/url" "net/http/cookiejar" "fmt" ) func main() { go http.ListenAndServe(":5000", nil) time.Sleep(time.Second) clients := make([]http.Client, 3) for index, client := range clients { jar, err := cookiejar.New(nil) if (err == nil) { client.Jar = jar } for i := 0; i < 3; i++ { req, err := http.NewRequest(http.MethodGet, "http://localhost:5000/cookie", nil) if (err == nil) { response, err := client.Do(req) if (err == nil && response.StatusCode == http.StatusOK) { fmt.Fprintf(os.Stdout, "Client %v: ", index) io.Copy(os.Stdout, response.Body) defer response.Body.Close() } else { Printfln("Request Error: %v", err.Error()) } } else { Printfln("Request Init Error: %v", err.Error()) } } } } ==================================================================== Output: Client 0: Request contains no cookies Client 0: Cookie Name: counter, Value: 1 Client 0: Cookie Name: counter, Value: 2 Client 1: Request contains no cookies Client 1: Cookie Name: counter, Value: 1 Client 1: Cookie Name: counter, Value: 2 Client 2: Request contains no cookies Client 2: Cookie Name: counter, Value: 1 Client 2: Cookie Name: counter, Value: 2 `, "456": `456.Sharing a CookieJar in the main.go example: main.go: package main import ( "io" "net/http" "os" "time" //"encoding/json" //"strings" //"net/url" "fmt" "net/http/cookiejar" ) func main() { go http.ListenAndServe(":5000", nil) time.Sleep(time.Second) jar, err := cookiejar.New(nil) clients := make([]http.Client, 3) for index, client := range clients { //jar, err := cookiejar.New(nil) if err == nil { client.Jar = jar } for i := 0; i < 3; i++ { req, err := http.NewRequest(http.MethodGet, "http://localhost:5000/cookie", nil) if err == nil { response, err := client.Do(req) if err == nil && response.StatusCode == http.StatusOK { fmt.Fprintf(os.Stdout, "Client %v: ", index) io.Copy(os.Stdout, response.Body) defer response.Body.Close() } else { Printfln("Request Error: %v", err.Error()) } } else { Printfln("Request Init Error: %v", err.Error()) } } } } ==================================================================== Output: Client 0: Request contains no cookies Client 0: Cookie Name: counter, Value: 1 Client 0: Cookie Name: counter, Value: 2 Client 1: Cookie Name: counter, Value: 3 Client 1: Cookie Name: counter, Value: 4 Client 1: Cookie Name: counter, Value: 5 Client 2: Cookie Name: counter, Value: 6 Client 2: Cookie Name: counter, Value: 7 Client 2: Cookie Name: counter, Value: 8 `, "457": `457.Managing Redirections By default, a Client will stop following redirections after ten requests, but this can be changed by specifying a custom policy. example: The Contents of the server_redirects.go File in the httpclient Folder: package main import "net/http" func init() { http.HandleFunc("/redirect1", func(writer http.ResponseWriter, request *http.Request) { http.Redirect(writer, request, "/redirect2", http.StatusTemporaryRedirect) }) http.HandleFunc("/redirect2", func(writer http.ResponseWriter, request *http.Request) { http.Redirect(writer, request, "/redirect1", http.StatusTemporaryRedirect) }) } ==================================================================== Sending a Request in the main.go File in the httpclient Folder: package main import ( "net/http" "os" "io" "time" //"encoding/json" //"strings" //"net/url" //"net/http/cookiejar" //"fmt" ) func main() { go http.ListenAndServe(":5000", nil) time.Sleep(time.Second) req, err := http.NewRequest(http.MethodGet, "http://localhost:5000/redirect1", nil) if (err == nil) { var response *http.Response response, err = http.DefaultClient.Do(req) if (err == nil) { io.Copy(os.Stdout, response.Body) } else { Printfln("Request Error: %v", err.Error()) } } else { Printfln("Error: %v", err.Error()) } } ==================================================================== Output: Request Error: Get "/redirect1": stopped after 10 redirects `, "458": `458.Defining a Custom Redirection Policy in the main.go example: main.go: package main import ( "net/http" "os" "io" "time" //"encoding/json" //"strings" "net/url" //"net/http/cookiejar" //"fmt" ) func main() { go http.ListenAndServe(":5000", nil) time.Sleep(time.Second) http.DefaultClient.CheckRedirect = func(req *http.Request, previous []*http.Request) error { if len(previous) == 3 { url, _ := url.Parse("http://localhost:5000/html") req.URL = url } return nil } req, err := http.NewRequest(http.MethodGet, "http://localhost:5000/redirect1", nil) if (err == nil) { var response *http.Response response, err = http.DefaultClient.Do(req) if (err == nil) { io.Copy(os.Stdout, response.Body) } else { Printfln("Request Error: %v", err.Error()) } } else { Printfln("Error: %v", err.Error()) } } ==================================================================== Output: <!DOCTYPE html> <html> <head> <title>Pro Go</title> <meta name="viewport" content="width=device-width" /> </head> <body> <h1>Hello, World</div> </body> </html> `, "459": `459.Creating Multipart Forms: The mime/multipart package can be used to create a request body encoded as multipart/form-data, which allows a form to safely contain binary data, such as the contents of a file. `, "460": `460.The Contents of the server_forms.go File in the httpclient Folder example: server_forms.go: package main import ( "net/http" "fmt" "io" ) func init() { http.HandleFunc("/form", func (writer http.ResponseWriter, request *http.Request) { err := request.ParseMultipartForm(10000000) if (err == nil) { for name, vals := range request.MultipartForm.Value { fmt.Fprintf(writer, "Field %v: %v\n", name, vals) } for name, files := range request.MultipartForm.File { for _, file := range files { fmt.Fprintf(writer, "File %v: %v\n", name, file.Filename) if f, err := file.Open(); err == nil { defer f.Close() io.Copy(writer, f) } } } } else { fmt.Fprintf(writer, "Cannot parse form %v", err.Error()) } }) } ==================================================================== Output: <!DOCTYPE html> <html> <head> <title>Pro Go</title> <meta name="viewport" content="width=device-width" /> </head> <body> <h1>Hello, World</div> </body> </html> `, "461": `461.The multipart.Writer Constructor Function Name Description --------------- ---------------------------- NewWriter(writer) This function creates a new multipart.Writer that writes form data to the specified io.Writer. `, "462": `462.The multipart.Writer Methods Name Description ------------------------ --------------------------------------- CreateFormField(fieldname) This method creates a new form field with the specified name. The results are an io.Writer that is used to write the field data and an error that reports problems creating the field. CreateFormFile(fieldname, filename) This method creates a new file field with the specified field name and file name. The results are an io.Writer that is used to write the field data and an error that reports problems creating the field. FormDataContentType() This method returns a string that is used to set the Content-Type request header and includes the string that denotes the boundaries between the parts of the form. Close() This function finalizes the form and writes the terminating boundary that denotes the end of the form data. `, "463": `463.Creating and Sending a Multipart Form in the main.go example: main.go: package main import ( "io" "net/http" "os" "time" //"encoding/json" //"strings" //"net/url" //"net/http/cookiejar" //"fmt" "bytes" "mime/multipart" ) func main() { go http.ListenAndServe(":5000", nil) time.Sleep(time.Second) var buffer bytes.Buffer formWriter := multipart.NewWriter(&buffer) fieldWriter, err := formWriter.CreateFormField("name") if err == nil { io.WriteString(fieldWriter, "Alice") } fieldWriter, err = formWriter.CreateFormField("city") if err == nil { io.WriteString(fieldWriter, "New York") } fileWriter, err := formWriter.CreateFormFile("codeFile", "printer.go") if err == nil { fileData, err := os.ReadFile("./printer.go") if err == nil { fileWriter.Write(fileData) } } formWriter.Close() req, err := http.NewRequest(http.MethodPost, "http://localhost:5000/form", &buffer) req.Header["Content-Type"] = []string{formWriter.FormDataContentType()} if err == nil { var response *http.Response response, err = http.DefaultClient.Do(req) if err == nil { io.Copy(os.Stdout, response.Body) } else { Printfln("Request Error: %v", err.Error()) } } else { Printfln("Error: %v", err.Error()) } } ==================================================================== Output: Field name: [Alice] Field city: [New York] File codeFile: printer.go package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template+"\n", values...) } Caution Don't use the defer keyword on the call to the Close method; otherwise, the final boundary string won't be added to the form until after the request will be sent, producing a form that not all servers will process. It is important to call the Close method before sending the request. `, "464": `464.Working with Databases There are drivers for a wide range of databases, and a list can be found at https://github.com/golang/go/wiki/sqldrivers Putting Working with Databases in Context What is it? The database/sql package provides features for working with SQL databases. Why is it useful? Relational databases remain the most effective way of storing large amounts of structured data and are used in most large projects. How is it used? Driver packages provide support for specific databases, while the database/sql package provides a set of types that allow databases to be used consistently. Are there any pitfalls or limitations? These features do not automatically populate struct fields from result rows. Are there any alternatives? There are third-party packages that build on these features to simplify or enhance their use. Preparing for This Chapter: 1-Initializing the Module go mod init data 2-Add a file named printer.go to the data folder package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } 3-Add a file named main.go package main func main() { Printfln("Hello, Data") } 4-Compiling and Executing the Project go run . ==================================================================== Output: Hello, Data Preparing the Database: add a file named products.sql to the data folder: DROP TABLE IF EXISTS Categories; DROP TABLE IF EXISTS Products; CREATE TABLE IF NOT EXISTS Categories ( Id INTEGER NOT NULL PRIMARY KEY, Name TEXT ); CREATE TABLE IF NOT EXISTS Products ( Id INTEGER NOT NULL PRIMARY KEY, Name TEXT, Category INTEGER, Price decimal(8, 2), CONSTRAINT CatRef FOREIGN KEY(Category) REFERENCES Categories (Id) ); INSERT INTO Categories (Id, Name) VALUES (1, "Watersports"), (2, "Soccer"); INSERT INTO Products (Id, Name, Category, Price) VALUES (1, "Kayak", 1, 279), (2, "Lifejacket", 1, 48.95), (3, "Soccer Ball", 2, 19.50), (4, "Corner Flags", 2, 34.95); =================================================== Go to https://www.sqlite.org/download.html , look for the precompiled binaries section for your operating system, and download the tools package. Unpack the zip archive and copy the sqlite3 or sqlite3.exe file into the data folder. Run the command Creating the Database command: ./sqlite3 products.db ".read products.sql" `, "465": `465.Creating the Database command: ./sqlite3 products.db ".read products.sql" `, "466": `466.Installing a Database Driver Run the command: go get modernc.org/sqlite Most database servers are set up separately so that the database driver opens a connection to a separate process. SQLite is an embedded database and is included in the driver package, which means no additional configuration is required. `, "467": `467.Opening a Database The standard library provides the database/sql package for working with databases. The functions described here: The database/sql Functions for Opening a Database Name Description ---------------- ------------------------------ Drivers() This function returns a slice of strings, each of which contains the name of a database driver. Open(driver,connectionStr) This function opens a database using the specified driver and connection string. The results are a pointer to a DB struct, which is used to interact with the database and an error that indicates problems opening the database. `, "468": `468.The Contents of the database.go package main // The blank identifier is used to import the database driver package, which loads the driver and allows it // to register as a provider of the SQL API: import ( "database/sql" _ "modernc.org/sqlite" ) func listDrivers() { for _, driver := range sql.Drivers() { Printfln("Driver: %v", driver) } } func openDatabase() (db *sql.DB, err error) { db, err = sql.Open("sqlite", "products.db") if err == nil { Printfln("Opened database") } return } ==================================================================== Using the DB Struct in the main.go: package main func main() { listDrivers() db, err := openDatabase() if (err == nil) { db.Close() } else { panic(err) } } ==================================================================== in Terminal: go mod tidy Output in Terminal: go: finding module for package modernc.org/sqlite go: downloading modernc.org/sqlite v1.27.0 go: found modernc.org/sqlite in modernc.org/sqlite v1.27.0 go: downloading golang.org/x/sys v0.9.0 go: downloading modernc.org/ccgo/v3 v3.16.13 go: downloading modernc.org/libc v1.29.0 go: downloading modernc.org/mathutil v1.6.0 go: downloading github.com/mattn/go-sqlite3 v1.14.16 go: downloading github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 go: downloading modernc.org/tcl v1.15.2 go: downloading github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec go: downloading github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 go: downloading golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78 go: downloading modernc.org/cc/v3 v3.40.0 go: downloading modernc.org/opt v0.1.3 go: downloading github.com/dustin/go-humanize v1.0.1 go: downloading github.com/google/uuid v1.3.0 go: downloading github.com/mattn/go-isatty v0.0.16 go: downloading modernc.org/memory v1.7.2 go: downloading golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 go: downloading golang.org/x/mod v0.3.0 go: downloading lukechampine.com/uint128 v1.2.0 go: downloading modernc.org/strutil v1.1.3 go: downloading modernc.org/token v1.0.1 go: downloading modernc.org/httpfs v1.0.6 go: downloading modernc.org/z v1.7.3 go: downloading modernc.org/ccorpus v1.11.6 ==================================================================== Output: The main method calls the listDrivers function to print out the names of the loaded drivers and then calls the openDatabase function to open the database. Nothing is done with the database yet, but Close method is called. `, "469": `469.The DB Method for Closing the Database: Name Description ------- ---------------------- Close() This function closes the database and prevents further operations from being performed. `, "470": `470.Executing Statements and Queries The DB Methods for Executing SQL Statements Name Description -------------------- ----------------------------- Query(query,...args) This method executes the specified query, using the optional placeholder arguments. The results are a Rows struct, which contains the query results, and an error that indicates problems executing the query. QueryRow(query, ..args) This method executes the specified query, using the optional placeholder arguments. The result is a Row struct, which represents the first row from the query results. See the “Executing Queries for Single Rows” section. Exec(query,...args) This method executes statements or queries that do not return rows of data. The method returns a Result, which describes the response from the database, and an error that signals problems with execution. See the “Executing Other Queries” section. `, "471": `471.Using Contexts with Databases the context package and the Context interface it defines, which is used to manage requests as they are processed by a server. All the important methods defined in the database/sql package also have versions that accept a Context argument, which is useful if you want to take advantage of features like request handling timeouts. `, "472": `472.Querying for Multiple Rows The Query method executes a query that retrieves one or more rows from the database. The Query method returns a Rows struct, which contains the query results and an error that indicates problems. The row data is accessed through the methods described in below The Rows Struct Methods Name Description ---------------- ----------------------------- Next() This method advances to the next result row. The result is a bool, which is true when there is data to read and false when the end of the data has been reached, at which point the Close method is automatically called. NextResultSet() This method advances to the next result set when there are multiple result sets in the same database response. The method returns true if there is another set of rows to process. Scan(...targets) This method assigns the SQL values from the current row to the specified variables. The values are assigned via pointers and the method returns an error that indicates when the values cannot be scanned. See the “Understanding the Scan Method” section for details. Close() This method prevents further enumeration of the results and is used when not all of the data is required. There is no need to call this method if the Next method is used to advance until it returns false. `, "473": `473.Querying the Database in the main.go example: package main import "database/sql" func queryDatabase(db *sql.DB) { rows, err := db.Query("SELECT * from Products") if err == nil { for rows.Next() { var id, category int var name string var price float64 rows.Scan(&id, &name, &category, &price) Printfln("Row: %v %v %v %v", id, name, category, price) } } else { Printfln("Error: %v", err) } } func main() { //listDrivers() db, err := openDatabase() if err == nil { queryDatabase(db) db.Close() } else { panic(err) } } ==================================================================== The queryDatabase function performs a simple SELECT query on the Products table with the Query method, which produces a Rows result and an error. If the error is nil, a for loop is used to move through the result rows by calling the Next method, which returns true if there is a row to process and returns false when the end of the data has been reached. `, "474": `474.Using Reflection Preparing for This Chapter 1-go mod init reflection 2-printer.go: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } 3-types.go: package main type Product struct { Name, Category string Price float64 } type Customer struct { Name, City string } 4-main.go: package main func printDetails(values ...Product) { for _, elem := range values { Printfln("Product: Name: %v, Category: %v, Price: %v", elem.Name, elem.Category, elem.Price) } } func main() { product := Product { Name: "Kayak", Category: "Watersports", Price: 279, } printDetails(product) } ==================================================================== Output: Product: Name: Kayak, Category: Watersports, Price: 279 `, "475": `475.Understanding the Need for Reflection The Go type system is rigorously enforced, which means you can't use a value of one type when a different type is inspected. example: Mixing Types in the main.go package main func printDetails(values ...Product) { for _, elem := range values { Printfln("Product: Name: %v, Category: %v, Price: %v", elem.Name, elem.Category, elem.Price) } } func main() { product := Product { Name: "Kayak", Category: "Watersports", Price: 279, } customer := Customer { Name: "Alice", City: "New York" } printDetails(product, customer) } ==================================================================== Output: # reflection ./main.go:13:24: cannot use customer (variable of type Customer) as Product value in argument to printDetails interfaces, which allow common characteristics to be defined through methods, which can be invoked regardless of the type that implements the interface. `, "476": `476.Using the Empty Interface in the main.go File in the reflection example: package main func printDetails(values ...interface{}) { for _, elem := range values { switch val := elem.(type) { case Product: Printfln("Product: Name: %v, Category: %v, Price: %v", val.Name, val.Category, val.Price) case Customer: Printfln("Customer: Name: %v, City: %v", val.Name, val.City) } } } func main() { product := Product { Name: "Kayak", Category: "Watersports", Price: 279, } customer := Customer { Name: "Alice", City: "New York" } printDetails(product, customer) } ==================================================================== Output: Product: Name: Kayak, Category: Watersports, Price: 279 Customer: Name: Alice, City: New York The limitation of this approach is that the printDetails function can only process types that are known in advance. Many projects will deal with a small enough set of types that this won't be an issue or will be able to define interfaces with methods that provides access to common functionality. Reflection solves this issue for those projects for which this isn't the case, either because there are a large number of types to deal with or because interfaces and methods can't be written. `, "477": `477.Using Reflection The reflect package provides the Go reflection features, and the key functions are called TypeOf and ValueOf `, "478": `478.The Key Reflection Functions: Name Description ----------- ------------------------------- TypeOf(val) This function returns a value that implements the Type interface, which describes the type of the specified value. ValueOf(val) This function returns a Value struct, which allows the specified value to be inspected and manipulated. `, "479": `479.Using Reflection in the main.go example: package main import ( "reflect" "strings" "fmt" ) func printDetails(values ...interface{}) { for _, elem := range values { fieldDetails := []string {} elemType := reflect.TypeOf(elem) elemValue := reflect.ValueOf(elem) if elemType.Kind() == reflect.Struct { for i := 0; i < elemType.NumField(); i++ { fieldName := elemType.Field(i).Name fieldVal := elemValue.Field(i) fieldDetails = append(fieldDetails, fmt.Sprintf("%v: %v", fieldName, fieldVal )) } Printfln("%v: %v", elemType.Name(), strings.Join(fieldDetails, ", ")) } else { Printfln("%v: %v", elemType.Name(), elemValue) } } } type Payment struct { Currency string Amount float64 } func main() { product := Product { Name: "Kayak", Category: "Watersports", Price: 279, } customer := Customer { Name: "Alice", City: "New York" } payment := Payment { Currency: "USD", Amount: 100.50 } printDetails(product, customer, payment, 10, true) } ==================================================================== Code that uses reflection can be verbose, but the basic pattern becomes easy to follow once you become familiar with the basics. The key point to remember is that there are two aspects of reflection that work together: the reflected type and the reflected value. The reflected type gives you access to details of a Go type without knowing in advance what it is. You can explore the reflected type, exploring its details and characteristics through the methods defined by the Type interface. The reflected value lets you work with the specific value with which you have been provided. You can't just read a struct field or call a method, for example, as you would in normal code when you don't know what type you are dealing with. The use of the reflected type and reflected value leads to the code verbosity. If you know you are dealing with a Product struct, for example, you can just read the Name field and get a string result. If you don't know what type is being used, then you must use the reflected type to establish whether you are dealing with a struct and whether it has a Name field. Once you have determined there is such as field, you use the reflected value to read that field and get its value. =============================================================== Output: Product: Name: Kayak, Category: Watersports, Price: 279 Customer: Name: Alice, City: New York Payment: Currency: USD, Amount: 100.5 int: 10 bool: true `, "480": `480.Using the Basic Type Features The Type interface provides basic details about a type through the methods described in below. There are specialized methods for working with specific kinds of types, such as arrays, which are described in later sections, but these are the methods that provide the essential details for all types. `, "481": `481.Basic Methods Defined by the Type Interface Name Description --------------- ------------------------------------ Name() This method returns the name of the type. PkgPath() This method returns the package path for the type. The empty string is returned for built-in types, such as int and bool. Kind() This method returns the kind of type, using a value that matches one of the constant values defined by the reflect package, as described in Table 27-5. String() This method returns a string representation of the type name, including the package name. Comparable() This method returns true if values of this type can be compared using the standard comparison operator, as described in the “Comparing Values” section. AssignableTo(type) This method returns true if values of this type can be assigned to variables or fields of the specified reflected type. `, "482": `482.The Kind Constants The reflect package defines a type named Kind, which is an alias for uint, and which is used for a series of constants that describe different kinds of type. Name Description ---------------------- ----------------------------------------- Bool This value denotes a bool. Int, Int8, Int16, Int32, Int64 These values denote the different sizes of integer types. Uint, Uint8, Uint16, Uint32, Uint64 These values denote the different sizes of unsigned integer types. Float32, Float64 These values denote the different sizes of floating-point types. String This value denotes a string. Struct This value denotes a struct. Array This value denotes an array. Slice This value denotes a slice. Map This value denotes a map. Chan This value denotes a channel. Func This value defines a function. Interface This value denotes an interface. Ptr This value denotes a pointer Uintptr This value denotes an unsafe pointer, which is not described in this book. `, "483": `483.Printing Type Details in the main.go File in the reflection Folder added a function that replaces empty package names so that built-in types are more obviously described. example: main.go: package main import ( "reflect" // "strings" // "fmt" ) func getTypePath(t reflect.Type) (path string) { path = t.PkgPath() if path == "" { path = "(built-in)" } return } func printDetails(values ...interface{}) { for _, elem := range values { elemType := reflect.TypeOf(elem) Printfln("Name: %v, PkgPath: %v, Kind: %v", elemType.Name(), getTypePath(elemType), elemType.Kind()) } } type Payment struct { Currency string Amount float64 } func main() { product := Product{ Name: "Kayak", Category: "Watersports", Price: 279, } customer := Customer{Name: "Alice", City: "New York"} payment := Payment{Currency: "USD", Amount: 100.50} printDetails(product, customer, payment, 10, true) } ============================== printer.go: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } ============================== types.go: package main type Product struct { Name, Category string Price float64 } type Customer struct { Name, City string } >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Output: Name: Product, PkgPath: main, Kind: struct Name: Customer, PkgPath: main, Kind: struct Name: Payment, PkgPath: main, Kind: struct Name: int, PkgPath: (built-in), Kind: int Name: bool, PkgPath: (built-in), Kind: bool `, "484": `484.Using the Basic Value Features For each group of reflected type features, there are corresponding features for reflected values. The Value struct defines the methods described in here, which provide access to basic reflection features, including accessing the underlying value. Basic Methods Defined by the Value Struct Name Description ------- ---------------------------------------------------- Kind() This method returns the kind of the value's type. Type() This method returns the Type for the Value. IsNil() This method returns true if the value is nil. This method will panic if the underlying value isn't a function, an interface, a pointer, a slice, or a channel. IsZero() This method returns true if the underlying value is the zero value for its type. Bool() This method returns the underlying bool value. The method panics if the underlying value's Kind is not Bool. Bytes() This method returns the underlying []byte value. The method panics if the underlying value is not a byte slice. I demonstrate how to determine the type of a slice in the “Identifying Byte Slices” section. Int() This method returns the underlying value as an int64. The method panics if the underlying value's Kind is not Int, Int8, Int16, Int32, or Int64. Uint() This method returns the underlying value as an uint64. The method panics if the underlying value's Kind is not Uint, Uint8, Uint16, Uint32, or Uint64. Float() This method returns the underlying value as an float64. The method panics if the underlying value's Kind is not Float32, or Float64. String() This method returns the underlying value as a string if the value's Kind is String. For other Kind values, this method returns the string <T Value> where T is the underlying type, such as <int Value>. Elem() This method returns the Value to which a pointer refers. This method can also be used with interfaces, as described in Chapter 29. This method panics if the underlying value's Kind is not Ptr. IsValid() This method returns false if the Value is the zero value, created as Value{} rather than obtained using ValueOf, for example. This method doesn't relate to reflected values that are the zero value of their reflected type. If this method returns false, then all other Value methods will panic. `, "485": `485.Using the Basic Value Methods in the main.go example: main.go: package main import ( "reflect" // "strings" // "fmt" ) func printDetails(values ...interface{}) { for _, elem := range values { elemValue := reflect.ValueOf(elem) switch elemValue.Kind() { case reflect.Bool: var val bool = elemValue.Bool() Printfln("Bool: %v", val) case reflect.Int: var val int64 = elemValue.Int() Printfln("Int: %v", val) case reflect.Float32, reflect.Float64: var val float64 = elemValue.Float() Printfln("Float: %v", val) case reflect.String: var val string = elemValue.String() Printfln("String: %v", val) case reflect.Ptr: var val reflect.Value = elemValue.Elem() if val.Kind() == reflect.Int { Printfln("Pointer to Int: %v", val.Int()) } default: Printfln("Other: %v", elemValue.String()) } } } func main() { product := Product{ Name: "Kayak", Category: "Watersports", Price: 279, } number := 100 printDetails(true, 10, 23.30, "Alice", &number, product) } ================================================================ printer: package main import "fmt" func Printfln(template string, values ...interface{}) { fmt.Printf(template + "\n", values...) } ================================================================ types.go: package main type Product struct { Name, Category string Price float64 } type Customer struct { Name, City string } >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Output: Bool: true Int: 10 Float: 23.3 String: Alice Pointer to Int: 100 Other: <main.Product Value> `, "486": ``, "487": ``, "488": `488.go tool dist list Output: aix/ppc64 android/386 android/amd64 android/arm android/arm64 darwin/amd64 darwin/arm64 dragonfly/amd64 freebsd/386 freebsd/amd64 freebsd/arm freebsd/arm64 freebsd/riscv64 illumos/amd64 ios/amd64 ios/arm64 js/wasm linux/386 linux/amd64 linux/arm linux/arm64 linux/loong64 linux/mips linux/mips64 linux/mips64le linux/mipsle linux/ppc64 linux/ppc64le linux/riscv64 linux/s390x netbsd/386 netbsd/amd64 netbsd/arm netbsd/arm64 openbsd/386 openbsd/amd64 openbsd/arm openbsd/arm64 plan9/386 plan9/amd64 plan9/arm solaris/amd64 wasip1/wasm windows/386 windows/amd64 windows/arm windows/arm64 `, "489": ``, "490": ``, "491": ``, "492": ``, "493": ``, "494": ``, "495": ``, "496": ``, "497": ``, "498": ``, "499": ``, }, }
View Source
var OriginalWorkWithFiles = DataBase{
Alldatafield: `
334.Working with Files
Putting Working with Files in Context
Answer Question
--------------------------- -------------------------------------------------------
What are they? These features provide access to the file system so that files can be read and written.
Why are they useful? Files are used for everything from logging to configuration files.
How are they used? These features are accessed through the os package, which provides platform-
neutral access to the file system.
Are there any pitfallsor limitations? Some consideration of the underlying file system must be made, especially when
dealing with paths.
Are there any alternatives? Go supports alternative ways of storing data, such as databases, but there are no
alternative mechanisms for accessing files.
Preparing
printer.go:
package main
import (
"fmt"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
product.go:
package main
type Product struct {
Name, Category string
Price float64
}
var Kayak = Product{
Name: "Kayak",
Category: "Watersports",
Price: 279,
}
var Products = []Product{
{"Kayak", "Watersports", 279},
{"Lifejacket", "Watersports", 49.95},
{"Soccer Ball", "Soccer", 19.50},
{"Corner Flags", "Soccer", 34.95},
{"Stadium", "Soccer", 79500},
{"Thinking Cap", "Chess", 16},
{"Unsteady Chair", "Chess", 75},
{"Bling-Bling King", "Chess", 1200},
}
main.go:
package main
func main() {
for _, p := range Products {
Printfln("Product: %v, Category: %v, Price: $%.2f",
p.Name, p.Category, p.Price)
}
}
Output:
Product: Kayak, Category: Watersports, Price: $279.00
Product: Lifejacket, Category: Watersports, Price: $49.95
Product: Soccer Ball, Category: Soccer, Price: $19.50
Product: Corner Flags, Category: Soccer, Price: $34.95
Product: Stadium, Category: Soccer, Price: $79500.00
Product: Thinking Cap, Category: Chess, Price: $16.00
Product: Unsteady Chair, Category: Chess, Price: $75.00
Product: Bling-Bling King, Category: Chess, Price: $1200.00
████████████████████████████████████████████████████████████████████████
335.The os Package Functions for Reading Files
Name Description
------------- -------------------------------------
ReadFile(name) This function opens the specified file and reads its contents. The results are a byte
slice containing the file content and an error indicating problems opening or reading
the file.
Open(name) This function opens the specified file for reading. The result is a File struct and an
error that indicates problems opening the file.
One of the most common reasons to read a file is to load configuration data. The JSON format is well-
suited for configuration files because it is simple to process, has good support in the Go standard library
The Contents of the config.json File in the files Folder:
{
"Username": "Alice",
"AdditionalProducts": [
{"name": "Hat", "category": "Skiing", "price": 10},
{"name": "Boots", "category":"Skiing", "price": 220.51 },
{"name": "Gloves", "category":"Skiing", "price": 40.20 }
]
}
████████████████████████████████████████████████████████████████████████
336.os.ReadFile()
The LoadConfig function uses the ReadFile function to read the contents of the config.json file.
The file will be read from the current working directory when the application is executed,
which means that I can open the file just with its name.
The contents of the file are returned as a byte slice, which is converted to a string and written out. The
LoadConfig function is invoked by an initialization function, which ensures the configuration file is read.
example:
readconfig.go:
package main
import "os"
func LoadConfig() (err error) {
data, err := os.ReadFile("config.json")
if err == nil {
Printfln(string(data))
}
return
}
func init() {
err := LoadConfig()
if err != nil {
Printfln("Error Loading Config: %v", err.Error())
}
}
Output:
{
"Username": "Alice",
"AdditionalProducts": [
{"name": "Hat", "category": "Skiing", "price": 10},
{"name": "Boots", "category":"Skiing", "price": 220.51 },
{"name": "Gloves", "category":"Skiing", "price": 40.20 }
]
}
Product: Kayak, Category: Watersports, Price: $279.00
Product: Lifejacket, Category: Watersports, Price: $49.95
Product: Soccer Ball, Category: Soccer, Price: $19.50
Product: Corner Flags, Category: Soccer, Price: $34.95
Product: Stadium, Category: Soccer, Price: $79500.00
Product: Thinking Cap, Category: Chess, Price: $16.00
Product: Unsteady Chair, Category: Chess, Price: $75.00
Product: Bling-Bling King, Category: Chess, Price: $1200.00
████████████████████████████████████████████████████████████████████████
337.Decoding the JSON Data
example:
readconfig.go:
package main
import (
"encoding/json"
"os"
"strings"
)
type ConfigData struct {
UserName string
AdditionalProducts []Product
}
var Config ConfigData
func LoadConfig() (err error) {
data, err := os.ReadFile("config.json")
if err == nil {
decoder := json.NewDecoder(strings.NewReader(string(data)))
err = decoder.Decode(&Config)
}
return
}
func init() {
err := LoadConfig()
if err != nil {
Printfln("Error Loading Config: %v", err.Error())
} else {
Printfln("Username: %v", Config.UserName)
Products = append(Products, Config.AdditionalProducts...)
}
}
Output:
Username: Alice
Product: Kayak, Category: Watersports, Price: $279.00
Product: Lifejacket, Category: Watersports, Price: $49.95
Product: Soccer Ball, Category: Soccer, Price: $19.50
Product: Corner Flags, Category: Soccer, Price: $34.95
Product: Stadium, Category: Soccer, Price: $79500.00
Product: Thinking Cap, Category: Chess, Price: $16.00
Product: Unsteady Chair, Category: Chess, Price: $75.00
Product: Bling-Bling King, Category: Chess, Price: $1200.00
Product: Hat, Category: Skiing, Price: $10.00
Product: Boots, Category: Skiing, Price: $220.51
Product: Gloves, Category: Skiing, Price: $40.20
████████████████████████████████████████████████████████████████████████
338.os.Open("config.json")
Using the File Struct to Read a File
The Open function opens a file for reading and returns a File value, which represents the open file, and an
error, which is used to indicate problems opening the file. The File struct implements the Reader interface,
which makes it simple to read and process the example JSON data, without reading the entire file into a byte
slice.
The File struct also implements the Closer interface, which defines a Close method.
The defer keyword can be used to call the Close method when the enclosing function completes,
like this:
defer file.Close()
using the defer keyword ensures that the file is closed even when a function returns early.
example:
readconfig.go:
package main
import (
"encoding/json"
"os"
"time"
)
type ConfigData struct {
UserName string
AdditionalProducts []Product
}
var Config ConfigData
func LoadConfig() (err error) {
file, err := os.Open("config.json")
if (err == nil) {
defer file.Close()
decoder := json.NewDecoder(file)
err = decoder.Decode(&Config)
}
return
}
func init() {
time.Sleep(time.Second)
}
func init() {
err := LoadConfig()
if err != nil {
Printfln("Error Loading Config: %v", err.Error())
} else {
Printfln("Username: %v", Config.UserName)
Products = append(Products, Config.AdditionalProducts...)
}
}
Output:
Username: Alice
Product: Kayak, Category: Watersports, Price: $279.00
Product: Lifejacket, Category: Watersports, Price: $49.95
Product: Soccer Ball, Category: Soccer, Price: $19.50
Product: Corner Flags, Category: Soccer, Price: $34.95
Product: Stadium, Category: Soccer, Price: $79500.00
Product: Thinking Cap, Category: Chess, Price: $16.00
Product: Unsteady Chair, Category: Chess, Price: $75.00
Product: Bling-Bling King, Category: Chess, Price: $1200.00
Product: Hat, Category: Skiing, Price: $10.00
Product: Boots, Category: Skiing, Price: $220.51
Product: Gloves, Category: Skiing, Price: $40.20
████████████████████████████████████████████████████████████████████████
339.Methods Defined by the File Struct for Reading at a Specific Location
Name Description
-------------------- ------------------------------------------
ReadAt(slice, offset) This method is defined by the ReaderAt interface and performs a read into the
specific slice at the specified position offset in the file.
Seek(offset, how) This method is defined by the Seeker interface and moves the offset into
جستجو کنید the file for the next read. The offset is determined by the combination of the
two arguments: the first argument specifies the number of bytes to offset,
and the second argument determines how the offset is applied—a value of 0
means the offset is relative to the start of the file, a value of 1 means the offset
is relative to the current read position, and a value of 2 means the offset is
relative to the end of the file.
Reading from specific locations requires knowledge of the file structure.
In this example, I know the location of the data I want to read,
which allows me to use the ReadAt method to read the username value
and the Seek method to jump to the start of the product data.
example:
readconfig.go:
package main
import (
"os"
"encoding/json"
//"strings"
)
type ConfigData struct {
UserName string
AdditionalProducts []Product
}
var Config ConfigData
func LoadConfig() (err error) {
file, err := os.Open("config.json")
if (err == nil) {
defer file.Close()
nameSlice := make([]byte, 5)
file.ReadAt(nameSlice, 19)
Config.UserName = string(nameSlice)
file.Seek(55, 0)
decoder := json.NewDecoder(file)
err = decoder.Decode(&Config.AdditionalProducts)
}
return
}
func init() {
err := LoadConfig()
if err != nil {
Printfln("Username: %v", Config.UserName)
Products = append(Products, Config.AdditionalProducts...)
} else {
Printfln("Error Loading Config: %v", err.Error())
}
}
Output:
Username: Alice
Product: Kayak, Category: Watersports, Price: $279.00
Product: Lifejacket, Category: Watersports, Price: $49.95
Product: Soccer Ball, Category: Soccer, Price: $19.50
Product: Corner Flags, Category: Soccer, Price: $34.95
Product: Stadium, Category: Soccer, Price: $79500.00
Product: Thinking Cap, Category: Chess, Price: $16.00
Product: Unsteady Chair, Category: Chess, Price: $75.00
Product: Bling-Bling King, Category: Chess, Price: $1200.00
████████████████████████████████████████████████████████████████████████
340.The os Package Function for Writing Files
Name Description
------------------------------- ----------------------------------------
WriteFile(name,slice, modePerms) This function creates a file with the specified name, mode, and permissions and
writes the content of the specified byte slice. If the file already exists, its contents
will be replaced with the byte slice. The result is an error that reports any problems
creating the file or writing the data.
OpenFile(name, flag, modePerms) The function opens the file with the specified name, using the flags to control how
the file is opened. If a new file is created, then the specified mode and permissions
are applied. The result is a File value that provides access to the file contents and
an error that indicates problems opening the file.
████████████████████████████████████████████████████████████████████████
341.the Write Convenience Function
The file mode is used to specify special characteristics for the file,
but a value of zero is used for regular files, as in the example.
You can find a list of the file mode values and their settings at
https://golang.org/pkg/io/fs/#FileMode
https://cs.opensource.google/go/go/+/go1.21.1:src/io/fs/fs.go;l=165
type FileMode
type FileMode uint32
A FileMode represents a file's mode and permission bits.
The bits have the same definition on all systems,
so that information about files can be moved from one system to another portably.
Not all bits apply to all systems.
The only required bit is ModeDir for directories.
const (
// The single letters are the abbreviations
// used by the String method's formatting.
ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory
ModeAppend // a: append-only
ModeExclusive // l: exclusive use
ModeTemporary // T: temporary file; Plan 9 only
ModeSymlink // L: symbolic link
ModeDevice // D: device file
ModeNamedPipe // p: named pipe (FIFO)
ModeSocket // S: Unix domain socket
ModeSetuid // u: setuid
ModeSetgid // g: setgid
ModeCharDevice // c: Unix character device, when ModeDevice is set
ModeSticky // t: sticky
ModeIrregular // ?: non-regular file; nothing else is known about this file
// Mask for the type bits. For regular files, none will be set.
ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular
ModePerm FileMode = 0777 // Unix permission bits
)
The defined file mode bits are the most significant bits of the FileMode.
The nine least-significant bits are the standard Unix rwxrwxrwx permissions.
The values of these bits should be considered part of the public API and
may be used in wire protocols or disk representations: they must not be changed,
although new bits might be added.
example:
main.go:
package main
import (
"fmt"
"os"
"time"
)
func main() {
total := 0.0
for _, p := range Products {
total += p.Price
}
dataStr := fmt.Sprintf("Time: %v, Total: $%.2f\n",time.Now().Format("Mon 15:04:05"), total)
err := os.WriteFile("output.txt", []byte(dataStr), 0666)
if err == nil {
fmt.Println("Output file created")
} else {
Printfln("Error: %v", err.Error())
}
}
Output: a file create with this name= output.txt
Time: Thu 07:27:34, Total: $279.00
████████████████████████████████████████████████████████████████████████
342.Using the File Struct to Write to a File
The OpenFile function opens a file and returns a File value.
Unlike the Open function, the OpenFile function accepts one or
more flags that specify how the file should be opened.
The flags are defined as constants in the os package,
Care must be taken with these flags, not all of which are supported by every operating system.
████████████████████████████████████████████████████████████████████████
343.The File Opening Flags
Name Description
-------- ------------------------------------------
O_RDONLY This flag opens the file read-only so that it can be read from but not written to.
O_WRONLY This flag opens the file write-only so that it can be written to but not read from.
O_RDWR This flag opens the file read-write so that it can be written to and read from.
O_APPEND This flag will append writes to the end of the file.
O_CREATE This flag will create the file if it doesn't exist.
O_EXCL This flag is used in conjunction with O_CREATE to ensure that a new file is created. If the file
already exists, this flag will trigger an error.
O_SYNC This flag enables synchronous writes, such that data is written to the storage device before
the write function/method returns.
O_TRUNC This flag truncates the existing content in the file.
████████████████████████████████████████████████████████████████████████
344.Writing to a File
example:
main.go:
package main
import (
"fmt"
"time"
"os"
)
func main() {
total := 0.0
for _, p := range Products {
total += p.Price
}
dataStr := fmt.Sprintf("Time: %v, Total: $%.2f\n",
time.Now().Format("Mon 15:04:05"), total)
file, err := os.OpenFile("output.txt",os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0666)
if (err == nil) {
defer file.Close()
file.WriteString(dataStr)
} else {
Printfln("Error: %v", err.Error())
}
}
I combined the O_WRONLY flag to open the file for writing,
the O_CREATE file to create if it doesn't already
exist, and the O_APPEND flag to append any written data to the end of the file.
Output:
appended to file exist:
Time: Thu 07:27:34, Total: $279.00
Time: Thu 08:17:05, Total: $81174.40
Time: Thu 08:17:09, Total: $81174.40
████████████████████████████████████████████████████████████████████████
345.The File Methods for Writing Data
Name Description
----------------------- ---------------------------------------------------
Seek(offset, how) This method sets the location for subsequent operations.
Write(slice) This method writes the contents of the specified byte slice to the file.
The results are the number of bytes written and an error that indicates
problems writing the data.
WriteAt(slice, offset) This method writes the data in the slice at the specified location and is the
counterpart to the ReadAt method.
WriteString(str) This method writes a string to the file. This is a convenience method that
converts the string to a byte slice, invokes the Write method, and returns the
results it receives.
████████████████████████████████████████████████████████████████████████
346.Writing JSON Data to a File
example:
main.go:
package main
import (
// "fmt"
// "time"
"encoding/json"
"os"
)
func main() {
cheapProducts := []Product{}
for _, p := range Products {
if p.Price < 100 {
cheapProducts = append(cheapProducts, p)
}
}
file, err := os.OpenFile("cheap.json", os.O_WRONLY|os.O_CREATE, 0666)
if err == nil {
defer file.Close()
encoder := json.NewEncoder(file)
encoder.Encode(cheapProducts)
} else {
Printfln("Error: %v", err.Error())
}
}
Output:
create file = cheap.json
content of this:
[{"Name":"Lifejacket","Category":"Watersports","Price":49.95},{"Name":"Soccer Ball","Category":"Soccer","Price":19.5},{"Name":"Corner Flags","Category":"Soccer","Price":34.95},{"Name":"Thinking Cap","Category":"Chess","Price":16},{"Name":"Unsteady Chair","Category":"Chess","Price":75}]
████████████████████████████████████████████████████████████████████████
347.the Convenience Functions to Create New Files
The os Package Functions for Creating Files
The CreateTemp function can be useful, but it is important to understand that the purpose of this
function is to generate a random filename and that in all other respects, the file that is created is just a
regular file. The file that is created isn't removed automatically and will remain on the storage device after
the application has been executed.
Name Description
------------------------- --------------------------------------------
Create(name) This function is equivalent to calling OpenFile with the O_RDWR, O_CREATE, and
O_TRUNC flags. The results are the File, which can be used for reading and writing,
and an error that is used to indicate problems creating the file. Note that this
combination of flags means that if a file exists with the specified name, it will be
opened, and its contents will be deleted.
CreateTemp(dirName, fileName) This function creates a new file in the directory with the specified name. If the
name is the empty string, then the system temporary directory is used, obtained
using the TempDir function. The file is created with a
name that contains a random sequence of characters, as demonstrated in the text
after the table. The file is opened with the O_RDWR, O_CREATE, and O_EXCL flags.
The file isn't removed when it is closed.
████████████████████████████████████████████████████████████████████████
348.Creating a Temporary File
The location of the temporary file is specified with a period, meaning the current working directory.
if the empty string is used, then the file will be created in the default temporary
directory, which is obtained using the TempDir function described.
The name of the file can include an asterisk (the * character),
and if this is present, the random part of the filename will replace it.
If the filename does not contain an asterisk,
then the random part of the filename will be added to the end of the name.
ompile and execute the project, and once execution is complete, you will see a new file in the files
folder. The file in my project is named tempfile-1732419518.json, but your filename will be different, and
you will see a new file and a unique name each time the program is executed.
example:
main.go:
package main
import (
"os"
"encoding/json"
)
func main() {
cheapProducts := []Product {}
for _, p := range Products {
if (p.Price < 100) {
cheapProducts = append(cheapProducts, p)
}
}
file, err := os.CreateTemp(".", "tempfile-*.json")
if (err == nil) {
defer file.Close()
encoder := json.NewEncoder(file)
encoder.Encode(cheapProducts)
} else {
Printfln("Error: %v", err.Error())
}
}
Output:
tempfile-1982129407.json:
[{"Name":"Lifejacket","Category":"Watersports","Price":49.95},{"Name":"Soccer Ball","Category":"Soccer","Price":19.5},{"Name":"Corner Flags","Category":"Soccer","Price":34.95},{"Name":"Thinking Cap","Category":"Chess","Price":16},{"Name":"Unsteady Chair","Category":"Chess","Price":75}]
████████████████████████████████████████████████████████████████████████
349.Working with File Paths
If you want to read and write files in
other locations, then you must specify file paths. The issue is that not all of the operating systems that Go
supports express file paths in the same way. For example, the path to a file named mydata.json in my home
directory on a Linux system might be expressed like this:
/home/adam/mydata.json
where the path to the same in my home directory is expressed like this:
C:\Users\adam\mydata.json
████████████████████████████████████████████████████████████████████████
350.The Common Location Functions Defined by the os Package
Name Description
-------------- ------------------------------------
Getwd() This function returns the current working directory, expressed as a string, and an
error that indicates problems obtaining the value.
UserHomeDir() This function returns the user's home directory and an error that indicates problems
obtaining the path.
UserCacheDir() This function returns the default directory for user-specific cached data and an error
that indicates problems obtaining the path.
UserConfigDir() This function returns the default directory for user-specific configuration data and an
error that indicates problems obtaining the path.
TempDir() This function returns the default directory for temporary files and an error that
indicates problems obtaining the path.
████████████████████████████████████████████████████████████████████████
351.The path/filepath Functions for Paths
Name Description
------------ ------------------------------------------
Abs(path) This function returns an absolute path, which is useful if you have a relative path,
such as a filename.
IsAbs(path) This function returns true if the specified path is absolute.
Base(path) This function returns the last element from the path.
Clean(path) This function tidies up path strings by removing duplicate separators and relative references.
Dir(path) This function returns all but the last element of the path.
EvalSymlinks(path) This function evaluates a symbolic link and returns the resulting path.
Ext(path) This function returns the file extension from the specified path, which is
assumed to be the suffix following the final period in the path string.
FromSlash(path) This function replaces each forward slash with the platform's file separator character.
ToSlash(path) This function replaces the platform's file separator with forward slashes.
Join(...elements) This function combines multiple elements using the platform's file separator.
Match(pattern, path) This function returns true if the path is matched by the specified pattern.
Split(path) This function returns the components on either side of the final path separator in
the specified path.
SplitList(path) This function splits a path into its components, which are returned as a string slice.
VolumeName(path) This function returns the volume component of the specified path or the empty
string if the path does not contain a volume.
████████████████████████████████████████████████████████████████████████
352.Working with a Path
example:
main.go
package main
import (
// "fmt"
// "time"
"os"
//"encoding/json"
"path/filepath"
)
func main() {
path, err := os.UserHomeDir()
if (err == nil) {
path = filepath.Join(path, "MyApp", "MyTempFile.json")
}
Printfln("Full path: %v", path)
Printfln("Volume name: %v", filepath.VolumeName(path))
Printfln("Dir component: %v", filepath.Dir(path))
Printfln("File component: %v", filepath.Base(path))
Printfln("File extension: %v", filepath.Ext(path))
}
Output:
Username: Alice
Full path: /home/sina/MyApp/MyTempFile.json
Volume name:
Dir component: /home/sina/MyApp
File component: MyTempFile.json
File extension: .json
received on the Windows machine:
Username: Alice
Full path: C:\Users\adam\MyApp\MyTempFile.json
Volume name: C:
Dir component: C:\Users\adam\MyApp
File component: MyTempFile.json
File extension: .json
████████████████████████████████████████████████████████████████████████
353.The os Package Functions for Managing Files and Directories
Name Description
----------------- ------------------------------------------
Chdir(dir) This function changes the current working directory to the specified directory.
The result is an error that indicates problems making the change.
Mkdir(name, modePerms) This function creates a directory with the specified name and mode/
permissions. The result is an error that is nil if the directory is created or that
describes a problem if one arises.
MkdirAll(name, modePerms) This function performs the same task as Mkdir but creates any parent directories
in the specified path.
MkdirTemp(parentDir, name) This function is similar to CreateTemp but creates a directory rather than a file.
A random string is added to the end of the specified name or in place of an
asterisk, and the new directory is created within the specified parent. The results
are the name of the directory and an error indicating problems.
Remove(name) This function removes the specified file or directory. The result is an error that
describes any problems that arise.
RemoveAll(name) This function removes the specified file or directory. If the name specifies a
directory, then any children it contains are also removed. The result is an error
that describes any problems that arise.
Rename(old, new) This function renames the specified file or folder. The result is an error that
describes any problems that arise.
Symlink(old, new) This function creates a symbolic link to the specified file. The result is an error
that describes any problems that arise.
████████████████████████████████████████████████████████████████████████
354.Creating Directories
example:
main.go:
package main
import (
// "fmt"
// "time"
"encoding/json"
"os"
"path/filepath"
)
func main() {
path, err := os.UserHomeDir()
if err == nil {
path = filepath.Join(path, "0-Repo/TEST-2/MyApp", "MyTempFile.json")
}
Printfln("Full path: %v", path)
err = os.MkdirAll(filepath.Dir(path), 0766)
if err == nil {
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666)
if err == nil {
defer file.Close()
encoder := json.NewEncoder(file)
encoder.Encode(Products)
}
}
if err != nil {
Printfln("Error %v", err.Error())
}
}
Output:
print this:
Username: Alice
Full path: /home/sina/0-Repo/TEST-2/MyApp/MyTempFile.json
Create this:
MyTempFile.json in that directory with content:
[{"Name":"Kayak","Category":"Watersports","Price":279},{"Name":"Lifejacket","Category":"Watersports","Price":49.95},{"Name":"Soccer Ball","Category":"Soccer","Price":19.5},{"Name":"Corner Flags","Category":"Soccer","Price":34.95},{"Name":"Stadium","Category":"Soccer","Price":79500},{"Name":"Thinking Cap","Category":"Chess","Price":16},{"Name":"Unsteady Chair","Category":"Chess","Price":75},{"Name":"Bling-Bling King","Category":"Chess","Price":1200}]
████████████████████████████████████████████████████████████████████████
355.ReadDir(name)
The os Package Function for Listing Directories
This function reads the specified directory and returns a DirEntry slice, each of which
describes an item in the directory.
The result of the ReadDir function is a slice of values that implement the DirEntry interface, which
defines the methods.
████████████████████████████████████████████████████████████████████████
356.The Methods Defined by the DirEntry Interface
Name Description
-------- --------------------------
Name() This method returns the name of the file or directory described by the DirEntry value.
IsDir() This method returns true if the DirEntry value represents a directory.
Type() This method returns a FileMode value, which is an alias to uint32, which describes the file more
and the permissions of the file or directory represented by the DirEntry value.
Info() This method returns a FileInfo value that provides additional details about the file or directory
represented by the DirEntry value.
████████████████████████████████████████████████████████████████████████
357.Useful Methods Defined by the FileInfo Interface
Name Description
---------- ------------------------------------
Name() This method returns a string containing the name of the file or directory.
Size() This method returns the size of the file, expressed as an int64 value.
Mode() This method returns the file mode and permission settings for the file or directory.
ModTime() This method returns the last modified time of the file or directory.
████████████████████████████████████████████████████████████████████████
358.Stat(path)
The os Package Function for Inspecting a File
This function accepts a path string. It returns a FileInfo value that describes the file and an
error, which indicates problems inspecting the file.
████████████████████████████████████████████████████████████████████████
359.Enumerating Files
example:
main.go:
package main
import (
"os"
)
func main() {
path, err := os.Getwd()
if err == nil {
dirEntries, err := os.ReadDir(path)
if err == nil {
for _, dentry := range dirEntries {
Printfln("Entry name: %v, IsDir: %v", dentry.Name(), dentry.IsDir())
}
}
}
if err != nil {
Printfln("Error %v", err.Error())
}
}
Output:
Username: Alice
Entry name: .git, IsDir: true
Entry name: .vscode, IsDir: true
Entry name: README.md, IsDir: false
Entry name: U.sh, IsDir: false
Entry name: cheap.json, IsDir: false
Entry name: config.json, IsDir: false
Entry name: go.mod, IsDir: false
Entry name: main.go, IsDir: false
Entry name: output.txt, IsDir: false
Entry name: printer.go, IsDir: false
Entry name: product.go, IsDir: false
Entry name: readconfig.go, IsDir: false
████████████████████████████████████████████████████████████████████████
360.Determining Whether a File Exists تعیین اینکه آیا یک فایل وجود دارد یا خیر
Checking Whether a File Exists:
main.go:
package main
import (
"os"
)
func main() {
targetFiles := []string { "no_such_file.txt", "config.json" }
for _, name := range targetFiles {
info, err := os.Stat(name)
if os.IsNotExist(err) {
Printfln("File does not exist: %v", name)
} else if err != nil {
Printfln("Other error: %v", err.Error())
} else {
Printfln("File %v, Size: %v", info.Name(), info.Size())
}
}
}
Output:
Username: Alice
File does not exist: no_such_file.txt
File config.json, Size: 253
████████████████████████████████████████████████████████████████████████
361.The path/filepath Function for Locating Files with a Pattern
Name Description
-------------------- -------------------------------
Match(pattern, name) This function matches a single path against a pattern. The results are a bool,
which indicates if there is a match, and an error, which indicates problems
with the pattern or with performing the match.
Glob(pathPatten) This function finds all the files that match the specified pattern. The results
are a string slice containing the matched paths and an error that indicates
problems with performing the search.
████████████████████████████████████████████████████████████████████████
362.The Search Pattern Syntax for the path/filepath Functions
Term Description
-------- -------------------
* This term matches any sequence of characters, excluding the path separator.
? This term matches any single character, excluding the path separator.
[a-Z] This term matches any character in the specified range.
████████████████████████████████████████████████████████████████████████
363.Locating Files
example:
main.go:
package main
import (
"os"
"path/filepath"
)
func main() {
path, err := os.Getwd()
if err == nil {
matches, err := filepath.Glob(filepath.Join(path, "*.json"))
if err == nil {
for _, m := range matches {
Printfln("Match: %v", m)
}
}
}
if err != nil {
Printfln("Error %v", err.Error())
}
}
Output:
Username: Alice
Match: /home/sina/0-Repo/TEST-2/cheap.json
Match: /home/sina/0-Repo/TEST-2/config.json
████████████████████████████████████████████████████████████████████████
364.The Function Provided by the path/filepath Package
Name Description
----------------------- ----------------------------------
WalkDir(directory, func) This function calls the specified function for each file and directory in the
specified directory.
████████████████████████████████████████████████████████████████████████
365.Walking a Directory قدم زدن در یک فهرست
example:
main.go:
package main
import (
"os"
"path/filepath"
)
func callback(path string, dir os.DirEntry, dirErr error) (err error) {
info, _ := dir.Info()
Printfln("Path %v, Size: %v", path, info.Size())
return
}
func main() {
path, err := os.Getwd()
if err == nil {
err = filepath.WalkDir(path, callback)
} else {
Printfln("Error %v", err.Error())
}
}
Output:
Username: Alice
Path /home/sina/0-Repo/TEST-2, Size: 4096
Path /home/sina/0-Repo/TEST-2/.git, Size: 4096
Path /home/sina/0-Repo/TEST-2/.git/COMMIT_EDITMSG, Size: 4
Path /home/sina/0-Repo/TEST-2/.git/FETCH_HEAD, Size: 92
Path /home/sina/0-Repo/TEST-2/.git/refs/remotes/origin/main, Size: 41
Path /home/sina/0-Repo/TEST-2/.git/refs/tags, Size: 4096
Path /home/sina/0-Repo/TEST-2/.vscode, Size: 4096
Path /home/sina/0-Repo/TEST-2/.vscode/extensions.json, Size: 79
Path /home/sina/0-Repo/TEST-2/.vscode/settings.json, Size: 405
Path /home/sina/0-Repo/TEST-2/README.md, Size: 9
Path /home/sina/0-Repo/TEST-2/U.sh, Size: 68
Path /home/sina/0-Repo/TEST-2/cheap.json, Size: 487
Path /home/sina/0-Repo/TEST-2/config.json, Size: 253
Path /home/sina/0-Repo/TEST-2/go.mod, Size: 22
Path /home/sina/0-Repo/TEST-2/main.go, Size: 8579
Path /home/sina/0-Repo/TEST-2/output.txt, Size: 109
Path /home/sina/0-Repo/TEST-2/printer.go, Size: 129
Path /home/sina/0-Repo/TEST-2/product.go, Size: 474
Path /home/sina/0-Repo/TEST-2/readconfig.go, Size: 704
████████████████████████████████████████████████████████████████████████
`,
}
View Source
var Questions = AllQuestions{ SingleQues: map[string]string{ "operator": "The cars are produced on an assembly line. The assembly line has a certain speed, that can be changed. The faster the assembly line speed is, the more cars are produced. However, changing the speed of the assembly line also changes the number of cars that are produced successfully, that is cars without any errors in their production. Implement a function that takes in the number of cars produced per hour and the success rate and calculates the number of successful cars made per hour. The success rate is given as a percentage, Note: the return value should be a float64, from 0 to 100:", "operator 1": "The cars are produced on an assembly line. The assembly line has a certain speed, that can be changed. The faster the assembly line speed is, the more cars are produced. However, changing the speed of the assembly line also changes the number of cars that are produced successfully, that is cars without any errors in their production. Implement a function that takes in the number of cars produced per hour and the success rate and calculates the number of successful cars made per hour. The success rate is given as a percentage, Note: the return value should be a float64, from 0 to 100:", }, }
View Source
var QuestionsSample = QuestionsSampleStruct{ MapQuestionsSample: map[string]string{ "operator": `Sample: CalculateWorkingCarsPerHour(1547, 90) expect: // => 1392.3`, "operator 1": `Sample: CalculateWorkingCarsPerHour(1547, 90) expect: // => 1392.3`, }, }
View Source
var TitleDataBases = []string{
"ALL WORKING WITH DATABASES",
"ALLWORKINGWITHDATABASES",
"ALL DATABASES",
"ALLDATABASES",
}
View Source
var TitleHTTPClients = []string{
"ALL HTTP CLIENTS",
"ALLHTTPCLIENTS",
"ALL CREATING HTTP CLIENTS",
"ALLCREATINGHTTPCLIENTS",
}
View Source
var TitleHTTPServers = []string{
"ALL HTTP SERVERS",
"ALLHTTPSERVERS",
"ALL CREATING HTTP SERVERS",
"ALLCREATINGHTTPSERVERS",
}
View Source
var TitleOfAllIndexSlices = map[string]string{
"READING AND WRITING DATA": "READING AND WRITING DATA",
"READINGANDWRITINGDATA": "READINGANDWRITINGDATA",
"ALL READING AND WRITING DATA": "ALL READING AND WRITING DATA",
"ALLREADINGANDWRITINGDATA": "ALLREADINGANDWRITINGDATA",
"ALL REGEX": "ALL REGEX",
"ALLREGEX": "ALLREGEX",
"ALL TIME": "ALL TIME",
"ALLTIME": "ALLTIME",
"ALL HTML AND TEMPLATE": "ALL HTML AND TEMPLATE",
"ALLHTMLANDTEMPLATE": "ALLHTMLANDTEMPLATE",
"ALL WORKING WITH FILES": "ALL WORKING WITH FILES",
"ALLWORKINGWITHFILES": "ALLWORKINGWITHFILES",
"ALL JSON": "ALL JSON",
"ALLJSON": "ALLJSON",
"ALL JSON DATA": "ALL JSON DATA",
"ALLJSONDATA": "ALLJSONDATA",
"ALL WORK WITH JSON DATA": "ALL WORK WITH JSON DATA",
"ALLWORKWITHJSONDATA": "ALLWORKWITHJSONDATA",
"ALL WORKING WITH JSON DATA": "ALL WORKING WITH JSON DATA",
"ALLWORKINGWITHJSONDATA": "ALLWORKINGWITHJSONDATA",
"ALL HTTP SERVERS": "ALL HTTP SERVERS",
"ALLHTTPSERVERS": "ALLHTTPSERVERS",
"ALL CREATING HTTP SERVERS": "ALL CREATING HTTP SERVERS",
"ALLCREATINGHTTPSERVERS": "ALLCREATINGHTTPSERVERS",
"ALL HTTP CLIENTS": "ALL HTTP CLIENTS",
"ALLHTTPCLIENTS": "ALLHTTPCLIENTS",
"ALL CREATING HTTP CLIENTS": "ALL CREATING HTTP CLIENTS",
"ALLCREATINGHTTPCLIENTS": "ALLCREATINGHTTPCLIENTS",
"ALL WORKING WITH DATABASES": "ALL WORKING WITH DATABASES",
"ALLWORKINGWITHDATABASES": "ALLWORKINGWITHDATABASES",
"ALL DATABASES": "ALL DATABASES",
"ALLDATABASES": "ALLDATABASES",
"ALLUSINGREFLECTION": "ALLUSINGREFLECTION",
"ALL USING REFLECTION": "ALL USING REFLECTION",
}
View Source
var TitleOfEveryWords = []string{
"all",
"-all",
"--all",
}
View Source
var TitleOfHelp = []string{
"-h",
"help",
"-help",
"--help",
}
View Source
var TitleOfJSON = []string{
"ALL JSON",
"ALLJSON",
"ALL JSON DATA",
"ALLJSONDATA",
"ALL WORK WITH JSON DATA",
"ALLWORKWITHJSONDATA",
"ALL WORKING WITH JSON DATA",
"ALLWORKINGWITHJSONDATA",
}
View Source
var TitleOfReadingWriting = []string{
"READING AND WRITING DATA",
"READINGANDWRITINGDATA",
"ALL READING AND WRITING DATA",
"ALLREADINGANDWRITINGDATA",
}
View Source
var TitleOfRegEx = []string{
"ALL REGEX",
"ALLREGEX",
}
View Source
var TitleOfTimeData = []string{
"ALL TIME",
"ALLTIME",
}
View Source
var TitleOfUsingHTMLAndTextTemplates = []string{
"ALL HTML AND TEMPLATE",
"ALLHTMLANDTEMPLATE",
}
View Source
var TitleOfWorkingFiles = []string{
"ALL WORKING WITH FILES",
"ALLWORKINGWITHFILES",
}
View Source
var TitleUsingReflection = []string{
"ALLUSINGREFLECTION",
"ALL USING REFLECTION",
}
Functions ¶
func ClearTerminal ¶
func ClearTerminal()
func GetMapReturnSlice ¶
func GetMapReturnSlice() []string
func GoodByePrint ¶
func GoodByePrint()
func HelpMessage ¶
func HelpMessage()
func PrintWordByWord ¶
func PrintWordByWord(words []string)
func SplitIntoWords ¶
Types ¶
type AllAnswers ¶ added in v1.0.29
type AllMapUsages ¶ added in v1.0.29
type AllQuestions ¶ added in v1.0.28
type AnswersSampleStruct ¶ added in v1.0.29
type Features ¶
type Features struct {
EveryWordsInGolang string
}
var OriginalFeatures Features = Features{
EveryWordsInGolang: `
████████████████████████████████████████████████████████████████████████
0.Create Environment GO
in Command Line Interface Go:
1- go mod init YOURNAME
2- go work init YOURWORKDIRECTORY
3- go run main.go OR go run projectName.go
package main = first executable file main.go
func main() {} = every execute files in "package main"
import = for importing another package
function
A function is an independent section of code
that mapszero or more input parameters to zero or more outputparameters.
Functions (also known as procedures orsubroutines)
are often represented as a black box: (theblack box represents the function)
func FunctionName(ParameterName ParameterType) ReturnType { body of function}
example:
func getUserName(UserInput string) string { return UserInput }
returning multiple values
Go is also capable of returning multiple values from afunction
variadic functions
func add(args ...int) int {}
████████████████████████████████████████████████████████████████████████
1.build
Using the Go Command
The go build command compiles the source code in the current directory
and generates an executable file.
████████████████████████████████████████████████████████████████████████
2.clean
The go clean command removes the output produced by the go build command,
including the executable and any temporary files that were created during the build.
████████████████████████████████████████████████████████████████████████
3.doc
The go doc command generates documentation from source code.
████████████████████████████████████████████████████████████████████████
4.fmt
The go fmt command ensures consistent indentation and alignment in source code files.
fmt = for printing in CLI
fmt.Printf("%v %s %f", variable, string, float32)
████████████████████████████████████████████████████████████████████████
5.get
The go get command downloads and installs external packages.
flag
flag package:
Command-line flags are a common way to specify options for command-line programs.
For example, in wc -l the -l is a command-line flag.
Go provides a flag package supporting basic command-line flag parsing.
We'll use this package to implement our example command-line program.
████████████████████████████████████████████████████████████████████████
6.install
The go install command downloads packages and is usually used to install tool packages.
████████████████████████████████████████████████████████████████████████
7.help
The go help command displays help information for other Go features. The command go
help build, for example, displays information about the build argument.
████████████████████████████████████████████████████████████████████████
8.mod
The go mod command is used to create and manage a Go module.
████████████████████████████████████████████████████████████████████████
9.run
The go run command builds and executes the source code in a specified folder without
creating an executable output
████████████████████████████████████████████████████████████████████████
10.test
The go test command executes unit tests
████████████████████████████████████████████████████████████████████████
11.version
The go version command writes out the Go version number.
████████████████████████████████████████████████████████████████████████
12.vet
The go vet command detects common problems in Go code
████████████████████████████████████████████████████████████████████████
13.print <expr>
Useful Debugger State Commands
This command evaluates an expression and displays the result. It can
be used to display a value (print i) or perform a more complex test
(print i > 0).
████████████████████████████████████████████████████████████████████████
14.set <variable> = <value>
This command changes the value of the specified variable.
این دستور مقدار متغیر مشخص شده را تغییر می دهد.
████████████████████████████████████████████████████████████████████████
15.locals
This command prints the value of all local variables.
████████████████████████████████████████████████████████████████████████
16.whatis <expr>
This command prints the type of the specified expression such as whatis
████████████████████████████████████████████████████████████████████████
17.continue
Useful Debugger Commands for Controlling Execution
This command resumes execution of the application.
████████████████████████████████████████████████████████████████████████
18.next
This command moves to the next statement.
████████████████████████████████████████████████████████████████████████
19.step
This command steps into the current statement.
████████████████████████████████████████████████████████████████████████
20.stepout
This command steps out of the current statement.
████████████████████████████████████████████████████████████████████████
21.restart
This command restarts the process. Use the continue command to begin execution.
████████████████████████████████████████████████████████████████████████
22.exit
This command exits the debugger.
████████████████████████████████████████████████████████████████████████
23.int = integers
Understanding the Basic Data Types
This type represents a whole number, which can be positive or negative. The
int type size is platform-dependent and will be either 32 or 64 bits. There are
also integer types that have a specific size, such as int8, int16, int32, and
int64, but the int type should be used unless you need a specific size.
Literal Value Examples:
20, -20. Values can also be expressed in hex (0x14), octal (0o24), and binary notation
(0b0010100).
████████████████████████████████████████████████████████████████████████
24.unit
uint8, uint16, uint32, uint64,int8, int16, int32
This type represents a positive whole number. The uint type size is platform-
dependent and will be either 32 or 64 bits. There are also unsigned integer
types that have a specific size, such as uint8, uint16, uint32, and uint64, but
the uint type should be used unless you need a specific size.
Literal Value Examples:
There are no uint literals. All literal whole numbers are treated as int values.
████████████████████████████████████████████████████████████████████████
25.byte = uint8
This type is an alias for uint8 and is typically used to represent a byte of data.
byte = 8 bits,
1024bytes = 1 kilobyte
1024 kilobytes = 1 megabyte
Literal Value Examples:
There are no byte literals. Bytes are typically expressed as integer literals (such as 101) or run
literals ('e') since the byte type is an alias for the uint8 type.
████████████████████████████████████████████████████████████████████████
26.float32, float64
Floating Point Numbers. e.g 3.14, 123.541, 100.4020401
These types represent numbers with a fraction. These types allocate 32 or 64
bits to store the value.
Literal Value Examples:
20.2, -20.2, 1.2e10, 1.2e-10. Values can also be expressed in hex notation (0x2p10), although
the exponent is expressed in decimal digits.
████████████████████████████████████████████████████████████████████████
27.complex64, complex128
These types represent numbers that have real and imaginary components.
These types allocate 64 or 128 bits to store the value.
████████████████████████████████████████████████████████████████████████
28.bool
bolean
This type represents a Boolean truth with the values true and false.
Literal Value Examples:
true, false.
████████████████████████████████████████████████████████████████████████
29.string
This type represents a sequence of characters.
Literal Value Examples:
"Hello". Character sequences escaped with a backslash are interpreted if the value is enclosed
in double quotes ("Hello\n"). Escape sequences are not interpreted if the value is enclosed in
backquotes ('Hello\n').
████████████████████████████████████████████████████████████████████████
30.rune = int32
This type represents a single Unicode code point. Unicode is complicated,
but—loosely—this is the representation of a single character. The rune type is
an alias for int32.
Literal Value Examples:
'A', '\n', '\u00A5', '¥'. Characters, glyphs, and escape sequences are enclosed in single
quotes (the ' character).
████████████████████████████████████████████████████████████████████████
31.var = variable
Variables are defined using the var keyword, and, unlike constants, the value assigned to a variable can be
changed
████████████████████████████████████████████████████████████████████████
32.const = constant
Constants are basically variables whose values cannot be changed later.
The Zero Values for the Basic Data Types
Type Zero Value
------ -----------
int 0
unit 0
byte 0
float64 0
bool false
string “” (the empty string)
rune 0
████████████████████████████████████████████████████████████████████████
33.+, -, *, /, %
Operations and Conversions
These operators are used to perform arithmetic using numeric values.
████████████████████████████████████████████████████████████████████████
34.==, !=, <, <=, >, >=
These operators compare two values.
████████████████████████████████████████████████████████████████████████
35., &&, !
These are the logical operators, which are applied to bool values and return a bool value.
████████████████████████████████████████████████████████████████████████
36.=, :=
These are the assignment operators. The standard assignment operator (=) is used to set
the initial value when a constant or variable is defined, or to change the value assigned to
a previously defined variable. The shorthand operator (:=) is used to define a variable and
assign a value.
████████████████████████████████████████████████████████████████████████
37.-=, +=, ++, --
These operators increment and decrement numeric values.
████████████████████████████████████████████████████████████████████████
38.&, , ^, &^, <<, >>
These are the bitwise operators, which can be applied to integer values. These operators are
not often required in mainstream development
where the operator is used to configure the Go logging features.
The Arithmetic Operators
Operator Description
------- ---------------------
+ This operator returns the sum of two operands.
- This operator returns the difference between two operands.
* This operator returns the product of two operands.
/ This product returns the quotient of two operators.
% This product returns the remainder, which is similar to the modulo operator provided by
other programming languages but can return negative values, as described in the “Using the
Remainder Operator” section.
The Comparison Operators
Operator Description
------- ---------------------
== This operator returns true if the operands are equal.
!= This operator returns true if the operands are not equal.
< This operator returns true if the first operand is less than the second operand.
> This operator returns true if the first operand is greater than the second operand.
<= This operator returns true if the first operand is less than or equal to the second operand.
>= This operator returns true if the first operand is greater than or equal to the second
operand.
The Logical Operators
Operator Description
------- ---------------------
|| This operator returns true if either operand is true.
If the first operand is true, then the second
operand will not be evaluated.
&& This operator returns true if both operands are true.
If the first operand is false, then the
second operand will not be evaluated.
! This operator is used with a single operand.
It returns true if the operand is false and false if
the operand is true.
████████████████████████████████████████████████████████████████████████
39.Ceil(value)
Converting Floating-Point Values to Integers
Functions in the math Package for Converting Numeric Types
This function returns the smallest integer that is greater than the specified floating-
point value. The smallest integer that is greater than 27.1, for example, is 28.
████████████████████████████████████████████████████████████████████████
40.Floor(value)
This function returns the largest integer that is less than the specified floating-point
value. The largest integer that is less than 27.1, for example, is 28.
████████████████████████████████████████████████████████████████████████
41.Round(value)
This function rounds the specified floating-point value to the nearest integer.
████████████████████████████████████████████████████████████████████████
42.RoundToEven(value)
This function rounds the specified floating-point value to the nearest even integer.
████████████████████████████████████████████████████████████████████████
43.ParseBool(str)
Parsing from Strings
Functions for Parsing Strings into Other Data Types
This function parses a string into a bool value. Recognized string values are "true",
"false", "TRUE", "FALSE", "True", "False", "T", "F", "0", and "1".
████████████████████████████████████████████████████████████████████████
44.ParseFloat(str,size)
This function parses a string into a floating-point value with the specified size, as
described in the “Parsing Floating-Point Numbers” section.
████████████████████████████████████████████████████████████████████████
45.ParseInt(str,base, size)
This function parses a string into an int64 with the specified base and size. Acceptable
base values are 2 for binary, 8 for octal, 16 for hex, and 10.
████████████████████████████████████████████████████████████████████████
46.ParseUint(str,base, size)
This function parses a string into an unsigned integer value with the specified base and size.
████████████████████████████████████████████████████████████████████████
47.Atoi(str)
This function parses a string into a base 10 int and is equivalent to calling
ParseInt(str, 10, 0)
████████████████████████████████████████████████████████████████████████
48.FormatBool(val)
Formatting Values as Strings
The strconv Functions for Converting Values into Strings
This function returns the string true or false based on the value of the
specified bool.
████████████████████████████████████████████████████████████████████████
49.FormatInt(val, base)
This function returns a string representation of the specified int64 value,
expressed in the specified base.
████████████████████████████████████████████████████████████████████████
50.FormatUint(val, base)
This function returns a string representation of the specified uint64 value,
expressed in the specified base.
████████████████████████████████████████████████████████████████████████
51.FormatFloat(val, format, precision, size)
This function returns a string representation of the specified float64 value,
expressed using the specified format, precision, and size.
████████████████████████████████████████████████████████████████████████
52.Itoa(val)
This function returns a string representation of the specified int value,
expressed using base 10.
Commonly Used Format Options for Floating-Point String Formatting
Function
f
The floating-point value will be expressed in the form ±ddd.ddd without an exponent, such as
49.95.
e, E
The floating-point value will be expressed in the form ±ddd.ddde±dd, such as 4.995e+01 or
4.995E+01. The case of the letter denoting the exponent is determined by the case of the rune
used as the formatting argument.
g, G
The floating-point value will be expressed using format e/E for large exponents or format f for
smaller values.
████████████████████████████████████████████████████████████████████████
53.if
Understanding Flow Control
The if keyword is followed by the expression and then the group of statements to be executed,
surrounded by braces
████████████████████████████████████████████████████████████████████████
54.else
The else keyword can be used to create additional clauses in an if statement
████████████████████████████████████████████████████████████████████████
55.else if
The else/if combination can be repeated to create a sequence of clauses
████████████████████████████████████████████████████████████████████████
56.for
Go allows loops only inside of functions.
The for keyword is used to create loops that repeatedly execute statements. The most basic for loops will
repeat indefinitely unless interrupted by the break keyword
Incorporating the Condition into the Loop
example:
for (counter <= 3) {}
Enumerating Sequences:
for index, character := range product {
fmt.Println("Index:", index, "Character:", string(character))
}
range : is for variable like slice or array like this :
for name := range sliceOfNames
████████████████████████████████████████████████████████████████████████
57.switch
A switch statement provides an alternative way to control execution flow, based on matching the result of an
expression to a specific value, as opposed to evaluating a true or false result
example:
switch (character) {
case 'K':
fmt.Println("K at position", index)
case 'y':
fmt.Println("y at position", index)
}
Matching Multiple Values:
example:
switch (character) {
case 'K', 'k':
fmt.Println("K or k at position", index)
case 'y':
fmt.Println("y at position", index)
}
Terminate case Statement Execution:
example:
switch (character) {
case 'K', 'k':
if (character == 'k') {
fmt.Println("Lowercase k at position", index)
break
}
fmt.Println("Uppercase K at position", index)
case 'y':
fmt.Println("y at position", index)
}
████████████████████████████████████████████████████████████████████████
58.Falling Through
Go switch statements don't automatically fall through, but this behavior can be enabled using the
fallthrough keyword
example:
switch (character) {
case 'K':
fmt.Println("Uppercase character")
fallthrough
case 'k':
fmt.Println("k at position", index)
case 'y':
fmt.Println("y at position", index)
}
████████████████████████████████████████████████████████████████████████
59.default
The default keyword is used to define a clause that will be executed when none of the case statements
matches the switch statement's value
example:
switch (character) {
case 'K', 'k':
if (character == 'k') {
fmt.Println("Lowercase k at position", index)
break
}
fmt.Println("Uppercase K at position", index)
case 'y':
fmt.Println("y at position", index)
default:
fmt.Println("Character", string(character), "at position", index)
}
████████████████████████████████████████████████████████████████████████
60.goto label
Label statements allow execution to jump to a different point,
giving greater flexibility than other flow control features
example:
counter := 0
target: fmt.Println("Counter", counter)
counter++
if (counter < 5) {
goto target
}
████████████████████████████████████████████████████████████████████████
61.Array
Go arrays are a fixed length and contain elements of a single type, which are accessed by index
var names [3]string
An array is a numbered sequence of elements of a sin-gle type with a fixed length.
Array Literal Syntax:
names := [3]string { "Kayak", "Lifejacket", "Paddle" }
The number of elements specified with the literal syntax can be less than the capacity of the array.
Any position in the array for which a value is not provided will be assigned the zero value for the array type.
Enumerating Arrays:
example:
names := [3]string { "Kayak", "Lifejacket", "Paddle" }
for index, value := range names {
fmt.Println("Index:", index, "Value:", value)
}
████████████████████████████████████████████████████████████████████████
62.Slices
The slice type in this example is []string
The best way to think of slices is as a variable-length array because they are useful when you don't know how
many values you need to store or when the number changes over time. One way to define a slice is to use the
built-in make function.
A slice is a segment of an array. Like arrays slices areindexable and have a length.
Unlike arrays this lengthis allowed to change.
if you want to create a slice you should use the built-inmake function
example:
names := make([]string, 3)
names[0] = "Kayak"
names[1] = "Lifejacket"
names[2] = "Paddle"
Literal Syntax:
example:
names := []string {"Kayak", "Lifejacket", "Paddle"}
████████████████████████████████████████████████████████████████████████
63.append
If you define a slice variable but don't initialize it, then the result is a slice that has a length of zero
and a capacity of zero, and this will cause an error when an element is appended to it.
One of the key advantages of slices is that they can be expanded to accommodate additional elements
Appending Elements to a Slice:
example:
names := []string {"Kayak", "Lifejacket", "Paddle"}
names = append(names, "Hat", "Gloves")
Appending Items to a Slice:
example:
names := []string {"Kayak", "Lifejacket", "Paddle"}
appendedNames := append(names, "Hat", "Gloves")
names[0] = "Canoe"
Allocating Additional Slice Capacity:
example:
names := make([]string, 3, 6)
names[0] = "Kayak"
names[1] = "Lifejacket"
names[2] = "Paddle"
Adding Elements to a Slice:
example:
names := make([]string, 3, 6)
names[0] = "Kayak"
names[1] = "Lifejacket"
names[2] = "Paddle"
appendedNames := append(names, "Hat", "Gloves")
names[0] = "Canoe"
fmt.Println("names:",names)
fmt.Println("appendedNames:", appendedNames)
Appending One Slice to Another:
example:
names := make([]string, 3, 6)
names[0] = "Kayak"
names[1] = "Lifejacket"
names[2] = "Paddle"
moreNames := []string { "Hat Gloves"}
appendedNames := append(names, moreNames...)
fmt.Println("appendedNames:", appendedNames)
Creating Slices from Existing Arrays:
example:
products := [4]string { "Kayak", "Lifejacket", "Paddle", "Hat"}
someNames := products[1:3]
allNames := products[:]
Specifying Capacity When Creating a Slice from an Array:
example:
products := [4]string { "Kayak", "Lifejacket", "Paddle", "Hat"}
someNames := products[1:3:3]
allNames := products[:]
someNames = append(someNames, "Gloves")
Creating Slices from Other Slices:
example:
products := [4]string { "Kayak", "Lifejacket", "Paddle", "Hat"}
allNames := products[1:]
someNames := allNames[1:3]
allNames = append(allNames, "Gloves")
allNames[1] = "Canoe"
Comparing Slices:
example:
Go restricts the use of the comparison operator so that slices can be compared only to the nil value.
p1 := []string { "Kayak", "Lifejacket", "Paddle", "Hat"}
p2 := p1
fmt.Println("Equal:", p1 == p2)
Output:
.\main.go:13:30: invalid operation: p1 == p2 (slice can only be compared to nil)
████████████████████████████████████████████████████████████████████████
64.copy
The copy function is used to copy elements between slices. This function can be used to ensure that slices
have separate arrays and to create slices that combine elements from different sources.
Duplicating a Slice:
The copy function accepts two arguments:
example:
products := [4]string { "Kayak", "Lifejacket", "Paddle", "Hat"}
allNames := products[1:]
someNames := make([]string, 2)
copy(someNames, allNames)
Output:
allNames [Lifejacket Paddle Hat]
someNames: [Lifejacket Paddle]
the Uninitialized Slice Pitfall:
the copy function doesn't resize the destination slice. A common
pitfall is to try to copy elements into a slice that has not been initialized
example:
products := [4]string { "Kayak", "Lifejacket", "Paddle", "Hat"}
allNames := products[1:]
var someNames []string
copy(someNames, allNames)
Output:
someNames: []
allNames [Lifejacket Paddle Hat]
Specifying Ranges When Copying Slices:
Fine-grained control over the elements that are copied can be achieved using ranges
example:
products := [4]string { "Kayak", "Lifejacket", "Paddle", "Hat"}
allNames := products[1:]
someNames := []string { "Boots", "Canoe"}
copy(someNames[1:], allNames[2:3])
Output:
allNames [Lifejacket Paddle Hat]
someNames: [Boots Hat]
Copying Slices with Different Sizes:
products := []string { "Kayak", "Lifejacket", "Paddle", "Hat"}
replacementProducts := []string { "Canoe", "Boots"}
copy(products, replacementProducts)
Output:
products: [Canoe Boots Paddle Hat]
Copying a Larger Source Slice
example:
products := []string { "Kayak", "Lifejacket", "Paddle", "Hat"}
replacementProducts := []string { "Canoe", "Boots"}
copy(products[0:1], replacementProducts)
fmt.Println("products:", products)
Output:
products: [Canoe Lifejacket Paddle Hat]
Deleting Slice Elements:
products := [4]string { "Kayak", "Lifejacket", "Paddle", "Hat"}
deleted := append(products[:2], products[3:]...)
fmt.Println("Deleted:", deleted)
Enumerating Slices:
example:
products := []string { "Kayak", "Lifejacket", "Paddle", "Hat"}
for index, value := range products[2:] {
fmt.Println("Index:", index, "Value:", value)
}
Output:
Index: 0 Value: Paddle
Index: 1 Value: Hat
████████████████████████████████████████████████████████████████████████
65.sort
There is no built-in support for sorting slices, but the standard library includes the sort package, which
defines functions for sorting different types of slice.
example:
products := []string { "Kayak", "Lifejacket", "Paddle", "Hat"}
sort.Strings(products)
for index, value := range products {
fmt.Println("Index:", index, "Value:", value)
}
Output:
Index: 0 Value: Hat
Index: 1 Value: Kayak
Index: 2 Value: Lifejacket
Index: 3 Value: Paddle
████████████████████████████████████████████████████████████████████████
66.DeepEqual
The DeepEqual function can be used to compare a wider range of data types than the
equality operator, including slices.
example:
package main
import (
"fmt"
"reflect"
)
func main() {
p1 := []string { "Kayak", "Lifejacket", "Paddle", "Hat"}
p2 := p1
fmt.Println("Equal:", reflect.DeepEqual(p1, p2))
}
Output:
Equal: true
████████████████████████████████████████████████████████████████████████
67.map
Maps are a built-in data structure that associates data values with keys.
Unlike arrays, where values are associated with sequential integer locations,
maps can use other data types as keys.
A map is an unordered collection of key-value pairs.
Also known as an associative array, a hash table or dictionary,
maps are used to look up a value by its associated key.
example:
products := make(map[string]float64, 10)
products["Kayak"] = 279
products["Lifejacket"] = 48.95
Map Literal Syntax:
products := map[string]float64 {
"Kayak" : 279,
"Lifejacket": 48.95,
}
Checking for Items in a Map:
example:
products := map[string]float64 {
"Kayak" : 279,
"Lifejacket": 48.95,
"Hat": 0,
}
value, ok := products["Hat"]
if (ok) {
fmt.Println("Stored value:", value)
} else {
fmt.Println("No stored value")
}
Removing Items from a Map:
example:
products := map[string]float64 {
"Kayak" : 279,
"Lifejacket": 48.95,
"Hat": 0,
}
delete(products, "Hat")
Enumerating the Contents of a Map:
example:
products := map[string]float64 {
"Kayak" : 279,
"Lifejacket": 48.95,
"Hat": 0,
}
for key, value := range products {
fmt.Println("Key:", key, "Value:", value)
}
Enumerating a Map in Order:
example:
import (
"fmt"
"sort"
)
func main() {
products := map[string]float64 {
"Kayak" : 279,
"Lifejacket": 48.95,
"Hat": 0,
}
keys := make([]string, 0, len(products))
for key, := range products {
keys = append(keys, key)
}
sort.Strings(keys)
for , key := range keys {
fmt.Println("Key:", key, "Value:", products[key])
}
}
████████████████████████████████████████████████████████████████████████
68.strconv
Understanding the Dual Nature of Strings
Indexing and Slicing a String:
var price string = "$48.95"
var currency byte = price[0]
var amountString string = price[1:]
amount, parseErr := strconv.ParseFloat(amountString, 64)
fmt.Println("Currency:", currency)
if (parseErr == nil) {
fmt.Println("Amount:", amount)
} else {
fmt.Println("Parse Error:", parseErr)
}
Output:
Currency: 36
Amount: 48.95
Converting the Result:
var price string = "$48.95"
var currency string = string(price[0])
var amountString string = price[1:]
amount, parseErr := strconv.ParseFloat(amountString, 64)
fmt.Println("Currency:", currency)
if (parseErr == nil) {
fmt.Println("Amount:", amount)
} else {
fmt.Println("Parse Error:", parseErr)
}
Output:
Currency: $
Amount: 48.95
Changing the Currency Symbol:
example:
var price string = "€48.95"
var currency string = string(price[0])
var amountString string = price[1:]
amount, parseErr := strconv.ParseFloat(amountString, 64)
fmt.Println("Currency:", currency)
if (parseErr == nil) {
fmt.Println("Amount:", amount)
} else {
fmt.Println("Parse Error:", parseErr)
}
Output:
Currency: â
Parse Error: strconv.ParseFloat: parsing "\x82\xac48.95": invalid syntax
Obtaining the Length:
fmt.Println("Length:", len(price))
Output:
Length: 8
████████████████████████████████████████████████████████████████████████
69.rune
Converting a String to Runes
The rune type represents a Unicode code point,
which is essentially a single character.
To avoid slicing strings in the middle of characters,
an explicit conversion to a rune slice can be performed
Unicode is incredibly complex, as you would expect
from any standard that aims to describe multiple
writing systems evolved over thousands of years.
Converting to Runes:
var price []rune = []rune("€48.95")
var amountString string = string(price[1:])
Enumerating Strings
example:
var price = "€48.95"
for index, char := range price {
fmt.Println(index, char, string(char))
}
Output:
0 8364 €
3 52 4
4 56 8
5 46 .
6 57 9
7 53 5
Enumerating the Bytes
example:
var price = "€48.95"
for index, char := range []byte(price) {
fmt.Println(index, char)
}
Output:
0 226
1 130
2 172
3 52
4 56
5 46
6 57
7 53
████████████████████████████████████████████████████████████████████████
70.func
Functions are groups of code statements that are executed only when the function is
invoked during the flow of execution.
████████████████████████████████████████████████████████████████████████
71.Function Parameters
Parameters allow a function to receive data values when it is called,
allowing its behavior to be altered.
Values for parameters are supplied as arguments when invoking the function, meaning that different
values can be provided each time the function is called. Arguments are provided between the parentheses
that follow the function name, separated by commas and in the same order in which the parameters have
been defined
example:
func printPrice(product string, price float64, taxRate float64) {
taxAmount := price * taxRate
fmt.Println(product, "price:", price, "Tax:", taxAmount)
}
████████████████████████████████████████████████████████████████████████
72.Defining Variadic Parameters
example:
func printSuppliers(product string, suppliers []string ) {
for , supplier := range suppliers {
fmt.Println("Product:", product, "Supplier:", supplier)
}
}
example:
func printSuppliers(product string, suppliers ...string ) {
for , supplier := range suppliers {
fmt.Println("Product:", product, "Supplier:", supplier)
}
}
████████████████████████████████████████████████████████████████████████
73.Dealing with No Arguments for a Variadic Parameter
example:
func printSuppliers(product string, suppliers ...string ) {
for , supplier := range suppliers {
fmt.Println("Product:", product, "Supplier:", supplier)
}
}
func main() {
printSuppliers("Kayak", "Acme Kayaks", "Bob's Boats", "Crazy Canoes")
printSuppliers("Lifejacket", "Sail Safe Co")
printSuppliers("Soccer Ball")
}
Output:
Product: Kayak Supplier: Acme Kayaks
Product: Kayak Supplier: Bob's Boats
Product: Kayak Supplier: Crazy Canoes
Product: Lifejacket Supplier: Sail Safe Co
████████████████████████████████████████████████████████████████████████
74.return Function Results
example:
func calcTax(price float64) float64 {
return price + (price * 0.2)
}
████████████████████████████████████████████████████████████████████████
75.Returning Multiple Function Results
example:
func swapValues(first, second int) (int, int) {
return second, first
}
func main() {
val1, val2 := 10, 20
fmt.Println("Before calling function", val1, val2)
val1, val2 = swapValues(val1, val2)
fmt.Println("After calling function", val1, val2)
}
████████████████████████████████████████████████████████████████████████
76.Using Named Results
example:
func calcTax(price float64) (float64, bool) {
if (price > 100) {
return price * 0.2, true
}
return 0, false
}
func calcTotalPrice(products map[string]float64,
minSpend float64) (total, tax float64) {
total = minSpend
for , price := range products {
if taxAmount, due := calcTax(price); due {
total += taxAmount;
tax += taxAmount
} else {
total += price
}
}
return
}
████████████████████████████████████████████████████████████████████████
77.defer
The defer keyword is used to schedule a function call that will be performed immediately before the current
function returns
The defer keyword lets you group the statements that create, use, and
release the resource together.
The defer keyword can be used with any function call
a single function can use the defer keyword multiple times.
Immediately before the function returns, Go will perform the
calls scheduled with the defer keyword in the order in which they were defined.
████████████████████████████████████████████████████████████████████████
78.Function Types
Functions in Go have a data type, which describes the combination of parameters the
function consumes and the results the function produces. This type can be specified
explicitly or inferred from a function defined using a literal syntax.
Function types are defined using the func keyword, followed by a signature that
describes the parameters and results. No function body is specified.
Go does not support arrow functions, where functions are expressed more concisely using the =>
operator, without the func keyword and a code block surrounded by braces. In Go, functions must always be
defined with the keyword and a body.
████████████████████████████████████████████████████████████████████████
79.Function Comparisons and the Zero Type
example:
func calcWithTax(price float64) float64 {
return price + (price * 0.2)
}
func calcWithoutTax(price float64) float64 {
return price
}
func main() {
products := map[string]float64 {
"Kayak" : 275,
"Lifejacket": 48.95,
}
for product, price := range products {
var calcFunc func(float64) float64
fmt.Println("Function assigned:", calcFunc == nil)
if (price > 100) {
calcFunc = calcWithTax
} else {
calcFunc = calcWithoutTax
}
fmt.Println("Function assigned:", calcFunc == nil)
totalPrice := calcFunc(price)
fmt.Println("Product:", product, "Price:", totalPrice)
}
}
████████████████████████████████████████████████████████████████████████
80.Functions as Arguments
example:
func calcWithTax(price float64) float64 {
return price + (price * 0.2)
}
func calcWithoutTax(price float64) float64 {
return price
}
func printPrice(product string, price float64, calculator func(float64) float64 ) {
fmt.Println("Product:", product, "Price:", calculator(price))
}
func main() {
products := map[string]float64 {
"Kayak" : 275,
"Lifejacket": 48.95,
}
for product, price := range products {
if (price > 100) {
printPrice(product, price, calcWithTax)
} else {
printPrice(product, price, calcWithoutTax)
}
}
}
████████████████████████████████████████████████████████████████████████
81.Functions as Results
example:
func calcWithTax(price float64) float64 {
return price + (price * 0.2)
}
func calcWithoutTax(price float64) float64 {
return price
}
func printPrice(product string, price float64, calculator func(float64) float64 ) {
fmt.Println("Product:", product, "Price:", calculator(price))
}
func selectCalculator(price float64) func(float64) float64 {
if (price > 100) {
return calcWithTax
}
return calcWithoutTax
}
func main() {
products := map[string]float64 {
"Kayak" : 275,
"Lifejacket": 48.95,
}
for product, price := range products {
printPrice(product, price, selectCalculator(price))
}
}
████████████████████████████████████████████████████████████████████████
82.Function Type Aliases
Go supports type aliases, which can be used to assign a name to
a function signature so that the parameter and result types are not specified every time the function type is
used.
example:
type calcFunc func(float64) float64
func calcWithTax(price float64) float64 {
return price + (price * 0.2)
}
func calcWithoutTax(price float64) float64 {
return price
}
func printPrice(product string, price float64, calculator calcFunc) {
fmt.Println("Product:", product, "Price:", calculator(price))
}
func selectCalculator(price float64) calcFunc {
if (price > 100) {
return calcWithTax
}
return calcWithoutTax
}
func main() {
products := map[string]float64 {
"Kayak" : 275,
"Lifejacket": 48.95,
}
for product, price := range products {
printPrice(product, price, selectCalculator(price))
}
}
████████████████████████████████████████████████████████████████████████
83.the Literal Function Syntax
example:
func selectCalculator(price float64) calcFunc {
if (price > 100) {
var withTax calcFunc = func (price float64) float64 {
return price + (price * 0.2)
}
return withTax
}
████████████████████████████████████████████████████████████████████████
84.Function Variable Scope
example:
func selectCalculator(price float64) calcFunc {
if (price > 100) {
var withTax calcFunc = func (price float64) float64 {
return price + (price * 0.2)
}
return withTax
} else if (price < 10) {
return withTax
}
withoutTax := func (price float64) float64 {
return price
}
return withoutTax
}
████████████████████████████████████████████████████████████████████████
85.Functions Values Directly
example:
func selectCalculator(price float64) calcFunc {
if (price > 100) {
return func (price float64) float64 {
return price + (price * 0.2)
}
}
return func (price float64) float64 {
return price
}
}
████████████████████████████████████████████████████████████████████████
86.Literal Function Argument
example:
func main() {
products := map[string]float64 {
"Kayak" : 275,
"Lifejacket": 48.95,
}
for product, price := range products {
printPrice(product, price, func (price float64) float64 {
return price + (price * 0.2)
})
}
}
████████████████████████████████████████████████████████████████████████
87.Function Closure
Functions defined using the literal syntax can reference variables from the surrounding code, a feature
known as closure.
example:
type calcFunc func(float64) float64
func printPrice(product string, price float64, calculator calcFunc) {
fmt.Println("Product:", product, "Price:", calculator(price))
}
func main() {
watersportsProducts := map[string]float64 {
"Kayak" : 275,
"Lifejacket": 48.95,
}
soccerProducts := map[string] float64 {
"Soccer Ball": 19.50,
"Stadium": 79500,
}
calc := func(price float64) float64 {
if (price > 100) {
return price + (price * 0.2)
}
return price;
}
for product, price := range watersportsProducts {
printPrice(product, price, calc)
}
calc = func(price float64) float64 {
if (price > 50) {
return price + (price * 0.1)
}
return price
}
for product, price := range soccerProducts {
printPrice(product, price, calc)
}
}
████████████████████████████████████████████████████████████████████████
88.struct
What are they?
Structs are data types, comprised of fields.
Why are they useful?
Structs allow custom data types to be defined.
How are they used?
The type and struct keywords are used to define a type,
allowing field names and types to be specified.
A struct can mix regular and embedded field types.
example:
func main() {
type Product struct {
name, category string
price float64
}
kayak := Product {
name: "Kayak",
category: "Watersports",
price: 275,
}
fmt.Println(kayak.name, kayak.category, kayak.price)
kayak.price = 300
fmt.Println("Changed price:", kayak.price)
}
Go doesn't differentiate between structs and classes, in the way that other languages do. All custom
data types are defined as structs, and the decision to pass them by reference or by value is made
depending on whether a pointer is used.
Go doesn't allow structs to be used with the const keyword, and the compiler will report an error if
you try to define a constant struct.
████████████████████████████████████████████████████████████████████████
89.struct tag
The struct type can be defined with tags, which provide additional information about how a field should
be processed. Struct tags are just strings that are interpreted by the code that processes struct values,
using the features provided by the reflect package.
████████████████████████████████████████████████████████████████████████
90.struct values
Values do not have to be provided for all fields when creating a struct value
example:
func main() {
type Product struct {
name, category string
price float64
}
kayak := Product {
name: "Kayak",
category: "Watersports",
}
}
████████████████████████████████████████████████████████████████████████
91.new
the new function to create struct values
var lifejacket = new(Product)
The result is a pointer to a struct value whose fields are
initialized with their type's zero value.
This is equivalent to this statement:
var lifejacket = &Product{}
These approaches are interchangeable, and choosing between them is a matter of preference.
████████████████████████████████████████████████████████████████████████
92.Field Positions to Create Struct Values
example:
func main() {
type Product struct {
name, category string
price float64
}
var kayak = Product { "Kayak", "Watersports", 275.00 }
fmt.Println("Name:", kayak.name)
fmt.Println("Category:", kayak.category)
fmt.Println("Price:", kayak.price)
}
████████████████████████████████████████████████████████████████████████
93.Defining Embedded Fields
If a field is defined without a name,
it is known as an embedded field, and it is accessed using the name of its type.
example:
func main() {
type Product struct {
name, category string
price float64
}
type StockLevel struct {
Product
count int
}
stockItem := StockLevel {
Product: Product { "Kayak", "Watersports", 275.00 },
count: 100,
}
fmt.Println("Name:", stockItem.Product.name)
fmt.Println("Count:", stockItem.count)
}
████████████████████████████████████████████████████████████████████████
94.Defining an Additional Field
example:
func main() {
type Product struct {
name, category string
price float64
}
type StockLevel struct {
Product
Alternate Product
count int
}
stockItem := StockLevel {
Product: Product { "Kayak", "Watersports", 275.00 },
Alternate: Product{"Lifejacket", "Watersports", 48.95 },
count: 100,
}
fmt.Println("Name:", stockItem.Product.name)
fmt.Println("Alt Name:", stockItem.Alternate.name)
}
████████████████████████████████████████████████████████████████████████
95.Comparing Struct Values
example:
func main() {
type Product struct {
name, category string
price float64
}
p1 := Product { name: "Kayak", category: "Watersports", price: 275.00 }
p2 := Product { name: "Kayak", category: "Watersports", price: 275.00 }
p3 := Product { name: "Kayak", category: "Boats", price: 275.00 }
fmt.Println("p1 == p2:", p1 == p2)
fmt.Println("p1 == p3:", p1 == p3)
}
████████████████████████████████████████████████████████████████████████
96.Anonymous Struct Types
Anonymous struct types are defined without using a name
example:
package main
import "fmt"
func writeName(val struct {
name, category string
price float64}) {
fmt.Println("Name:", val.name)
}
func main() {
type Product struct {
name, category string
price float64
//otherNames []string
}
type Item struct {
name string
category string
price float64
}
prod := Product { name: "Kayak", category: "Watersports", price: 275.00 }
item := Item { name: "Stadium", category: "Soccer", price: 75000 }
writeName(prod)
writeName(item)
}
████████████████████████████████████████████████████████████████████████
97.Creating Arrays, Slices, and Maps Containing Struct Values
Omitting the Struct Type
example:
package main
import "fmt"
func main() {
type Product struct {
name, category string
price float64
//otherNames []string
}
type StockLevel struct {
Product
Alternate Product
count int
}
array := [1]StockLevel {
{
Product: Product { "Kayak", "Watersports", 275.00 },
Alternate: Product{"Lifejacket", "Watersports", 48.95 },
count: 100,
},
}
fmt.Println("Array:", array[0].Product.name)
slice := []StockLevel {
{
Product: Product { "Kayak", "Watersports", 275.00 },
Alternate: Product{"Lifejacket", "Watersports", 48.95 },
count: 100,
},
}
fmt.Println("Slice:", slice[0].Product.name)
kvp := map[string]StockLevel {
"kayak": {
Product: Product { "Kayak", "Watersports", 275.00 },
Alternate: Product{"Lifejacket", "Watersports", 48.95 },
count: 100,
},
}
fmt.Println("Map:", kvp["kayak"].Product.name)
}
████████████████████████████████████████████████████████████████████████
98.Structs and Pointers
Assigning a struct to a new variable or using a struct as a function parameter
creates a new value that copies the field values.
████████████████████████████████████████████████████████████████████████
99.Copying a Struct Value
example:
package main
import "fmt"
func main() {
type Product struct {
name, category string
price float64
}
p1 := Product {
name: "Kayak",
category: "Watersports",
price: 275,
}
p2 := p1
p1.name = "Original Kayak"
fmt.Println("P1:", p1.name)
fmt.Println("P2:", p2.name)
}
████████████████████████████████████████████████████████████████████████
100.Using a Pointer to a Struct
example:
package main
import "fmt"
func main() {
type Product struct {
name, category string
price float64
}
p1 := Product {
name: "Kayak",
category: "Watersports",
price: 275,
}
p2 := &p1
p1.name = "Original Kayak"
fmt.Println("P1:", p1.name)
fmt.Println("P2:", (*p2).name)
}
████████████████████████████████████████████████████████████████████████
101.the Struct Pointer Convenience Syntax
example:
package main
import "fmt"
type Product struct {
name, category string
price float64
}
func calcTax(product *Product) {
if ((*product).price > 100) {
(*product).price += (*product).price * 0.2
}
}
func main() {
kayak := Product {
name: "Kayak",
category: "Watersports",
price: 275,
}
calcTax(&kayak)
fmt.Println("Name:", kayak.name, "Category:",
kayak.category, "Price", kayak.price)
}
████████████████████████████████████████████████████████████████████████
102.Struct Constructor Functions
A constructor function is responsible for creating struct values using values received through parameters
Constructor functions are used to create struct values consistently. Constructor functions are usually
named new or New followed by the struct type so that the constructor function for creating Product values
is named newProduct.
example:
package main
import "fmt"
type Product struct {
name, category string
price float64
}
func newProduct(name, category string, price float64) *Product {
return &Product{name, category, price}
}
func main() {
products := [2]*Product {
newProduct("Kayak", "Watersports", 275),
newProduct("Hat", "Skiing", 42.50),
}
for , p := range products {
fmt.Println("Name:", p.name, "Category:", p.category, "Price", p.price)
}
}
████████████████████████████████████████████████████████████████████████
103.Modifying a Constructor
The benefit of using constructor functions is consistency,
ensuring that changes to the construction
process are reflected in all the struct values created by the function.
example:
func newProduct(name, category string, price float64) *Product {
return &Product{name, category, price - 10}
}
████████████████████████████████████████████████████████████████████████
104.Pointer Types for Struct Fields
example:
package main
import "fmt"
type Product struct {
name, category string
price float64
*Supplier
}
type Supplier struct {
name, city string
}
func newProduct(name, category string, price float64, supplier *Supplier) *Product {
return &Product{name, category, price -10, supplier}
}
func main() {
acme := &Supplier { "Acme Co", "New York"}
products := [2]*Product {
newProduct("Kayak", "Watersports", 275, acme),
newProduct("Hat", "Skiing", 42.50, acme),
}
for , p := range products {
fmt.Println("Name:", p.name, "Supplier:",
p.Supplier.name, p.Supplier.city)
}
}
████████████████████████████████████████████████████████████████████████
105.Pointer Field Copying
Care must be taken when copying structs to consider the effect on pointer fields
example:
package main
import "fmt"
type Product struct {
name, category string
price float64
*Supplier
}
type Supplier struct {
name, city string
}
func newProduct(name, category string, price float64, supplier *Supplier) *Product {
return &Product{name, category, price -10, supplier}
}
func main() {
acme := &Supplier { "Acme Co", "New York"}
p1 := newProduct("Kayak", "Watersports", 275, acme)
p2 := *p1
p1.name = "Original Kayak"
p1.Supplier.name = "BoatCo"
for , p := range []Product { *p1, p2 } {
fmt.Println("Name:", p.name, "Supplier:",
p.Supplier.name, p.Supplier.city)
}
}
████████████████████████████████████████████████████████████████████████
106.Method
What are they?
Methods are functions that are invoked on a struct and have access to all of the
fields defined by the value's type. Interfaces define sets of methods, which can be
implemented by struct types.
Why are they useful?
These features allow types to be mixed and used through their common
characteristics.
How are they used?
Methods are defined using the func keyword, but with the addition of a receiver.
Interfaces are defined using the type and interface keywords.
Are there any pitfalls or limitations?
Careful use of pointers is important when creating methods, and care must be taken
when using interfaces to avoid problems with the underlying dynamic types.
████████████████████████████████████████████████████████████████████████
107.Defining and Using Method
example:
package main
import "fmt"
type Product struct {
name, category string
price float64
}
func newProduct(name, category string, price float64) *Product {
return &Product{ name, category, price }
}
func (product *Product) printDetails() {
fmt.Println("Name:", product.name, "Category:", product.category,
"Price", product.price)
}
func main() {
products := []*Product {
newProduct("Kayak", "Watersports", 275),
newProduct("Lifejacket", "Watersports", 48.95),
newProduct("Soccer Ball", "Soccer", 19.50),
}
for , p := range products {
p.printDetails()
}
}
████████████████████████████████████████████████████████████████████████
108.Defining Method Parameters and Results
example:
package main
import "fmt"
type Product struct {
name, category string
price float64
}
func newProduct(name, category string, price float64) *Product {
return &Product{ name, category, price }
}
func (product *Product) printDetails() {
fmt.Println("Name:", product.name, "Category:", product.category,
"Price", product.price)
}
func main() {
products := []*Product {
newProduct("Kayak", "Watersports", 275),
newProduct("Lifejacket", "Watersports", 48.95),
newProduct("Soccer Ball", "Soccer", 19.50),
}
for , p := range products {
p.calcTax(0.2, 100) //<-------------------------------here
}
}
████████████████████████████████████████████████████████████████████████
109.Defining and Using Interfaces
One interface can enclose another, with the effect that types must implement all the methods defined
by the enclosing and enclosed interfaces. Interfaces are simpler than structs, and there are no fields or
method to promote. The result of composing interfaces is a union of the method defined by the enclosing
and enclosed types.
example:
package main
import "fmt"
type Expense interface {
getName() string
getCost(annual bool) float64
}
func main() {
expenses := []Expense {
Product { "Kayak", "Watersports", 275 },
Service {"Boat Cover", 12, 89.50 },
}
for , expense := range expenses {
fmt.Println("Expense:", expense.getName(), "Cost:", expense.getCost(true))
}
}
████████████████████████████████████████████████████████████████████████
110.an Interface in a Function
example:
package main
import "fmt"
type Expense interface {
getName() string
getCost(annual bool) float64
}
func calcTotal(expenses []Expense) (total float64) {
for , item := range expenses {
total += item.getCost(true)
}
return
}
func main() {
expenses := []Expense {
Product { "Kayak", "Watersports", 275 },
Service {"Boat Cover", 12, 89.50 },
}
for , expense := range expenses {
fmt.Println("Expense:", expense.getName(), "Cost:", expense.getCost(true))
}
fmt.Println("Total:", calcTotal(expenses))
}
████████████████████████████████████████████████████████████████████████
111.an Interface for Struct Fields
example:
package main
import "fmt"
type Expense interface {
getName() string
getCost(annual bool) float64
}
func calcTotal(expenses []Expense) (total float64) {
for , item := range expenses {
total += item.getCost(true)
}
return
}
type Account struct {
accountNumber int
expenses []Expense
}
func main() {
account := Account {
accountNumber: 12345,
expenses: []Expense {
Product { "Kayak", "Watersports", 275 },
Service {"Boat Cover", 12, 89.50 },
},
}
for , expense := range account.expenses {
fmt.Println("Expense:", expense.getName(), "Cost:", expense.getCost(true))
}
fmt.Println("Total:", calcTotal(account.expenses))
}
████████████████████████████████████████████████████████████████████████
112.Comparing Interface Values
Care must be taken when comparing interface values, and inevitably, some knowledge of the dynamic
types is required.
The first two Expense values are not equal.
That's because the dynamic type for these values is a pointer
type, and pointers are equal only if they point to the same memory location
example:
package main
import "fmt"
type Expense interface {
getName() string
getCost(annual bool) float64
}
func main() {
var e1 Expense = &Product { name: "Kayak" }
var e2 Expense = &Product { name: "Kayak" }
var e3 Expense = Service { description: "Boat Cover" }
var e4 Expense = Service { description: "Boat Cover" }
fmt.Println("e1 == e2", e1 == e2)
fmt.Println("e3 == e4", e3 == e4)
}
Output:
e1 == e2 false
e3 == e4 true
████████████████████████████████████████████████████████████████████████
113.Empty Interface
Go allows the user of the empty interface—which means an interface that defines no methods—to represent
any type, which can be a useful way to group disparate types that share no common features
The empty interface represents all types, including the built-in types and any structs and interfaces
that have been defined.
████████████████████████████████████████████████████████████████████████
114.Empty Interface for Function Parameters
The empty interface can be used as the type for a function parameter, allowing a function to be called with
any value
example:
package main
import "fmt"
type Expense interface {
getName() string
getCost(annual bool) float64
}
type Person struct {
name, city string
}
func processItem(item interface{}) {
switch value := item.(type) {
case Product:
fmt.Println("Product:", value.name, "Price:", value.price)
case *Product:
fmt.Println("Product Pointer:", value.name, "Price:", value.price)
case Service:
fmt.Println("Service:", value.description, "Price:",
value.monthlyFee * float64(value.durationMonths))
case Person:
fmt.Println("Person:", value.name, "City:", value.city)
case *Person:
fmt.Println("Person Pointer:", value.name, "City:", value.city)
case string, bool, int:
fmt.Println("Built-in type:", value)
default:
fmt.Println("Default:", value)
}
}
func main() {
var expense Expense = &Product { "Kayak", "Watersports", 275 }
data := []interface{} {
expense,
Product { "Lifejacket", "Watersports", 48.95 },
Service {"Boat Cover", 12, 89.50, []string{} },
Person { "Alice", "London"},
&Person { "Bob", "New York"},
"This is a string",
100,
true,
}
for , item := range data {
processItem(item)
}
}
████████████████████████████████████████████████████████████████████████
115.Package
Packages are the Go feature that allows projects to be structured so that related functionality can be grouped
together, without the need to put all the code into a single file or folder.
What are they?
Packages allow projects to be structured so that related features can be developed together.
Why are they useful?
Packages are how Go implements access controls so that the implementation of
a feature can be hidden from the code that consumes it.
How are they used?
Packages are defined by creating code files in folders and using the package
keyword to denote which package they belong to.
████████████████████████████████████████████████████████████████████████
116.the Module File
This name is important because it is used to import features from other packages created within
the same project and third-party packages
The go statement specifies the version of Go that is used.
████████████████████████████████████████████████████████████████████████
117.Package Access Control
Go has an unusual approach to access control. Instead of relying on dedicated keywords, like public
and private, Go examines the first letter of the names given to the features in a code file, such as types,
functions, and methods. If the first letter is lowercase, then the feature can be used only within the package
that defines it. Features are exported for use outside of the package by giving them an uppercase first letter.
The access control rules do not apply to individual function or method parameters, which means that
the NewProduct function has to have an uppercase first character to be exported, but the parameter names
can be lowercase.
████████████████████████████████████████████████████████████████████████
118.Adding Code Files to Packages
Packages can contain multiple code files, and to simplify development, access control rules and package
prefixes do not apply when accessing features defined in the same package.
████████████████████████████████████████████████████████████████████████
119.func init()
Each code file can contain an initialization function that is executed only when all packages have been
loaded and all other initialization—such as defining constants and variables—has been done. The most
common use for initialization functions is to perform calculations that are difficult to perform or that require
duplication to perform
The initialization function is called init, and it is defined without parameters and a result. The init
function is called automatically and provides an opportunity to prepare the package for use.
The init function is not a regular Go function and cannot be invoked directly. And, unlike regular
functions, a single file can define multiple init functions, all of which will be executed.
AVOIDING THE MULTIPLE INITIALIZATION FUNCTION PITFALL
Each code file can have its own initialization function. When using the standard Go compiler, the
initialization functions are executed based on the alphabetic order of the filenames, so the function in
the a.go file will be executed before the function in the b.go file, and so on.
But this order is not part of the Go language specification and should not be relied on. Your initialization
functions should be self-contained and not rely on other init functions having been invoked previously.
████████████████████████████████████████████████████████████████████████
120.Creating Nested Packages
Packages can be defined within other packages, making it easy to break up complex features into as many
units as possible.
The package statement is used just as with any other package, without the need to include the name
of the parent or enclosing package. And dependency on custom packages must include the full package
path.
example:
Create the packages/store/cart folder
and add to it a file named cart.go with the contents:
#1
contents:
package cart
import "packages/store"
type Cart struct {
CustomerName string
Products []store.Product
}
func (cart *Cart) GetTotal() (total float64) {
for , p := range cart.Products {
total += p.Price()
}
return
}
The features defined by the nested package are accessed using the package name, just like any other
package. When importing a nested package, the package path starts with the module name and lists the
sequence of packages.
example:
package main
import (
"fmt"
"packages/store"
. "packages/fmt"
"packages/store/cart"
)
func main() {
product := store.NewProduct("Kayak", "Watersports", 279)
cart := cart.Cart {
CustomerName: "Alice",
Products: []store.Product{ *product },
}
fmt.Println("Name:", cart.CustomerName)
fmt.Println("Total:", ToCurrency(cart.GetTotal()))
}
████████████████████████████████████████████████████████████████████████
121.Initialization Function
example:
func init() {
for category, price := range categoryMaxPrices {
categoryMaxPrices[category] = price + (price * defaultTaxRate)
}
}
████████████████████████████████████████████████████████████████████████
122.Importing a Package Only for Initialization Effects
Go prevents packages from being imported but not used,
which can be a problem if you rely on the effect of
an initialization function but don't need to use any of the features the package exports.
If I need the effect of the initialization function,
but I don't need to use the GetData function the package exports.
example:
package main
import (
"fmt"
"packages/store"
. "packages/fmt"
"packages/store/cart"
"packages/data"
)
████████████████████████████████████████████████████████████████████████
123.Finding Go Packages
#1 https://pkg.go.dev
#2 https://github.com/golang/go/wiki/Projects
Many Go modules are written by individual developers
to solve a problem and then published for anyone else to use.
This creates a rich module ecosystem,
but it does mean that maintenance and support can be inconsistent.
████████████████████████████████████████████████████████████████████████
124.indirect
The indirect comment at the end of the statements is added automatically because
the packages are not used by the code in the project. A file named go.sum is created when the module is
obtained and contains checksums used to validate the packages.
example:
module packages
go 1.17
require (
github.com/fatih/color v1.10.0 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae // indirect
)
Note:
You can also use the go.mod file to create dependencies on projects you have created locally
The first time you run the go get command, you will see a list of the modules that are
downloaded, which illustrated that modules have their own dependencies and that these are resolved
automatically:
go: downloading github.com/fatih/color v1.10.0
go: downloading github.com/mattn/go-isatty v0.0.12
go: downloading github.com/mattn/go-colorable v0.1.8
go: downloading golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae
████████████████████████████████████████████████████████████████████████
125.Managing External Packages
Removing a Package
To update the go.mod file to reflect the change, run the command
████████████████████████████████████████████████████████████████████████
126.go mod tidyPutting Type and Interface Composition in Context
What is it?
Composition is the process by which new types are created by combining
structs and interfaces.
Why is it useful?
Composition allows types to be defined based on existing types.
How is it used?
Existing types are embedded in new types.
Are there any pitfalls or limitations?
Composition doesn't work in the same way as inheritance, and care must be
taken to achieve the desired outcome.
Are there any alternatives?
Composition is optional, and you can create entirely independent types.
Go doesn't support classes or inheritance and focuses on composition instead. But, despite the
differences, composition can be used to create hierarchies of types, just in a different way.
Steps:
1-Defining the Base Type -> struct - method
2-Defining a Constructor -> for create struct
example:
// struct:
type Product struct {
Name, Category string
price float64
}
// method:
func (p *Product) Price(taxRate float64) float64 {
return p.price + (p.price * taxRate)
}
// Constructor:
func NewProduct(name, category string, price float64) *Product {
return &Product{ name, category, price }
}
████████████████████████████████████████████████████████████████████████
127.Creating Struct Values in Packages
example:
package main
import (
"fmt"
"composition/store"
)
func main() {
// use Constructor:
kayak := store.NewProduct("Kayak", "Watersports", 275)
// use the literal syntax:
lifejacket := &store.Product{ Name: "Lifejacket", Category: "Watersports"}
for , p := range []*store.Product { kayak, lifejacket} {
fmt.Println("Name:", p.Name, "Category:", p.Category, "Price:", p.Price(0.2))
}
}
████████████████████████████████████████████████████████████████████████
128.Steps of composition
1-Defining the Base Type -> struct - method
2-Defining a Constructor -> for create struct
3-Composing Types
4-Using the Boat Struct in the main.go
A struct can mix regular and embedded field types, but the embedded fields are an important part of
the composition feature
Go gives special treatment to struct types that have fields whose type is another struct type, in the way
that the Boat type has a *Product field in the example project. You can see this special treatment in the
statement in the for loop, which is responsible for writing out details of each Boat.
Go allows the fields of the nested type to be accessed in two ways. The first is the conventional approach
of navigating the hierarchy of types to reach the value that is required. The *Product field is embedded, which
means that its name its its type.
The Boat type doesn't define a Name field, but it can be treated as though it did because of the direct
access feature. This is known as field promotion, and Go essentially flattens the types so that the Boat
type behaves as though it defines the fields that are provided by the nested Product type
example:
product.go:
// struct:
type Product struct {
Name, Category string
price float64
}
// method:
func (p *Product) Price(taxRate float64) float64 {
return p.price + (p.price * taxRate)
}
// Constructor:
func NewProduct(name, category string, price float64) *Product {
return &Product{ name, category, price }
}
boat.go:
package store
The Boat struct type defines an embedded *Product field
type Boat struct {
*Product -------------------> Embedded Type
Capacity int -----------------> Reguler Fields
Motorized bool
}
func NewBoat(name string, price float64, capacity int, motorized bool) *Boat {
return &Boat {
NewProduct(name, "Watersports", price), capacity, motorized,
}
}
main.go:
package main
import (
"fmt"
"composition/store"
)
func main() {
boats := []*store.Boat {
store.NewBoat("Kayak", 275, 1, false),
store.NewBoat("Canoe", 400, 3, false),
store.NewBoat("Tender", 650.25, 2, true),
}
for , b := range boats {
fmt.Println("Conventional:", b.Product.Name, "Direct:", b.Name)
}
}
Output:
Conventional: Kayak Direct: Kayak
Conventional: Canoe Direct: Canoe
Conventional: Tender Direct: Tender
If the field type is a value, such as Product, then any methods defined with Product or *Product
receivers will be promoted. If the field type is a pointer, such as *Product, then only methods with *Product
receivers will be prompted.
There is no Price method defined for the *Boat type, but Go promotes the method defined with a
*Product receiver.
Calling a Method in the main.go:
package main
import (
"fmt"
"composition/store"
)
func main() {
boats := []*store.Boat {
store.NewBoat("Kayak", 275, 1, false),
store.NewBoat("Canoe", 400, 3, false),
store.NewBoat("Tender", 650.25, 2, true),
}
for , b := range boats {
fmt.Println("Boat:", b.Name, "Price:", b.Price(0.2))
}
}
Output:
Boat: Kayak Price: 330
Boat: Canoe Price: 480
Boat: Tender Price: 780.3
████████████████████████████████████████████████████████████████████████
129.Creating a Chain of Nested Types
The composition feature can be used to create complex chains of nested types,
whose fields and methods are promoted to the top-level enclosing type.
Go performs promotion so that the fields defined by all three types in the chain can be
accessed directly.
Go promotes fields from the nested Boat and
Product types so they can be accessed through the top-
level RentalBoat type, which allows the Name field to be read.
Methods are also promoted to the top-level type,
which is why I can use the Price method, even though it is defined on the *Product type,
which is at the end of the chain.
example:
rentalboats.go:
package store
type RentalBoat struct {
*Boat
IncludeCrew bool
}
func NewRentalBoat(name string, price float64, capacity int,
motorized, crewed bool) *RentalBoat {
return &RentalBoat{NewBoat(name, price, capacity, motorized), crewed}
}
Accessing Nested Fields Directly in the main.go:
package main
import (
"fmt"
"composition/store"
)
func main() {
rentals := []*store.RentalBoat {
store.NewRentalBoat("Rubber Ring", 10, 1, false, false),
store.NewRentalBoat("Yacht", 50000, 5, true, true),
store.NewRentalBoat("Super Yacht", 100000, 15, true, true),
}
for , r := range rentals {
fmt.Println("Rental Boat:", r.Name, "Rental Price:", r.Price(0.2))
}
}
Output:
Rental Boat: Rubber Ring Rental Price: 12
Rental Boat: Yacht Rental Price: 60000
Rental Boat: Super Yacht Rental Price: 120000
████████████████████████████████████████████████████████████████████████
130.Multiple Nested Types in the Same Struct
example:
rentalboats.go:
package store
type Crew struct {
Captain, FirstOfficer string
}
type RentalBoat struct {
*Boat
IncludeCrew bool
*Crew
}
func NewRentalBoat(name string, price float64, capacity int,
motorized, crewed bool, captain, firstOfficer string) *RentalBoat {
return &RentalBoat{NewBoat(name, price, capacity, motorized), crewed,
&Crew{captain, firstOfficer}}
}
Using Promoted Fields in the main.go:
package main
import (
"fmt"
"composition/store"
)
func main() {
rentals := []*store.RentalBoat {
store.NewRentalBoat("Rubber Ring", 10, 1, false, false, "N/A", "N/A"),
store.NewRentalBoat("Yacht", 50000, 5, true, true, "Bob", "Alice"),
store.NewRentalBoat("Super Yacht", 100000, 15, true, true,
"Dora", "Charlie"),
}
for , r := range rentals {
fmt.Println("Rental Boat:", r.Name, "Rental Price:", r.Price(0.2),
"Captain:", r.Captain)
}
}
Output:
Rental Boat: Rubber Ring Rental Price: 12 Captain: N/A
Rental Boat: Yacht Rental Price: 60000 Captain: Bob
Rental Boat: Super Yacht Rental Price: 120000 Captain: Dora
████████████████████████████████████████████████████████████████████████
131.When Promotion Cannot Be Performed
example:
specialdeal.go File in the store Folder:
package store
type SpecialDeal struct {
Name string
*Product
price float64
}
func NewSpecialDeal(name string, p *Product, discount float64) *SpecialDeal {
return &SpecialDeal{ name, p, p.price - discount }
}
func (deal *SpecialDeal ) GetDetails() (string, float64, float64) {
return deal.Name, deal.price, deal.Price(0)
}
Using a New Type in the main.go:
package main
import (
"fmt"
"composition/store"
)
func main() {
product := store.NewProduct("Kayak", "Watersports", 279)
deal := store.NewSpecialDeal("Weekend Special", product, 50)
Name, price, Price := deal.GetDetails()
fmt.Println("Name:", Name)
fmt.Println("Price field:", price)
fmt.Println("Price method:", Price)
}
Output:
Name: Weekend Special
Price field: 229
Price method: 279
is a more concise way of expressing this statement:
return deal.Name, deal.price, deal.Product.Price(0)
The new Price method stops Go from promoting the Product method
and produces the following result
Defining a Method in the specialdeal.go:
package store
type SpecialDeal struct {
Name string
*Product
price float64
}
func NewSpecialDeal(name string, p *Product, discount float64) *SpecialDeal {
return &SpecialDeal{ name, p, p.price - discount }
}
func (deal *SpecialDeal ) GetDetails() (string, float64, float64) {
return deal.Name, deal.price, deal.Price(0)
}
func (deal *SpecialDeal) Price(taxRate float64) float64 {
return deal.price
}
Output:
Name: Weekend Special
Price field: 229
Price method: 229
████████████████████████████████████████████████████████████████████████
132.Promotion Ambiguity
A related issue arises when two embedded fields use the same field or method names
example:
An Ambiguous Method in the main.go:
package main
import (
"fmt"
"composition/store"
)
func main() {
kayak := store.NewProduct("Kayak", "Watersports", 279)
type OfferBundle struct {
*store.SpecialDeal
*store.Product
}
bundle := OfferBundle {
store.NewSpecialDeal("Weekend Special", kayak, 50),
store.NewProduct("Lifrejacket", "Watersports", 48.95),
}
fmt.Println("Price:", bundle.Price(0))
}
Output:
.\main.go:22:33: ambiguous selector bundle.Price
████████████████████████████████████████████████████████████████████████
133.Composition and Interfaces
example:
Mixing Types in the main.go:
package main
import (
"fmt"
"composition/store"
)
func main() {
products := map[string]*store.Product {
"Kayak": store.NewBoat("Kayak", 279, 1, false),
"Ball": store.NewProduct("Soccer Ball", "Soccer", 19.50),
}
for , p := range products {
fmt.Println("Name:", p.Name, "Category:", p.Category, "Price:", p.Price(0.2))
}
}
Output:
.\main.go:11:9: cannot use store.NewBoat("Kayak", 279, 1, false) (type *store.Boat) as
type *store.Product in map value
████████████████████████████████████████████████████████████████████████
134.Composition to Implement Interfaces
Go takes promoted methods into account when determining whether a type conforms to an interface,
which avoids the need to duplicate methods that are already present through an embedded field.
add a file named forsale.go to the store folder:
The Contents of the forsale.go:
package store
type ItemForSale interface {
Price(taxRate float64) float64
}
Using an Interface in the main.go:
package main
import (
"fmt"
"composition/store"
)
func main() {
products := map[string]store.ItemForSale {
"Kayak": store.NewBoat("Kayak", 279, 1, false),
"Ball": store.NewProduct("Soccer Ball", "Soccer", 19.50),
}
for key, p := range products {
fmt.Println("Key:", key, "Price:", p.Price(0.2))
}
}
Output:
Key: Kayak Price: 334.8
Key: Ball Price: 23.4
████████████████████████████████████████████████████████████████████████
135.the Type Switch Limitation
example:
main.go:
package main
import (
"fmt"
"composition/store"
)
func main() {
products := map[string]store.ItemForSale {
"Kayak": store.NewBoat("Kayak", 279, 1, false),
"Ball": store.NewProduct("Soccer Ball", "Soccer", 19.50),
}
for key, p := range products {
switch item := p.(type) {
case *store.Product, *store.Boat:
fmt.Println("Name:", item.Name, "Category:", item.Category,
"Price:", item.Price(0.2))
default:
fmt.Println("Key:", key, "Price:", p.Price(0.2))
}
}
}
Output:
.\main.go:21:42: item.Name undefined (type store.ItemForSale has no field or method Name)
.\main.go:21:66: item.Category undefined (type store.ItemForSale has no field or method
Category)
Using Separate case Statements in the main.go:
package main
import (
"fmt"
"composition/store"
)
func main() {
products := map[string]store.ItemForSale {
"Kayak": store.NewBoat("Kayak", 279, 1, false),
"Ball": store.NewProduct("Soccer Ball", "Soccer", 19.50),
}
for key, p := range products {
switch item := p.(type) {
case *store.Product:
fmt.Println("Name:", item.Name, "Category:", item.Category,
"Price:", item.Price(0.2))
case *store.Boat:
fmt.Println("Name:", item.Name, "Category:", item.Category,
"Price:", item.Price(0.2))
default:
fmt.Println("Key:", key, "Price:", p.Price(0.2))
}
}
}
Output:
Name: Kayak Category: Watersports Price: 334.8
Name: Soccer Ball Category: Soccer Price: 23.4
████████████████████████████████████████████████████████████████████████
136.the Type Switch Limitation An alternative solution
example:
Defining an Interface in the product.go:
package store
type Product struct {
Name, Category string
price float64
}
func NewProduct(name, category string, price float64) *Product {
return &Product{ name, category, price }
}
func (p *Product) Price(taxRate float64) float64 {
return p.price + (p.price * taxRate)
}
type Describable interface {
GetName() string
GetCategory() string
}
func (p *Product) GetName() string {
return p.Name
}
func (p *Product) GetCategory() string {
return p.Category
}
Using Interfaces in the main.go:
package main
import (
"fmt"
"composition/store"
)
func main() {
products := map[string]store.ItemForSale {
"Kayak": store.NewBoat("Kayak", 279, 1, false),
"Ball": store.NewProduct("Soccer Ball", "Soccer", 19.50),
}
for key, p := range products {
switch item := p.(type) {
case store.Describable:
fmt.Println("Name:", item.GetName(), "Category:", item.GetCategory(),
"Price:", item.(store.ItemForSale).Price(0.2))
default:
fmt.Println("Key:", key, "Price:", p.Price(0.2))
}
}
}
Output:
Name: Kayak Category: Watersports Price: 334.8
Name: Soccer Ball Category: Soccer Price: 23.4
████████████████████████████████████████████████████████████████████████
137.Composing Interfaces
Go allows interfaces to be composed from other interfaces
example:
Composing an Interface in the product.go:
package store
type Product struct {
Name, Category string
price float64
}
func NewProduct(name, category string, price float64) *Product {
return &Product{ name, category, price }
}
func (p *Product) Price(taxRate float64) float64 {
return p.price + (p.price * taxRate)
}
type Describable interface {
GetName() string
GetCategory() string
ItemForSale
}
func (p *Product) GetName() string {
return p.Name
}
func (p *Product) GetCategory() string {
return p.Category
}
████████████████████████████████████████████████████████████████████████
138.Goroutines and Channels
What are they?
Goroutines are lightweight threads created and managed by the Go runtime.
Channels are pipes that carry values of a specific type.
Why are they useful?
Goroutines allow functions to be executed concurrently, without needing to deal
with the complications of operating system threads. Channels allow goroutines to
produce results asynchronously.
How are they used?
Goroutines are created using the go keyword. Channels are defined as data types.
Are there any or limitations?
pitfalls Care must be taken to manage the direction of channels.
Goroutines that share data require additional features.
Are there any alternatives?
Goroutines and channels are the built-in Go concurrency features, but some
applications can rely on a single thread of execution, which is created by default to
execute the main function.
Receiving from a channel is a blocking operation,
meaning that execution will not continue until a value
has been received
Problem Solution
--------------------------------- -------------------------
Execute a function asynchronously Create a goroutine
Produce a result from a function Use a channel
executed asynchronously
Send and receive values Use arrow expressions
using a channel
Indicate that no further values Use the close function
will be sent over a channel
Enumerate the values received Use a for loop with the range keyword
from a channel
████████████████████████████████████████████████████████████████████████
139.How Go Executes Code
All Go programs use at least one goroutine because this is how Go executes the code in the
main function.
When compiled Go code is executed, the runtime creates a goroutine that starts executing
the statements in the entry point, which is the main function in the main package.
Each statement in the main function is executed in the order in which they are defined.
The goroutine keeps executing statements until
it reaches the end of the main function, at which point the application terminates.
The goroutine executes each statement in the main function synchronously,
which means that it waits
for the statement to complete before moving on to the next statement.
The statements in the main function
████████████████████████████████████████████████████████████████████████
140.Creating Additional Goroutines
Go allows the developer to create additional goroutines,
which execute code at the same time as the main
goroutine. Go makes it easy to create new goroutines
example:
package main
import (
"fmt"
"time
)
func main(){
fmt.Println("first statement")
fmt.Println("second statement")
time.Sleep(time.Second * 2)
go fmt.Println("first statement")
fmt.Println("first statement")
}
████████████████████████████████████████████████████████████████████████
141.Returning Results from Goroutines
Receiving from a channel is a blocking operation,
meaning that execution will not continue until a value
has been received
Getting a result from a function that is being executed asynchronously
can be complicated because it requires coordination between the goroutine
that produces the result and the goroutine that consumes the result.
To address this issue, Go provides channels, which are
conduits through which data can be sent and received.
Defining a Channel:
example:
var channel chan float64 = make(chan float64)
████████████████████████████████████████████████████████████████████████
142.Sending a Result Using a Channel
Receiving from a channel is a blocking operation,
meaning that execution will not continue until a value
has been received
example:
resultChannel <- total
████████████████████████████████████████████████████████████████████████
143.Receiving a Result Using a Channel
Receiving from a channel is a blocking operation,
meaning that execution will not continue until a value
has been received
example:
storeTotal += <- channel
████████████████████████████████████████████████████████████████████████
144.using adapters to execute functions asynchronously
It isn't always possible to rewrite existing functions or methods to use channels, but it is a simple matter
to execute synchronous functions asynchronously in a wrapper, like this:
The syntax is a little awkward because the arguments used to invoke the function are expressed
immediately following the function definition. But the result is the same, which is that a synchronous
function can be executed by a goroutine with the result being sent through a channel.
example:
...
calcTax := func(price float64) float64 {
return price + (price * 0.2)
}
wrapper := func (price float64, c chan float64) {
c <- calcTax(price)
}
resultChannel := make(chan float64)
go wrapper(275, resultChannel)
result := <- resultChannel
fmt.Println("Result:", result)
...
The wrapper function receives a channel, which it uses to send the value received from executing the
calcTax function synchronously. This can be expressed more concisely by defining a function without
assigning it to a variable, like this:
...
go func (price float64, c chan float64) {
c <- calcTax(price)
}(275, resultChannel)
...
████████████████████████████████████████████████████████████████████████
145.Coordinating Channels
By default, sending and receiving through a channel are blocking operations. This means a goroutine that
sends a value will not execute any further statements until another goroutine receives the value from the
channel. If a second goroutine sends a value, it will be blocked until the channel is cleared, causing a queue
of goroutines waiting for values to be received. This happens in the other direction, too, so that goroutines
that receive values will block until another goroutine sends one.
example:
If Bob has a message for Alice, the default channel behavior requires
Alice and Bob to agree on a meeting place, and
whoever gets there first will wait for the other to arrive.
Bob will only give the message to Alice when they are
both present. When Charlie also has a message for Alice, he will form a queue behind Bob.
Everyone waits patiently, messages are transferred only when the sender
and receiver are both available, and messages are
processed sequentially.
████████████████████████████████████████████████████████████████████████
146.Buffered Channel
The default channel behavior can lead to bursts of activity as goroutines do their work, followed by a long
idle period waiting for messages to be received. This doesn't have an impact on the example application
because the goroutines finish once their messages are received, but in a real project goroutines often have
repetitive tasks to perform, and waiting for a receiver can cause a performance bottleneck.
An alternative approach is to create a channel with a buffer,
which is used to accept values from a
sender and store them until a receiver becomes available.
This makes sending a message a nonblocking
operation, allowing a sender to pass its value to the channel and continue working without having to wait
for a receiver.
example:
This is similar to Alice having an inbox on her desk. Senders come to Alice's office and put
their message into the inbox, leaving it for Alice to read when she is ready. But, if the inbox is full, then they
will have to wait until she has processed some of her backlog before sending a new message.
Creating a Buffered Channel
example:
var channel chan float64 = make(chan float64, 2)
Result explain:
For this example, I have set the size of the buffer to 2, meaning that two senders will be able to send
values through the channel without having to wait for them to be received.
████████████████████████████████████████████████████████████████████████
147.Inspecting a Channel Buffer
You can determine the size of a channel's buffer using
the built-in cap function and determine how many
values are in the buffer using the len function
The modified statement uses the len and cap functions to report
the number of values in the channel's
buffer and the overall size of the buffer.
example:
len(channel), "items in buffer, size", cap(channel))
████████████████████████████████████████████████████████████████████████
148.Closing a Channel
The solution for this problem is for the sender to indicate when no further values are coming through the
channel, which is done by closing the channel
The built-in close function accepts a channel as its argument and is used to indicate that there will be
no further values sent through the channel. Receivers can check if a channel is closed when requesting a
value.
You need to close channels only when it is helpful to do so to coordinate your goroutines.
Go doesn't require channels to be closed to free up resources or perform any kind of housekeeping task.
example:
close(channel)
The receive operator can be used to obtain two values.
The first value is assigned the value received
from the channel, and the second value indicates whether the channel is closed
It is illegal to send values to a channel once it has been closed.
example:
if details, open := <- dispatchChannel; open {
fmt.Println("Dispatch to", details.Customer, ":", details.Quantity,
"x", details.Product.Name)
} else {
fmt.Println("Channel has been closed")
break
}
████████████████████████████████████████████████████████████████████████
149.Enumerating Channel Values
A for loop can be used with the range keyword to enumerate the values sent through a channel, allowing
the values to be received more easily and terminating the loop when the channel is closed
The range expression produces one value per iteration, which is the value received from the channel.
The for loop will continue to receive values until the channel is closed. (You can use a for...range loop on a
channel that isn't closed, in which case the loop will never exit.)
example:
go DispatchOrders(dispatchChannel)
for details := range dispatchChannel {
fmt.Println("Dispatch to", details.Customer, ":", details.Quantity,
"x", details.Product.Name)
}
fmt.Println("Channel has been closed")
████████████████████████████████████████████████████████████████████████
150.Restricting Channel Direction
By default, channels can be used to send and receive data, but this can be restricted when using channels
as arguments, such that only send or receive operations can be performed.
example:
func DispatchOrders(channel chan<- DispatchNotification)
The location of the arrow specifies the direction of the channel.
When the arrow follows the chan keyword,
then the channel can be used only to send.
The channel can be used to receive only if the arrow precedes the chan keyword (<-chan, for example).
Attempting to receive from a send-only (and vice versa) channel is a compile-time error,
example:
# concurrency
.\orderdispatch.go:29:29: invalid operation: <-channel (receive from send-only type chan<-
DispatchNotification)
████████████████████████████████████████████████████████████████████████
151.Restricting Channel Argument Direction
Go allows bidirectional channels to be assigned to unidirectional channel variables,
allowing restrictions to be applied
example:
func receiveDispatches(channel <-chan DispatchNotification) {
for details := range channel {
fmt.Println("Dispatch to", details.Customer, ":", details.Quantity,
"x", details.Product.Name)
}
fmt.Println("Channel has been closed")
}
Restrictions on channel direction can also be created through explicit conversion
The explicit conversion for the receive-only channel requires parentheses around the channel type
to prevent the compiler from interpreting a conversion to the DispatchNotification type.
example:
func main() {
dispatchChannel := make(chan DispatchNotification, 100)
go DispatchOrders(chan<- DispatchNotification(dispatchChannel))
receiveDispatches((<-chan DispatchNotification)(dispatchChannel))
}
████████████████████████████████████████████████████████████████████████
152.Select Statements
The select keyword is used to group operations that will send or receive from channels,
which allows for complex arrangements of goroutines and channels to be created.
There are several uses for select statements.
The simplest use for select statements is to receive from a channel without blocking, ensuring that a
goroutine won't have to wait when the channel is empty.
A select statement has a similar structure to a switch statement, except that the case statements are
channel operations.
When the select statement is executed, each channel operation is evaluated until one
that can be performed without blocking is reached. The channel operation is performed, and the statements
enclosed in the case statement are executed. If none of the channel operations can be performed, the
statements in the default clause are executed.
example:
for {
select {
case details, ok := <- dispatchChannel:
if ok {
fmt.Println("Dispatch to", details.Customer, ":",
details.Quantity, "x", details.Product.Name)
} else {
fmt.Println("Channel has been closed")
goto alldone
}
default:
fmt.Println("-- No message ready to be received")
time.Sleep(time.Millisecond * 500)
}
}
alldone: fmt.Println("All values received")
████████████████████████████████████████████████████████████████████████
153.Receiving from Multiple Channels
A select statement can be used to receive without blocking,
when there are multiple channels, through which values are sent at different
rates. A select statement will allow the receiver to obtain values from whichever channel has them, without
blocking on any single channel
In this example, the select statement is used to receive values from two channels, one that carries
DispatchNofitication values and one that carries Product values. Each time the select statement is
executed, it works its way through the case statements, building up a list of the ones from which a value can
be read without blocking. One of the case statements is selected from the list at random and executed. If
none of the case statements can be performed, the default clause is executed.
example:
package main
import (
"fmt"
"time"
)
func enumerateProducts(channel chan<- *Product) {
for , p := range ProductList[:3] {
channel <- p
time.Sleep(time.Millisecond * 800)
}
close(channel)
}
func main() {
dispatchChannel := make(chan DispatchNotification, 100)
go DispatchOrders(chan<- DispatchNotification(dispatchChannel))
productChannel := make(chan *Product)
go enumerateProducts(productChannel)
openChannels := 2
for {
select {
case details, ok := <- dispatchChannel:
if ok {
fmt.Println("Dispatch to", details.Customer, ":",
details.Quantity, "x", details.Product.Name)
} else {
fmt.Println("Dispatch channel has been closed")
dispatchChannel = nil
openChannels--
}
case product, ok := <- productChannel:
if ok {
fmt.Println("Product:", product.Name)
} else {
fmt.Println("Product channel has been closed")
productChannel = nil
openChannels--
}
default:
if (openChannels == 0) {
goto alldone
}
fmt.Println("-- No message ready to be received")
time.Sleep(time.Millisecond * 500)
}
}
alldone: fmt.Println("All values received")
}
████████████████████████████████████████████████████████████████████████
154.Sending Without Blocking
A select statement can also be used to send to a channel without blocking
example:
func enumerateProducts(channel chan<- *Product) {
for , p := range ProductList {
select {
case channel <- p:
fmt.Println("Sent product:", p.Name)
default:
fmt.Println("Discarding product:", p.Name)
time.Sleep(time.Second)
}
}
close(channel)
}
████████████████████████████████████████████████████████████████████████
155.Sending to Multiple Channels
If there are multiple channels available, a select statement can be used to find a channel for which sending
will not block
You can combine case statements with send and receive operations in the same select statement.
When the select statement is executed, the Go runtime builds a combined list of case statements that can be
executed without blocking and picks one at random, which can be either a send or a receive statement.
This example has two channels with small buffers. As with receiving, the select statement builds a list
of the channels through which a value can be sent without blocking and then picks one at random from that
list. If none of the channels can be used, then the default clause is executed. There is no default clause
in this example, which means that the select statement will block until one of the channels can receive
a value.
example:
func enumerateProducts(channel1, channel2 chan<- *Product) {
for , p := range ProductList {
select {
case channel1 <- p:
fmt.Println("Send via channel 1")
case channel2 <- p:
fmt.Println("Send via channel 2")
}
}
close(channel1)
close(channel2)
}
████████████████████████████████████████████████████████████████████████
156.Error Handling
What is it?
Go's error handling allows exceptional conditions and failures to be represented and dealt with.
Why is it useful?
Applications will often encounter unexpected situations,
and the error handling features provide a way to respond to those situations when they arise.
How is it used?
The error interface is used to define error conditions,
which are typically returned as function results.
The panic function is called when an unrecoverable error occurs.
Are there any pitfalls or limitations?
Care must be taken to ensure that errors are communicated to the part of the
application that can best decide how serious the situation is.
Are there any alternatives?
You don't have to use the error interface in your code,
but it is employed throughout the Go standard library and is difficult to avoid.
Go provides a predefined interface named error that provides one way to resolve this issue. Here is the
definition of the interface:
type error interface {
Error() string
}
Problem Solution
---------------------------------- ---------------------------------------------------
Indicate that an error has occurred Create a struct that implements the error interface
and return it as a function result
Report an error over a channel Add an error field to the struct type used for
channel messages
Indicate that an unrecoverable Call the panic function
error has occurred
Recover from a panic Use the defer keyword to register a function that
calls the recover function
████████████████████████████████████████████████████████████████████████
157.Generating Errors
Functions and methods can express exceptional or unexpected outcomes by producing error responses
Defining an Error
example:
package main
import "fmt"
func main() {
categories := []string { "Watersports", "Chess", "Running" }
for , cat := range categories {
total, err := Products.TotalPrice(cat)
if (err == nil) {
fmt.Println(cat, "Total:", ToCurrency(total))
} else {
fmt.Println(cat, "(no such category)")
}
}
}
Output:
Watersports Total: $328.95
Chess Total: $1291.00
Running (no such category)
████████████████████████████████████████████████████████████████████████
158.Ignoring Error Results
I don't recommend ignoring error results because it means you will lose important information.
but if you don't need to know when something goes wrong,
then you can use the blank identifier instead of a
name for the error result, like this:
example:
package main
import "fmt"
func main() {
categories := []string { "Watersports", "Chess", "Running" }
for , cat := range categories {
total, := Products.TotalPrice(cat)
fmt.Println(cat, "Total:", ToCurrency(total))
}
}
████████████████████████████████████████████████████████████████████████
159.Reporting Errors via Channels
example:
operations.go:
package main
type CategoryError struct {
requestedCategory string
}
func (e *CategoryError) Error() string {
return "Category " + e.requestedCategory + " does not exist"
}
type ChannelMessage struct {
Category string
Total float64
*CategoryError
}
func (slice ProductSlice) TotalPrice(category string) (total float64,
err *CategoryError) {
productCount := 0
for , p := range slice {
if (p.Category == category) {
total += p.Price
productCount++
}
}
if (productCount == 0) {
err = &CategoryError{ requestedCategory: category}
}
return
}
func (slice ProductSlice) TotalPriceAsync (categories []string,
channel chan<- ChannelMessage) {
for , c := range categories {
total, err := slice.TotalPrice(c)
channel <- ChannelMessage{
Category: c,
Total: total,
CategoryError: err,
}
}
close(channel)
}
main.go:
package main
import "fmt"
func main() {
categories := []string { "Watersports", "Chess", "Running" }
channel := make(chan ChannelMessage, 10)
go Products.TotalPriceAsync(categories, channel)
for message := range channel {
if message.CategoryError == nil {
fmt.Println(message.Category, "Total:", ToCurrency(message.Total))
} else {
fmt.Println(message.Category, "(no such category)")
}
}
}
Output:
Watersports Total: $328.95
Chess Total: $1291.00
Running (no such category)
████████████████████████████████████████████████████████████████████████
160.String Processing and Regular Expressions
What are they?
String processing includes a wide range of operations, from trimming
whitespace to splitting a string into components. Regular expressions
are patterns that allow string matching rules to be concisely defined.
Why are they useful?
These operations are useful when an application needs to process
string values. A common example is processing HTTP requests.
How are they used?
These features are contained in the strings and regexp packages,
which are part of the standard library.
Are there any pitfalls or limitations?
There are some quirks in the way that some of these operations are
performed, but they mostly behave as you would expect.
Are there any alternatives?
The use of these packages is optional, and they do not have
to be used. That said, there is little point in creating your own
implementations of these features since the standard library is well-
written and thoroughly tested.
Problem Solution
------------------- -------------------------------------------------------------------
Compare strings Use the Contains, EqualFold, or Has* function in the strings package
Convert string case Use the ToLower, ToUpper, Title, or ToTitle function in the
strings package
Check or change Use the functions provided by the unicode package
character case
Find content in strings Use the functions provided by the strings or regexp package
Split a string Use the Fields or Split* function in the strings and regexp packages
Join strings Use the Join or Repeat function in the strings package
Trim characters from Use the Trim* functions in the strings package
a string
Perform a substitution Use the Replace* or Map function in the strings package,
تعویض انجام دهید use a Replacer, or use the Replace* functions in the regexp package
Efficiently build a Use the Builder type in the strings package
string
████████████████████████████████████████████████████████████████████████
161.Comparing Strings
The strings Functions for Comparing Strings
Function Description
------------- ------------------------
Contains(s, substr) This function returns true if the string s contains substr and false if it does not.
ContainsAny(s, substr) This function returns true if the string s contains any of the characters
contained in the string substr.
ContainsRune(s, rune) This function returns true if the string s contains a specific rune.
EqualFold(s1, s2) This function performs a case-insensitive comparison and returns true of
strings s1 and s2 are the same.
HasPrefix(s, prefix) This function returns true if the string s begins with the string prefix.
HasSuffix(s, suffix) This function returns true if the string ends with the string suffix.
example:
package main
import (
"fmt"
"strings"
)
func main() {
product := "Kayak"
fmt.Println("Contains:", strings.Contains(product, "yak"))
fmt.Println("ContainsAny:", strings.ContainsAny(product, "abc"))
fmt.Println("ContainsRune:", strings.ContainsRune(product, 'K'))
fmt.Println("EqualFold:", strings.EqualFold(product, "KAYAK"))
fmt.Println("HasPrefix:", strings.HasPrefix(product, "Ka"))
fmt.Println("HasSuffix:", strings.HasSuffix(product, "yak"))
}
Output:
Contains: true
ContainsAny: true
ContainsRune: true
HasPrefix: true
HasSuffix: true
EqualFold: true
████████████████████████████████████████████████████████████████████████
162.Using The Byte-Oriented Functions
example:
package main
import (
"fmt"
"strings"
"bytes"
)
func main() {
price := "€100"
fmt.Println("Strings Prefix:", strings.HasPrefix(price, "€"))
fmt.Println("Bytes Prefix:", bytes.HasPrefix([]byte(price),
[]byte { 226, 130 }))
}
Output:
Strings Prefix: true
Bytes Prefix: true
████████████████████████████████████████████████████████████████████████
163.Converting String Case
The Case Functions in the strings Package
Function Description
-------------- ------------------------------------------------------
ToLower(str) This function returns a new string containing the characters in the specified string
mapped to lowercase.
ToUpper(str) This function returns a new string containing the characters in the specified string
mapped to lowercase.
Title(str) This function converts the specific string so that the first character of each word is
uppercase and the remaining characters are lowercase.
ToTitle(str) This function returns a new string containing the characters in the specified string
mapped to title case.
Care must be taken with the Title and ToTitle functions, which don't work the way you might expect.
The Title function returns a string that is suitable for use as a title, but it treats all words the same
Creating a Title:
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "A boat for sailing"
fmt.Println("Original:", description)
fmt.Println("Title:", strings.Title(description))
fmt.Println("Title:", strings.ToTitle(description))
}
Output:
Original: A boat for sailing
Title: A Boat For Sailing
Title: A BOAT FOR SAILING
████████████████████████████████████████████████████████████████████████
164.Title Case
example:
package main
import (
"fmt"
"strings"
)
func main() {
specialChar := "\u01c9"
fmt.Println("Original:", specialChar, []byte(specialChar))
upperChar := strings.ToUpper(specialChar)
fmt.Println("Upper:", upperChar, []byte(upperChar))
titleChar := strings.ToTitle(specialChar)
fmt.Println("Title:", titleChar, []byte(titleChar))
}
Output:
Original: lj [199 137]
Upper: LJ [199 135]
Title: Lj [199 136]
████████████████████████████████████████████████████████████████████████
165.Working with Character Case
The unicode package provides functions that can be used to determine or change the case of individual
characters
Functions in the unicode Package for Character Case
Function Description
------------- -----------------------------------------------------------------
IsLower(rune) This function returns true if the specified rune is lowercase.
ToLower(rune) This function returns the lowercase rune associated with the specified rune.
IsUpper(rune) This function returns true if the specified rune is uppercase.
ToUpper(rune) This function returns the upper rune associated with the specified rune.
IsTitle(rune) This function returns true if the specified rune is title case.
ToTitle(rune) This function returns the title case rune associated with the specified rune.
████████████████████████████████████████████████████████████████████████
166.the Rune Case Functions
example:
package main
import (
"fmt"
"unicode"
)
func main() {
product := "Kayak"
for _, char := range product {
fmt.Println(string(char), "Upper case:", unicode.IsUpper(char))
}
}
Output:
K Upper case: true
a Upper case: false
y Upper case: false
a Upper case: false
k Upper case: false
████████████████████████████████████████████████████████████████████████
167.Inspecting Strings
The strings Functions for Inspecting Strings
Function Description
------------ --------------------------------------------------
Count(s, sub) This function returns an int that reports how many times the specified
substring is found in the string s.
Index(s, sub) These functions return the index of the first or last occurrence of a specified.
LastIndex(s, sub) substring string within the string s, or -1 if there is no occurrence.
IndexAny(s, chars) These functions return the first or last occurrence of any character in the
LastIndexAny(s, chars) specified string within the string s, or -1 if there is no occurrence.
IndexByte(s, b) These functions return the index of the first or last occurrence of a specified
LastIndexByte(s, b) byte within the string s, or -1 if there is no occurrence.
IndexFunc(s, func) These functions return the index of the first or last occurrence of the
LastIndexFunc(s, func) character in the string s for which the specified function returns true, as
described in the “Inspecting Strings with Custom Functions” section.
example:
package main
import (
"fmt"
"strings"
//"unicode"
)
func main() {
description := "A boat for one person"
fmt.Println("Count:", strings.Count(description, "o"))
fmt.Println("Index:", strings.Index(description, "o"))
fmt.Println("LastIndex:", strings.LastIndex(description, "o"))
fmt.Println("IndexAny:", strings.IndexAny(description, "abcd"))
fmt.Println("LastIndex:", strings.LastIndex(description, "o"))
fmt.Println("LastIndexAny:", strings.LastIndexAny(description, "abcd"))
}
Output:
Count: 4
Index: 3
LastIndex: 19
IndexAny: 2
LastIndex: 19
LastIndexAny: 4
████████████████████████████████████████████████████████████████████████
168.IndexFunc and LastIndexFunc functions
Inspecting Strings with Custom Functions
The IndexFunc and LastIndexFunc functions use a custom function to inspect strings, using custom functions
Custom functions receive a rune and return a bool result that indicates if the character meets the
desired condition. The IndexFunc function invokes the custom function for each character in the string until
a true result is obtained, at which point the index is returned.
The isLetterB variable is assigned a custom function that receives a rune and returns true if the rune
is a uppercase or lowercase B. The custom function is passed to the strings.IndexFunc function
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "A boat for one person"
isLetterB := func (r rune) bool {
return r == 'B' || r == 'b'
}
fmt.Println("IndexFunc:", strings.IndexFunc(description, isLetterB))
}
Output:
IndexFunc: 2
████████████████████████████████████████████████████████████████████████
169.Splitting Strings
The Functions for Splitting Strings in the strings Package
Function Description
--------------- --------------------------------
Fields(s) This function splits a string on whitespace characters and returns a slice
containing the nonwhitespace sections of the string s.
FieldsFunc(s, func) This function splits the string s on the characters for which a custom function
returns true and returns a slice containing the remaining sections of the string.
Split(s, sub) This function splits the string s on every occurrence of the specified substring,
returning a string slice. If the separator is the empty string, then the slice will
contain strings for each character.
SplitN(s, sub, max) This function is similar to Split, but accepts an additional int argument that
specifies the maximum number of substrings to return. The last substring in the
result slice will contain the unsplit portion of the source string.
SplitAfter(s, sub) This function is similar to Split but includes the substring used in the results.
SplitAfterN(s, sub, max) This function is similar to SplitAfter, but accepts an additional int argument
that specifies the maximum number of substrings to return.
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "A boat for one person"
splits := strings.Split(description, " ")
for _, x := range splits {
fmt.Println("Split >>" + x + "<<")
}
splitsAfter := strings.SplitAfter(description, " ")
for _, x := range splitsAfter {
fmt.Println("SplitAfter >>" + x + "<<")
}
}
Output:
Split >>A<<
Split >>boat<<
Split >>for<<
Split >>one<<
Split >>person<<
SplitAfter >>A <<
SplitAfter >>boat <<
SplitAfter >>for <<
SplitAfter >>one <<
SplitAfter >>person<<
████████████████████████████████████████████████████████████████████████
170.SplitN and SplitAfterN functions
Restricting the Number of Results
The SplitN and SplitAfterN functions accept an int argument that specifies the maximum number of
results that should be included in the results
Restricting the Results
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "A boat for one person"
splits := strings.SplitN(description, " ", 3)
for _, x := range splits {
fmt.Println("Split >>" + x + "<<")
}
}
Output:
Split >>A<<
Split >>boat<<
Split >>for one person<<
████████████████████████████████████████████████████████████████████████
171.strings.SplitN function
Splitting on Whitespace Characters
One limitation of the Split, SplitN, SplitAfter, and SplitAfterN functions is they do not deal with
repeated sequences of characters, which can be a problem when splitting a string on whitespace characters
The words in the source string are double-spaced, but the SplitN function splits only on the first space
character, which produces odd results.
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "This is double spaced"
splits := strings.SplitN(description, " ", 3)
for _, x := range splits {
fmt.Println("Split >>" + x + "<<")
}
}
Output:
Split >>This<<
Split >><<
Split >>is double spaced<<
████████████████████████████████████████████████████████████████████████
172.Fields Function
The Fields function doesn't support a limit on the number of results
but does deal with the double spaces properly.
The Fields function has a better approach, which is to split on any character for
which the IsSpace function in the unicode package returns true.
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "This is double spaced"
splits := strings.Fields(description)
for _, x := range splits {
fmt.Println("Field >>" + x + "<<")
}
}
Output:
Field >>This<<
Field >>is<<
Field >>double<<
Field >>spaced<<
████████████████████████████████████████████████████████████████████████
173.FieldsFunc function
Splitting Using a Custom Function to Split Strings
The FieldsFunc function splits a string by passing each character to a custom function and splitting when
that function returns true
The custom function receives a rune and returns true if that rune should cause the string to split.
The FieldsFunc function is smart enough to deal with repeated characters
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "This is double spaced"
splitter := func(r rune) bool {
return r == ' '
}
splits := strings.FieldsFunc(description, splitter)
for _, x := range splits {
fmt.Println("Field >>" + x + "<<")
}
}
Output:
Field >>This<<
Field >>is<<
Field >>double<<
Field >>spaced<<
████████████████████████████████████████████████████████████████████████
174.Trimming Strings
The Functions for Trimming Strings in the strings Package
Function Description
------------ ---------------------------------------
TrimSpace(s) This function returns the string s without leading or trailing whitespace characters.
Trim(s, set) This function returns a string from which any leading or trailing characters
contained in the string set are removed from the string s.
TrimLeft(s, set) This function returns the string s without any leading character contained
in the string set. This function matches any of the specified characters—use
the TrimPrefix function to remove a complete substring.
TrimRight(s, set) This function returns the string s without any trailing character contained
in the string set. This function matches any of the specified characters—use
the TrimSuffix function to remove a complete substring.
TrimPrefix(s, prefix) This function returns the string s after removing the specified prefix string.
This function removes the complete prefix string—use the TrimLeft
function to remove characters from a set.
TrimSuffix(s, suffix) This function returns the string s after removing the specified suffix string.
This function removes the complete suffix string—use the TrimRight
function to remove characters from a set.
TrimFunc(s, func) This function returns the string s from which any leading or trailing
character for which a custom function returns true are removed.
TrimLeftFunc(s, func) This function returns the string s from which any leading character for
which a custom function returns true are removed.
TrimRightFunc(s, func) This function returns the string s from which any trailing character for
which a custom function returns true are removed.
████████████████████████████████████████████████████████████████████████
175.TrimSpace function
Trimming Whitespace
example:
package main
import (
"fmt"
"strings"
)
func main() {
username := " Alice"
trimmed := strings.TrimSpace(username)
fmt.Println("Trimmed:", ">>" + trimmed + "<<")
}
Output:
Trimmed: >>Alice<<
████████████████████████████████████████████████████████████████████████
176.Trim, TrimLeft, and TrimRight functions
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "A boat for one person"
trimmed := strings.Trim(description, "Anor ")
fmt.Println("Trimmed:>>"+ trimmed+ "<<")
}
Ourput:
Trimmed:>>boat for one pers<<
████████████████████████████████████████████████████████████████████████
177.TrimPrefix and TrimSuffix functions
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "A boat for one person"
prefixTrimmed := strings.TrimPrefix(description, "A boat ")
wrongPrefix := strings.TrimPrefix(description, "A hat ")
fmt.Println("Trimmed:", prefixTrimmed)
fmt.Println("Not trimmed:", wrongPrefix)
}
Output:
Trimmed: for one person
Not trimmed: A boat for one person
████████████████████████████████████████████████████████████████████████
178.TrimFunc, TrimLeftFunc, and TrimRightFunc functions
example:
package main
import (
"fmt"
"strings"
)
func main() {
description := "A boat for one person"
trimmer := func(r rune) bool {
return r == 'A' || r == 'n'
}
trimmed := strings.TrimFunc(description, trimmer)
fmt.Println("Trimmed:", trimmed)
}
Output:
Trimmed: boat for one person
████████████████████████████████████████████████████████████████████████
179.Altering Strings
The Functions for Altering Strings in the strings Package
Function Description
---------------- -----------------------------------
Replace(s, old, new, n) This function alters the string s by replacing occurrences of the string old with the
string new. The maximum number of occurrences that will be replaced is specified by
the int argument n.
ReplaceAll(s, old, new) This function alters the string s by replacing all occurrences of the string old with
the string new. Unlike the Replace function, there is no limit on the number of
occurrences that will be replaced.
Map(func, s) This function generates a string by invoking the custom function for each character in
the string s and concatenating the results. If the function produces a negative value,
the current character is dropped without a replacement.
████████████████████████████████████████████████████████████████████████
180.Replace and ReplaceAll functions
The Replace function allows a maximum number of changes to be specified,
while the ReplaceAll function will replace all the
occurrences of the substring it finds
example:
package main
import (
"fmt"
"strings"
)
func main() {
text := "It was a boat. A small boat."
replace := strings.Replace(text, "boat", "canoe", 1)
replaceAll := strings.ReplaceAll(text, "boat", "truck")
fmt.Println("Replace:", replace)
fmt.Println("Replace All:", replaceAll)
}
Output:
Replace: It was a canoe. A small boat.
Replace All: It was a truck. A small truck.
████████████████████████████████████████████████████████████████████████
181.Map function
example:
package main
import (
"fmt"
"strings"
)
func main() {
text := "It was a boat. A small boat."
mapper := func(r rune) rune {
if r == 'b' {
return 'c'
}
return r
}
mapped := strings.Map(mapper, text)
fmt.Println("Mapped:", mapped)
}
Output:
Mapped: It was a coat. A small coat.
████████████████████████████████████████████████████████████████████████
182.NewReplacer function
The strings package exports a struct type named Replacer that is used to replace strings
example:
package main
import (
"fmt"
"strings"
)
func main() {
text := "It was a boat. A small boat. 111"
replacer := strings.NewReplacer("boat", "kayak",
"small", "huge",
"111", "222",
)
replaced := replacer.Replace(text)
fmt.Println("Replaced:", replaced)
}
Output:
Replaced: It was a kayak. A huge kayak. 222
████████████████████████████████████████████████████████████████████████
183.The Replacer Methods
Name Description
------------------- --------------------------------------------
Replace(s) This method returns a string for which all the replacements specified with the
constructor have been performed on the string s.
WriteString(writer, s) This method is used to perform the replacements specified with the constructor
and write the results to an io.Writer
████████████████████████████████████████████████████████████████████████
184.Building and Generating Strings
The strings Functions for Generating Strings
Function Description
---------------- ----------------------------------------------------------------------------------------
Join(slice, sep) This function combines the elements in the specified string slice, with the specified
separator string placed between elements.
Repeat(s, count) This function generates a string by repeating the string s for a specified number of times.
████████████████████████████████████████████████████████████████████████
185.Join and Repeat functions
example:
package main
import (
"fmt"
"strings"
)
func main() {
text := "It was a boat. A small boat."
elements := strings.Fields(text)
joined := strings.Join(elements, "--")
fmt.Println("Joined:", joined)
esplited := strings.Split(text, " ")
fmt.Printf("%q\n",esplited)
}
Output:
Joined: It--was--a--boat.--A--small--boat.
["It" "was" "a" "boat." "A" "small" "boat."]
████████████████████████████████████████████████████████████████████████
186.Building Strings
The strings.Builder Methods
Name Description
--------------- --------------------------------------------
WriteString(s) This method appends the string s to the string being built.
WriteRune(r) This method appends the character r to the string being built.
WriteByte(b) This method appends the byte b to the string being built.
String() This method returns the string that has been created by the builder.
Reset() This method resets the string created by the builder.
Len() This method returns the number of bytes used to store the string created by the builder.
Cap() This method returns the number of bytes that have been allocated by the builder.
Grow(size) This method increases the number of bytes used allocated by the builder to store the
string that is being built.
████████████████████████████████████████████████████████████████████████
187.builder.String()
Creating the string using the Builder is more efficient than using the concatenation operator on regular
string values, especially if the Grow method is used to allocate storage in advance.
Care must be taken to use pointers when passing Builder values to and from functions and
methods; otherwise, the efficiency gains will be lost when the Builder is copied.
example:
package main
import (
"fmt"
"strings"
)
func main() {
text := "It was a boat. A small boat."
var builder strings.Builder
for _, sub := range strings.Fields(text) {
if (sub == "small") {
builder.WriteString("very ")
}
builder.WriteString(sub)
builder.WriteRune(' ')
}
fmt.Println("String:", builder.String())
}
████████████████████████████████████████████████████████████████████████
188.new() function
example:
package main
import "fmt"
func main() {
// 1.
var ChannelName1 string = "AcronProject"
fmt.Println("Value=", ChannelName1, "memory address=", &ChannelName1)
// 2.
var ChannelName2 *string = new(string)
fmt.Println("Value ??? = ", ChannelName2, "memory address=", &ChannelName2)
var ChannelName3 *string = new(string)
fmt.Println("Value ??? = ", ChannelName3, "memory address=", &ChannelName3)
fmt.Printf("%q \n",*ChannelName2 + *ChannelName3)
}
Output:
Value= AcronProject memory address= 0xc000014070
Value ??? = 0xc000014090 memory address= 0xc00004e028
Value ??? = 0xc0000140a0 memory address= 0xc00004e030
""
████████████████████████████████████████████████████████████████████████
189.Regular Expressions
The regular expressions used in this section perform basic matches, but the regexp package
supports an extensive pattern syntax, which is described at https://pkg.go.dev/regexp/syntax@go1.17.1.
The Basic Functions Provided by the regexp Package
Function Description
----------------- --------------------------------------------------------------------------
Match(pattern, b) This function returns a bool that indicates whether a pattern is matched by
the byte slice b.
MatchString(patten, s) This function returns a bool that indicates whether a pattern is matched by
the string s.
Compile(pattern) This function returns a RegExp that can be used to perform repeated pattern
matching with the specified pattern.
MustCompile(pattern) This function provides the same feature as Compile but panics,
if the specified pattern cannot be compiled.
example:
package main
import (
"fmt"
//"strings"
"regexp"
)
func main() {
description := "A boat for one person"
match, err := regexp.MatchString("[A-z]oat", description)
if (err == nil) {
fmt.Println("Match:", match)
} else {
fmt.Println("Error:", err)
}
}
Output:
Match: true
████████████████████████████████████████████████████████████████████████
190.Compiling and Reusing Patterns
The MatchString function is simple and convenient,
but the full power of regular expressions is accessed
through the Compile function
████████████████████████████████████████████████████████████████████████
191.regexp.Compile() function
This is more efficient because the pattern has to be compiled only once.
The result of the Compile
function is an instance of the RegExp type,
which defines the MatchString function.
example:
package main
import (
"fmt"
"regexp"
)
func main() {
pattern, compileErr := regexp.Compile("[A-z]oat")
description := "A boat for one person"
question := "Is that a goat?"
preference := "I like oats"
if (compileErr == nil) {
fmt.Println("Description:", pattern.MatchString(description))
fmt.Println("Question:", pattern.MatchString(question))
fmt.Println("Preference:", pattern.MatchString(preference))
} else {
fmt.Println("Error:", compileErr)
}
}
Output:
Description: true
Question: true
Preference: false
████████████████████████████████████████████████████████████████████████
192.Useful Basic Regexp Methods
Function Description
--------------- ----------------------------------------------------------------------
MatchString(s) This method returns true if the string s matches the compiled pattern.
FindStringIndex(s) This method returns an int slice containing the location for the left-
most match made by the compiled pattern in the string s. A nil result
indicates that no matches were made.
FindAllStringIndex(s, max) This method returns a slice of int slices that contain the location for all
the matches made by the compiled pattern in the string s. A nil result
indicates that no matches were made.
FindString(s) This method returns a string containing the left-most match made by the
compiled pattern in the string s. An empty string will be returned if no
match is made.
FindAllString(s, max) This method returns a string slice containing the matches made by the
compiled pattern in the string s. The int argument max specifies the
maximum number of matches, with -1 specifying no limit. A nil result is
returned if there are no matches.
Split(s, max) This method splits the string s using matches from the compiled pattern
as separators and returns a slice containing the split substrings.
example:
package main
import (
"fmt"
"regexp"
)
func getSubstring(s string, indices []int) string {
return string(s[indices[0]:indices[1]])
}
func main() {
pattern := regexp.MustCompile("K[a-z]{4}|[A-z]oat")
description := "Kayak. A boat for one person."
firstIndex := pattern.FindStringIndex(description)
allIndices := pattern.FindAllStringIndex(description, -1)
fmt.Println("First index", firstIndex[0], "-", firstIndex[1],
"=", getSubstring(description, firstIndex))
for i, idx := range allIndices {
fmt.Println("Index", i, "=", idx[0], "-",
idx[1], "=", getSubstring(description, idx))
}
}
Output:
First index 0 - 5 = Kayak
Index 0 = 0 - 5 = Kayak
Index 1 = 9 - 13 = boat
████████████████████████████████████████████████████████████████████████
193.FindString and FindAllString methods
If you dont need to know the location of the matches, then the FindString and FindAllString
methods are more useful because their results are the substrings matched by the regular expression
Getting Match Substrings
example:
package main
import (
"fmt"
"regexp"
)
func main() {
pattern := regexp.MustCompile("K[a-z]{4}|[A-z]oat")
description := "Kayak. A boat for one person."
firstMatch := pattern.FindString(description)
allMatches := pattern.FindAllString(description, -1)
fmt.Println("First match:", firstMatch)
for i, m := range allMatches {
fmt.Println("Match", i, "=", m)
}
}
Output:
First match: Kayak
Match 0 = Kayak
Match 1 = boat
████████████████████████████████████████████████████████████████████████
194.Splitting Strings Using a Regular Expression
The Split method splits a string using the matches made by a regular expression, which can provide a more
flexible alternative to the splitting functions
example:
package main
import (
"fmt"
"regexp"
)
func main() {
pattern := regexp.MustCompile(" |boat|one")
description := "Kayak. A boat for one person."
split := pattern.Split(description, -1)
for _, s := range split {
if s != "" {
fmt.Println("Substring:", s)
}
}
}
Output:
Substring: Kayak.
Substring: A
Substring: for
Substring: person.
example:
package main
import (
"fmt"
"regexp"
)
func main() {
pattern := regexp.MustCompile(" |boat|one")
description := "Kayak. A boat | | | | for one person."
split := pattern.Split(description, -2)
for _, s := range split {
if s != "" {
fmt.Println("Substring:", s)
}
}
}
Output:
Substring: Kayak.
Substring: A
Substring: |
Substring: |
Substring: |
Substring: |
Substring: for
Substring: person.
████████████████████████████████████████████████████████████████████████
195.Subexpressions
Subexpressions allow parts of a regular expression to be accessed, which can make it easier to extract
substrings from within a matched region.
The pattern in this example matches a specific sentence structure.
example:
package main
import (
"fmt"
"regexp"
)
func main() {
pattern := regexp.MustCompile("A [A-z]* for [A-z]* person")
description := "Kayak. A boat for one person."
str := pattern.FindString(description)
fmt.Println("Match:", str)
}
Output:
Match: A boat for one person
████████████████████████████████████████████████████████████████████████
196.FindStringSubmatch method
The FindStringSubmatch method performs the same
task as FindString, but also includes the substrings matched by the expressions in its result.
example:
package main
import (
"fmt"
"regexp"
)
func main() {
pattern := regexp.MustCompile("A ([A-z]*) for ([A-z]*) person")
description := "Kayak. A boat for one person."
subs := pattern.FindStringSubmatch(description)
for _, s := range subs {
fmt.Println("Match:", s)
}
}
Output:
Match: A boat for one person
Match: boat
Match: one
████████████████████████████████████████████████████████████████████████
197.The Regexp Methods for Subexpressions
Name Description
--------------------------- ------------------------------------------------------------------
FindStringSubmatch(s) This method returns a slice containing the first match made by the
pattern and the text for the subexpressions that the pattern defines.
FindAllStringSubmatch(s, max) This method returns a slice containing all the matches and the text
for the subexpressions. The int argument is used to specify the
maximum number of matches. A value of -1 specifies all matches.
FindStringSubmatchIndex(s) This method is equivalent to FindStringSubmatch but returns
indices rather than substrings.
FindAllStringSubmatchIndex
(s, max) This method is equivalent to FindAllStringSubmatch but returns
indices rather than substrings.
NumSubexp() This method returns the number of subexpressions.
SubexpIndex(name) This method returns the index of the subexpression with the
specified name or -1 if there is no such subexpression.
SubexpNames() This method returns the names of the subexpressions, expressed in
the order in which they are defined.
████████████████████████████████████████████████████████████████████████
198.SubexpIndex(name) method
the syntax for assigning names to subexpressions is awkward: within the parentheses, a question mark,
followed by an uppercase P, followed by the name within angle brackets.
pattern := regexp.MustCompile("A (?P<type>[A-z]*) for (?P<capacity>[A-z]*) person")
The subexpressions are given the names type and capacity. The SubexpIndex method returns the
position of a named subexpression in the results, which allows me to get the substrings matched by the type
and capacity subexpressions.
example:
package main
import (
"fmt"
"regexp"
)
func main() {
pattern := regexp.MustCompile(
"A (?P<type>[A-z]*) for (?P<capacity>[A-z]*) person")
description := "Kayak. A boat for one person."
subs := pattern.FindStringSubmatch(description)
for _, name := range []string { "type", "capacity" } {
fmt.Println(name, "=", subs[pattern.SubexpIndex(name)])
}
}
Output:
type = boat
capacity = one
████████████████████████████████████████████████████████████████████████
199.Replacing Substrings Using a Regular Expression
Name Description
----------------------------- ---------------------------------------------------------
ReplaceAllString(s, template) This method replaces the matched portion of the string s with the
specified template, which is expanded before it is included in the result to
incorporate subexpressions.
ReplaceAllLiteralString(s, sub) This method replaces the matched portion of the string s with the
specified content, which is included in the result without being expanded
for subexpressions.
ReplaceAllStringFunc(s, func) This method replaces the matched portion of the string s with the result
produced by the specified function.
████████████████████████████████████████████████████████████████████████
200.ReplaceAllString method
The result from the ReplaceAllString method is a string with the replaced content.
Notice that the template is responsible for only part of the result from the ReplaceAllString method,
The first part of the description string—the word Kayak, followed by a period and a space, is
not matched by the regular expression and is included in the result without being modified.
■ Tip
Use the ReplaceAllLiteralString method if you want to replace content without the new
substring being interpreted for subexpressions.
example:
package main
import (
"fmt"
"regexp"
)
func main() {
pattern := regexp.MustCompile(
"A (?P<type>[A-z]*) for (?P<capacity>[A-z]*) person")
description := "Kayak. A boat for one person."
template := "(type: ${type}, capacity: ${capacity})"
replaced := pattern.ReplaceAllString(description, template)
fmt.Println(replaced)
}
Output:
Kayak. (type: boat, capacity: one).
████████████████████████████████████████████████████████████████████████
201.ReplaceAllStringFunc method
The ReplaceAllStringFunc method replaces the matched section of a string
with content generated by a function
example:
package main
import (
"fmt"
"regexp"
)
func main() {
pattern := regexp.MustCompile(
"A (?P<type>[A-z]*) for (?P<capacity>[A-z]*) person")
description := "Kayak. A boat for one person."
replaced := pattern.ReplaceAllStringFunc(description, func(s string) string {
return "This is the replacement content"
})
fmt.Println(replaced)
}
Output:
Kayak. This is the replacement content.
████████████████████████████████████████████████████████████████████████
202.Formatting and Scanning Strings
Formatting is the process of composing a new string from one or more data values,
while scanning is the process of parsing values from a string.
What are they?
Formatting is the process of composing values into a string. Scanning is the process
of parsing a string for the values it contains.
Why are they useful?
Formatting a string is a common requirement and is used to produce strings for
everything from logging and debugging to presenting the user with information.
Scanning is useful for extracting data from strings, such as from HTTP requests or
user input.
How are they used?
Both sets of features are provided through functions defined in the fmt package.
Are there any pitfalls or limitations?
The templates used to format strings can be hard to read, and there is no built-in
function that allows a formatted string to be created to which a newline character
is appended automatically.
Are there any alternatives?
Larger amounts of text and HTML content can be generated using the template features.
████████████████████████████████████████████████████████████████████████
203.fmt package
The fmt package provides functions for composing and writing strings.
Some of these functions use writers, which are part of the Go support for input/output.
The Basic fmt Functions for Composing and Writing Strings.
Name Description
-------------------------- --------------------------------------------------------------------
Print(...vals) This function accepts a variable number of arguments and writes out their
values to the standard out. Spaces are added between values that are not
strings.
Println(...vals) This function accepts a variable number of arguments and writes out
their values to the standard out, separated by spaces and followed by a
newline character.
Fprint(writer, ...vals) This function writes out a variable number of arguments to the specified writer,
Spaces are added between values that are not strings.
Fprintln(writer, ...vals) This function writes out a variable number of arguments to the specified writer
followed by a newline character. Spaces are added between all values.
████████████████████████████████████████████████████████████████████████
204.Println and Fprintln functions
The Println and Fprintln functions add spaces between all the values, but the
Print and Fprint functions only add spaces between values that are not strings.
since the Print function adds spaces only between pairs of nonstring values, the results are different.
example:
package main
import "fmt"
func main() {
fmt.Println("Product:", 1234 , "Price:", 5555)
fmt.Print("Product:", 1234 , "Price:", 5555, "\n")
}
Output:
Product: 1234 Price: 5555
Product:1234Price:5555
████████████████████████████████████████████████████████████████████████
205.fmt.Printf
The Printf function accepts a template string and a series of values. The template is scanned for verbs,
which are denoted by the percentage sign (the % character) followed by a format specifier.
The first verb is %v, and it specifies the default representation for a type. For a string value, for example,
%v simply includes the string in the output. The %4.2f verb specifies the format for a floating-point value,
with 4 digits before the decimal point and 2 digits after. The values for the template verbs are taken from the
remaining arguments, used in the order they are specified. For the example, this means the %v verb is used
to format the Product.Name value, and the %4.2f verb is used to format the Product.Price value. These
values are formatted, inserted into the template string, and written out to the console.
example:
package main
import "fmt"
type Product struct {
Name, Category string
Price float64
}
var Kayak = Product {
Name: "Kayak",
Category: "Watersports",
Price: 275,
}
var Products = []Product {
{ "Kayak", "Watersports", 279 },
{ "Lifejacket", "Watersports", 49.95 },
{ "Soccer Ball", "Soccer", 19.50 },
{ "Corner Flags", "Soccer", 34.95 },
{ "Stadium", "Soccer", 79500 },
{ "Thinking Cap", "Chess", 16 },
{ "Unsteady Chair", "Chess", 75 },
{ "Bling-Bling King", "Chess", 1200 },
}
func main() {
fmt.Printf("Product: %v, Price: $%4.2f", Kayak.Name, Kayak.Price)
}
Output:
Product: Kayak, Price: $275.00
████████████████████████████████████████████████████████████████████████
206.The fmt Functions for Formatting Strings
Name Description
--------------------------- -----------------------------------------
Sprintf(t, ...vals) This function returns a string, which is created by processing the template t.
The remaining arguments are used as values for the template verbs.
Printf(t, ...vals) This function creates a string by processing the template t.
The remaining arguments are used as values for the template verbs.
The string is written to the standard out.
Fprintf(writer, t, ...vals) This function creates a string by processing the template t.
The remaining arguments are used as values for the template verbs.
The string is written to a Writer, which is described in Chapter 20.
Errorf(t, ...values) This function creates an error by processing the template t.
The remaining arguments are used as values for the template verbs. The
result is an error value whose Error method returns the formatted string.
████████████████████████████████████████████████████████████████████████
207.fmt.Sprintf
Both of the formatted strings in this example use the %v value,
which writes out values in their default form.
example:
package main
import "fmt"
type Product struct {
Name, Category string
Price float64
}
var Kayak = Product{
Name: "Kayak",
Category: "Watersports",
Price: 275,
}
var Products = []Product{
{"Kayak", "Watersports", 279},
{"Lifejacket", "Watersports", 49.95},
{"Soccer Ball", "Soccer", 19.50},
{"Corner Flags", "Soccer", 34.95},
{"Stadium", "Soccer", 79500},
{"Thinking Cap", "Chess", 16},
{"Unsteady Chair", "Chess", 75},
{"Bling-Bling King", "Chess", 1200},
}
func getProductName(index int) (name string, err error) {
if len(Products) > index {
name = fmt.Sprintf("Name of product: %v", Products[index].Name)
} else {
err = fmt.Errorf("error for index %v", index)
}
return
}
func main() {
name, _ := getProductName(1)
fmt.Println(name)
_, err := getProductName(10)
fmt.Println(err.Error())
}
Output:
Name of product: Lifejacket
error for index 10
████████████████████████████████████████████████████████████████████████
208.the General-Purpose Formatting Verbs
The general-purpose verbs can be used to display any value.
The Formatting Verbs for Any Value
Verb Description
------ -----------------------------------------------
%v This verb displays the default format for the value. Modifying the verb with a plus sign (%+v) includes
field names when writing out struct values.
%#v This verb displays a value in a format that could be used to re-create the value in a Go code file.
%T This verb displays the Go type of a value.
example:
package main
import "fmt"
type Product struct {
Name, Category string
Price float64
}
var Kayak = Product{
Name: "Kayak",
Category: "Watersports",
Price: 275,
}
var Products = []Product{
{"Kayak", "Watersports", 279},
{"Lifejacket", "Watersports", 49.95},
{"Soccer Ball", "Soccer", 19.50},
{"Corner Flags", "Soccer", 34.95},
{"Stadium", "Soccer", 79500},
{"Thinking Cap", "Chess", 16},
{"Unsteady Chair", "Chess", 75},
{"Bling-Bling King", "Chess", 1200},
}
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
Printfln("Value: %v", Kayak)
Printfln("Go syntax: %#v", Kayak)
Printfln("Type: %T", Kayak)
}
Output:
Value: {Kayak Watersports 275}
Go syntax: main.Product{Name:"Kayak", Category:"Watersports", Price:275}
Type: main.Product
████████████████████████████████████████████████████████████████████████
209.Controlling Struct Formatting
Go has a default format for all data types that the %v verb relies on.
For structs, the default value lists the field values within curly braces.
The default verb can be modified with a plus sign to include the field names in the output.
example:
package main
import "fmt"
type Product struct {
Name, Category string
Price float64
}
var Kayak = Product{
Name: "Kayak",
Category: "Watersports",
Price: 275,
}
var Products = []Product{
{"Kayak", "Watersports", 279},
{"Lifejacket", "Watersports", 49.95},
{"Soccer Ball", "Soccer", 19.50},
{"Corner Flags", "Soccer", 34.95},
{"Stadium", "Soccer", 79500},
{"Thinking Cap", "Chess", 16},
{"Unsteady Chair", "Chess", 75},
{"Bling-Bling King", "Chess", 1200},
}
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
Printfln("Value: %v", Kayak)
Printfln("Value with fields: %+v", Kayak)
}
Output:
Value: {Kayak Watersports 275}
Value with fields: {Name:Kayak Category:Watersports Price:275}
████████████████████████████████████████████████████████████████████████
210.Custom Struct Format
example:
package main
import "fmt"
type Product struct {
Name, Category string
Price float64
}
var Kayak = Product{
Name: "Kayak",
Category: "Watersports",
Price: 275,
}
var Products = []Product{
{"Kayak", "Watersports", 279},
{"Lifejacket", "Watersports", 49.95},
{"Soccer Ball", "Soccer", 19.50},
{"Corner Flags", "Soccer", 34.95},
{"Stadium", "Soccer", 79500},
{"Thinking Cap", "Chess", 16},
{"Unsteady Chair", "Chess", 75},
{"Bling-Bling King", "Chess", 1200},
}
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func (p Product) String() string {
return fmt.Sprintf("Product: %v, Price: $%4.2f", p.Name, p.Price)
}
func main() {
Printfln("Value: %v", Kayak)
Printfln("Value with fields: %+v", Kayak)
}
Output:
Value: Product: Kayak, Price: $275.00
Value with fields: Product: Kayak, Price: $275.00
████████████████████████████████████████████████████████████████████████
211.Formating Arrays, Slices, Maps
When arrays and slices are represented as strings, the output is a set of square brackets, within which
are the individual elements, like this:
example:
[Kayak Lifejacket Paddle]
Notice that no commas are separating the elements. When maps are represented as strings, the key-
value pairs are displayed within square brackets, preceded by the map keyword, like this:
example:
map[1:Kayak 2:Lifejacket 3:Paddle]
████████████████████████████████████████████████████████████████████████
212.Integer Formatting Verbs
Verb Description
------ ------------------------------------
%b This verb displays an integer value as a binary string.
%d This verb displays an integer value as a decimal string. This is the default format for integer
values, applied when the %v verb is used.
%o, %O These verbs display an integer value as an octal string. The %O verb adds the 0o prefix.
%x, %X These verbs display an integer value as a hexadecimal string. The letters A–F are displayed in
lowercase by the %x verb and in uppercase by the %X verb.
example:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
number := 250
Printfln("Binary: %b", number)
Printfln("Decimal: %d", number)
Printfln("Octal: %o, %O", number, number)
Printfln("Hexadecimal: %x, %X", number, number)
}
Output:
Binary: 11111010
Decimal: 250
Octal: 372, 0o372
Hexadecimal: fa, FA
████████████████████████████████████████████████████████████████████████
213.Floating-Point Formatting Verbs
The Formatting Verbs for Floating-Point Values
Verb Description
----- ------------------------------------
%b This verb displays a floating-point value with an exponent and without a decimal place.
%e, %E These verbs display a floating-point value with an exponent and a decimal place. The %e uses a
lowercase exponent indicator, while %E uses an uppercase indicator.
%f, %F These verbs display a floating-point value with a decimal place but no exponent. The %f and %F
verbs produce the same output.
%g This verb adapts to the value it displays. The %e format is used for values with large exponents,
and the %f format is used otherwise. This is the default format, applied when the %v verb is
used.
%G This verb adapts to the value it displays. The %E format is used for values with large exponents,
and the %f format is used otherwise.
%x, %X These verbs display a floating-point value in hexadecimal notation, with lowercase (%x) or
uppercase (%X) letters.
example:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
func main() {
number := 279.00
Printfln("Decimalless with exponent: %b", number)
Printfln("Decimal with exponent: %e", number)
Printfln("Decimal without exponent: %f", number)
Printfln("Hexadecimal: %x, %X", number, number)
}
Output:
Decimalless with exponent: 4908219906392064p-44
Decimal with exponent: 2.790000e+02
Decimal without exponent: 279.000000
Hexadecimal: 0x1.17p+08, 0X1.17P+08
████████████████████████████████████████████████████████████████████████
214.Controlling Formatting
example:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
func main() {
number := 279.00
Printfln("Decimal without exponent: >>%8.2f<<", number)
}
Output:
Decimal without exponent: >> 279.00<<
████████████████████████████████████████████████████████████████████████
215.Specifying Precision
example:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
func main() {
number := 279.00
Printfln("Decimal without exponent: >>%.2f<<", number)
}
Output:
Decimal without exponent: >>279.00<<
████████████████████████████████████████████████████████████████████████
216.The Formatting Verb Modifiers
Modifier Description
-------- --------------------------
+ This modifier (the plus sign) always prints a sign, positive or negative, for numeric values.
0 This modifier uses zeros, rather than spaces, as padding when the width is greater than the
number of characters required to display the value.
- This modifier (the subtracts symbol) adds padding to the right of the number, rather than
the left.
example:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
func main() {
number := 279.00
Printfln("Sign: >>%+.2f<<", number)
Printfln("Zeros for Padding: >>%07.2f<<", number)
Printfln("Zeros for Padding: >>%08.2f<<", number)
Printfln("Right Padding: >>%-8.2f<<", number)
}
Output:
Sign: >>+279.00<<
Zeros for Padding: >>0279.00<<
Zeros for Padding: >>00279.00<<
Right Padding: >>279.00 <<
████████████████████████████████████████████████████████████████████████
217.The Formatting Verbs for Strings and Runes
Verb Description
---- ----------------------------------------------------
%s This verb displays a string. This is the default format, applied when the %v verb is used.
%c This verb displays a character. Care must be taken to avoid slicing strings into individual bytes, as
explained in the text after the table.
%U This verb displays a character in the Unicode format so that the output begins with U+ followed by
a hexadecimal character code.
example:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
func main() {
name := "Kayak"
Printfln("String: %s", name)
Printfln("Character: %c", []rune(name)[0])
Printfln("Unicode: %U", []rune(name)[0])
}
Output:
String: Kayak
Character: K
Unicode: U+004B
████████████████████████████████████████████████████████████████████████
218.The bool Formatting Verb
Verb Description
---- -------------
%t This verb formats bool values and displays true or false.
example:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
func main() {
name := "Kayak"
Printfln("Bool: %t", len(name) > 1)
Printfln("Bool: %t", len(name) > 100)
}
Output:
Bool: true
Bool: false
████████████████████████████████████████████████████████████████████████
219.The Pointer Formatting Verb
Verb Description
---- -----------------
%p This verb displays a hexadecimal representation of the pointer's storage location.
example:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
func main() {
name := "Kayak"
Printfln("Pointer: %p", &name)
}
Output:
Pointer: 0xc00004a240
████████████████████████████████████████████████████████████████████████
220.The fmt Functions for Scanning Strings
Name Description
------------------------ ---------------------------------------------------------------------
Scan(...vals) This function reads text from the standard in and stores the space-
separated values into specified arguments. Newlines are treated as
spaces, and the function reads until it has received values for all of its
arguments. The result is the number of values that have been read and an
error that describes any problems.
Scanln(...vals) This function works in the same way as Scan but stops reading when it
encounters a newline character.
Scanf(template, ...vals) This function works in the same way as Scan but uses a template string
to select the values from the input it receives.
Fscan(reader, ...vals) This function reads space-separated values from the specified reader,
which is described in Chapter 20. Newlines are treated as spaces, and
the function returns the number of values that have been read and an
error that describes any problems.
Fscanln(reader, ...vals) This function works in the same way as Fscan but stops reading when it
encounters a newline character.
Fscanf(reader, template, ...vals) This function works in the same way as Fscan but uses a template to
select the values from the input it receives.
Sscan(str, ...vals) This function scans the specified string for space-separated values,
which are assigned to the remaining arguments. The result is the
number of values scanned and an error that describes any problems.
Sscanf(str, template, ...vals) This function works in the same way as Sscan but uses a template to
select values from the string.
Sscanln(str, template, ...vals) This function works in the same way as Sscanf but stops scanning the
string as soon as a newline character is encountered.
████████████████████████████████████████████████████████████████████████
221.Scanning a String
example:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
func main() {
var name string
var category string
var price float64
fmt.Print("Enter text to scan: ")
n, err := fmt.Scan(&name, &category, &price)
if (err == nil) {
Printfln("Scanned %v values", n)
Printfln("Name: %v, Category: %v, Price: %.2f", name, category, price)
} else {
Printfln("Error: %v", err.Error())
}
}
Output:
Enter text to scan: asd asd 123
Scanned 3 values
Name: asd, Category: asd, Price: 123.00
████████████████████████████████████████████████████████████████████████
222.Scanln Function
example:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
func main() {
var name string
var category string
var price float64
fmt.Print("Enter text to scan: ")
n, err := fmt.Scanln(&name, &category, &price)
if (err == nil) {
Printfln("Scanned %v values", n)
Printfln("Name: %v, Category: %v, Price: %.2f", name, category, price)
} else {
Printfln("Error: %v", err.Error())
}
}
Output:
Error: unexpected newline
████████████████████████████████████████████████████████████████████████
223.Sscan Function
example:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
func main() {
var name string
var category string
var price float64
source := "Lifejacket Watersports 48.95"
n, err := fmt.Sscan(source, &name, &category, &price)
if (err == nil) {
Printfln("Scanned %v values", n)
Printfln("Name: %v, Category: %v, Price: %.2f", name, category, price)
} else {
Printfln("Error: %v", err.Error())
}
}
Output:
Scanned 3 values
Name: Lifejacket, Category: Watersports, Price: 48.95
████████████████████████████████████████████████████████████████████████
224.Scanning Template
The template ignores the term Product, skipping that part of the string and
allowing the scanning to begin with the next term.
example:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
func main() {
var name string
var category string
var price float64
source := "Product Lifejacket Watersports 48.95"
template := "Product %s %s %f"
n, err := fmt.Sscanf(source, template, &name, &category, &price)
if (err == nil) {
Printfln("Scanned %v values", n)
Printfln("Name: %v, Category: %v, Price: %.2f", name, category, price)
} else {
Printfln("Error: %v", err.Error())
}
}
Output:
Scanned 3 values
Name: Lifejacket, Category: Watersports, Price: 48.95
████████████████████████████████████████████████████████████████████████
225.Putting Math Functions and Sorting Data in Context
What are they?
The math functions allow common calculations to be performed. Random
numbers are numbers generated in a sequence that is difficult to predict. Sorting
is the process of placing a sequence of values in a predetermined order.
Why are they useful?
These are features that are used throughout development.
How are they used?
These features are provided in the math, math/rand, and sort packages.
Are there any pitfalls or limitations?
Unless initialized with a seed value, the numbers produced by the math/rand
package are not random.
Are there any alternatives?
You could implement both sets of features from scratch, although these packages
are provided so that this is not required.
████████████████████████████████████████████████████████████████████████
226.Useful Functions from the math Package
Name Description
-------------- ----------------------------------------------------------
Abs(val) This function returns the absolute value of a float64 value, meaning the distance
from zero without considering direction.
Ceil(val) This function returns the smallest integer that is equal to or greater than the specified
float64 value. The result is also a float64 value, even though it represents an integer
number.
Copysign(x, y) This function returns a float64 value, which is the absolute value of x with the sign of y.
Floor(val) This function returns the largest integer that is smaller or equal to the specified
float64 value. The result is also a float64 value, even though it represents an integer number.
Max(x, y) This function returns whichever of the specified float64 value is the largest.
Min(x, y) This function returns whichever of the specified float64 value is smallest.
Mod(x, y) This function returns the remainder of x/y.
Pow(x, y) This function returns x raised to the exponent y.
Round(val) This function rounds the specified value to the nearest integer, rounding half values
up. The result is a float64 value, even though it represents an integer.
RoundToEven(val) This function rounds the specified value to the nearest integer, rounding half values
to the nearest even number. The result is a float64 value, even though it represents
an integer.
example:
package main
import "math"
func main() {
val1 := 279.00
val2 := 48.95
Printfln("Abs: %v", math.Abs(val1))
Printfln("Ceil: %v", math.Ceil(val2))
Printfln("Copysign: %v", math.Copysign(val1, -5))
Printfln("Floor: %v", math.Floor(val2))
Printfln("Max: %v", math.Max(val1, val2))
Printfln("Min: %v", math.Min(val1, val2))
Printfln("Mod: %v", math.Mod(val1, val2))
Printfln("Pow: %v", math.Pow(val1, 2))
Printfln("Round: %v", math.Round(val2))
Printfln("RoundToEven: %v", math.RoundToEven(val2))
}
Output:
Abs: 279
Ceil: 49
Copysign: -279
Floor: 48
Max: 279
Min: 48.95
Mod: 34.249999999999986
Pow: 77841
Round: 49
RoundToEven: 49
████████████████████████████████████████████████████████████████████████
227.The Limit Constants
Name Description
---------------- -----------------------------
MaxInt8 These constants represent the largest and smallest values that can be stored
MinInt8 using an int8.
MaxInt16 These constants represent the largest and smallest values that can be stored
MinInt16 using an int16.
MaxInt32 These constants represent the largest and smallest values that can be stored
MinInt32 using an int32.
MaxInt64 These constants represent the largest and smallest values that can be stored
MinInt64 using an int64.
MaxUint8 This constant represents the largest value that can be represented using a
uint8.The smallest value is zero.
MaxUint16 This constant represents the largest value that can be represented using a
uint16. The smallest value is zero.
MaxUint32 This constant represents the largest value that can be represented using a
uint32. The smallest value is zero.
MaxUint64 This constant represents the largest value that can be represented using a
uint64. The smallest value is zero.
MaxFloat32 These constants represent the largest values that can be represented using
MaxFloat64 float32 and float64 values.
SmallestNonzeroFloat32 These constants represent the smallest nonzero values that can be
SmallestNonzeroFloat32 represented using float32 and float64 values.
████████████████████████████████████████████████████████████████████████
228.Generating Random Numbers
Useful math/rand Functions
Name Description
-------------------- ---------------------------------------
Seed(s) This function sets the seed value using the specified int64 value.
Float32() This function generates a random float32 value between 0 and 1.
Float64() This function generates a random float64 value between 0 and 1.
Int() This function generates a random int value.
Intn(max) This function generates a random int smaller than a specified value, as
described after the table.
UInt32() This function generates a random uint32 value.
UInt64() This function generates a random uint64 value.
Shuffle(count, func) This function is used to randomize the order of elements, as described after
the table.
████████████████████████████████████████████████████████████████████████
229.rand.Int()
example:
package main
import "math/rand"
func main() {
for i := 0; i < 5; i++ {
Printfln("Value %v : %v", i, rand.Int())
}
}
Output:
Value 0 : 5577006791947779410
Value 1 : 8674665223082153551
Value 2 : 6129484611666145821
Value 3 : 4037200794235010051
Value 4 : 3916589616287113937
████████████████████████████████████████████████████████████████████████
230.rand.Seed()
Example:
package main
import (
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
for i := 0; i < 5; i++ {
Printfln("Value %v : %v", i, rand.Int())
}
}
Output:
Value 0 : 8113726196145714527
Value 1 : 3479565125812279859
Value 2 : 8074476402089812953
Value 3 : 3916870404047362448
Value 4 : 8226545715271170755
████████████████████████████████████████████████████████████████████████
231.rand.Intn(rangeNumber)
Generating a Random Number Within a Specific Range
example:
package main
import (
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
for i := 0; i < 5; i++ {
Printfln("Value %v : %v", i, rand.Intn(10))
}
}
Output:
Value 0 : 7
Value 1 : 5
Value 2 : 4
Value 3 : 0
Value 4 : 7
████████████████████████████████████████████████████████████████████████
232.Specifying a Lower Bound
example:
package main
import (
"math/rand"
"time"
)
func IntRange(min, max int) int {
return rand.Intn(max - min) + min
}
func main() {
rand.Seed(time.Now().UnixNano())
for i := 0; i < 5; i++ {
Printfln("Value %v : %v", i, IntRange(10, 20))
}
}
Output:
Value 0 : 10
Value 1 : 19
Value 2 : 11
Value 3 : 10
Value 4 : 17
████████████████████████████████████████████████████████████████████████
233.Shuffling Elements
example:
package main
import (
"math/rand"
"time"
"fmt"
)
var names = []string{"Alice", "Bob", "Charlie", "Dora", "Edith"}
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
rand.Seed(time.Now().UnixNano())
rand.Shuffle(len(names), func(first, second int) {
names[first], names[second] = names[second], names[first]
})
for i, name := range names {
Printfln("Index %v: Name: %v", i, name)
}
}
Output:
Index 0: Name: Edith
Index 1: Name: Dora
Index 2: Name: Charlie
Index 3: Name: Alice
Index 4: Name: Bob
████████████████████████████████████████████████████████████████████████
234.The Basic Functions for Sorting
Name Description
-------------------- -------------------------------------------
Float64s(slice) This function sorts a slice of float64 values. The elements are sorted in place.
Float64sAreSorted(slice) This function returns true if the elements in the specified float64 slice are in order.
Ints(slice) This function sorts a slice of int values. The elements are sorted in place.
IntsAreSorted(slice) This function returns true if the elements in the specified int slice are in order.
Strings(slice) This function sorts a slice of string values. The elements are sorted in place.
StringsAreSorted(slice) This function returns true if the elements in the specified string slice are in order.
████████████████████████████████████████████████████████████████████████
235.Sorting Slices
example:
package main
import (
"fmt"
"sort"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
ints := []int{9, 4, 2, -1, 10}
Printfln("Ints: %v", ints)
sort.Ints(ints)
Printfln("Ints Sorted: %v", ints)
floats := []float64{279, 48.95, 19.50}
Printfln("Floats: %v", floats)
sort.Float64s(floats)
Printfln("Floats Sorted: %v", floats)
strings := []string{"Kayak", "Lifejacket", "Stadium"}
Printfln("Strings: %v", strings)
if !sort.StringsAreSorted(strings) {
sort.Strings(strings)
Printfln("Strings Sorted: %v", strings)
} else {
Printfln("Strings Already Sorted: %v", strings)
}
}
Output:
Ints: [9 4 2 -1 10]
Ints Sorted: [-1 2 4 9 10]
Floats: [279 48.95 19.5]
Floats Sorted: [19.5 48.95 279]
Strings: [Kayak Lifejacket Stadium]
Strings Already Sorted: [Kayak Lifejacket Stadium]
████████████████████████████████████████████████████████████████████████
236.Creating a Sorted Copy of a Slice
example:
package main
import (
"fmt"
"sort"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
ints := []int{9, 4, 2, -1, 10}
sortedInts := make([]int, len(ints))
copy(sortedInts, ints)
sort.Ints(sortedInts)
Printfln("Ints: %v", ints)
Printfln("Ints Sorted: %v", sortedInts)
}
Output:
Ints: [9 4 2 -1 10]
Ints Sorted: [-1 2 4 9 10]
████████████████████████████████████████████████████████████████████████
237.The Functions for Searching Sorted Data
Name Description
---------------------- ----------------------------------
SearchInts(slice, val) This function searches the sorted slice for the specified int value. The
result is the index of the specified value or, if the value is not found, the
index at which the value can be inserted while maintaining the sorted order.
SearchFloat64s(slice, val) This function searches the sorted slice for the specified float64 value.
The result is the index of the specified value or, if the value is not found,
the index at which the value can be inserted while maintaining the
sorted order.
SearchStrings(slice, val) This function searches the sorted slice for the specified string value.
The result is the index of the specified value or, if the value is not found,
the index at which the value can be inserted while maintaining the
sorted order.
Search(count, testFunc) This function invokes the test function for the specified number of
elements. The result is the index for which the function returns true.
If there is no match, then the result is the index at which the specified
value can be inserted to maintain the sorted order.
████████████████████████████████████████████████████████████████████████
238.sort.SearchInts()
When a value is located, the functions return its position in the slice.
But unusually, if the value is not found, then the result is the position it can be
inserted while maintaining the sort order.
example:
package main
import (
"fmt"
"sort"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
ints := []int{9, 1, 2, -1, 10,5}
sortedInts := make([]int, len(ints))
copy(sortedInts, ints)
sort.Ints(sortedInts)
Printfln("Ints: %v", ints)
Printfln("Ints Sorted: %v", sortedInts)
// ===========================================
indexOf4 := sort.SearchInts(sortedInts, 4)
Printfln("Index of 4: %v", indexOf4)
indexOf3 := sort.SearchInts(sortedInts, 3)
Printfln("Index of 3: %v", indexOf3)
}
Output:
Ints: [9 1 2 -1 10 5]
Ints Sorted: [-1 1 2 5 9 10]
Index of 4: 3
Index of 3: 3
████████████████████████████████████████████████████████████████████████
239.sort.SearchInts() bool returned
example:
package main
import (
"fmt"
"sort"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
ints := []int{9, 1, 2, -1, 10,5}
sortedInts := make([]int, len(ints))
copy(sortedInts, ints)
sort.Ints(sortedInts)
Printfln("Ints: %v", ints)
Printfln("Ints Sorted: %v", sortedInts)
// =========================
indexOf4:= sort.SearchInts(sortedInts, 4)
indexOf3 := sort.SearchInts(sortedInts, 3)
Printfln("Index of 4: %v (present: %v)", indexOf4, sortedInts[indexOf4] == 4)
Printfln("Index of 3: %v (present: %v)", indexOf3, sortedInts[indexOf3] == 3)
}
Output:
Ints: [9 1 2 -1 10 5]
Ints Sorted: [-1 1 2 5 9 10]
Index of 4: 3 (present: false)
Index of 3: 3 (present: false)
████████████████████████████████████████████████████████████████████████
240.The Methods Defined by the sort.Interface Interface
Name Description
---------- ----------------
Len() This method returns the number of items that will be sorted.
Less(i, j) This method returns true if the element at index i should appear in the sorted sequence
before the element j. If Less(i,j) and Less(j, i) are both false, then the elements are
considered equal.
Swap(i, j) This method swaps the elements at the specified indices.
████████████████████████████████████████████████████████████████████████
241.The Functions for Sorting Types That Implement Interface
Name Description
---------- ---------------------
Sort(data) This function uses the methods described in 240 Number to sort the specified data.
Stable(data) This function uses the methods described in 240 Number to sort the specified data
without changing the order of elements of equal value.
IsSorted(data) This function returns true if the data is in sorted order.
Reverse(data) This function reverses the order of the data.
example:
main.go:
package main
import (
"fmt"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
products := []Product{
{"Kayak", 279},
{"Lifejacket", 49.95},
{"Soccer Ball", 19.50},
}
ProductSlices(products)
for _, p := range products {
Printfln("Name: %v, Price: %.2f", p.Name, p.Price)
}
}
Name: Soccer Ball, Price: 19.50
Name: Lifejacket, Price: 49.95
Name: Kayak, Price: 279.00
████████████████████████████████████████████████████████████████████████
242.Sorting with a Comparison Function
go mod init
AND
main.go File:
package main
import (
"fmt"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
products := []Product{
{"Kayak", 279},
{"Lifejacket", 49.95},
{"Soccer Ball", 19.50},
}
SortWith(products, func(p1, p2 Product) bool {
return p1.Name < p2.Name
})
for _, p := range products {
Printfln("Name: %v, Price: %.2f", p.Name, p.Price)
}
}
productsort.go File:
package main
import "sort"
type Product struct {
Name string
Price float64
}
type ProductSlice []Product
func ProductSlices(p []Product) {
sort.Sort(ProductSlice(p))
}
func ProductSlicesAreSorted(p []Product) {
sort.IsSorted(ProductSlice(p))
}
func (products ProductSlice) Len() int {
return len(products)
}
func (products ProductSlice) Less(i, j int) bool {
return products[i].Price < products[j].Price
}
func (products ProductSlice) Swap(i, j int) {
products[i], products[j] = products[j], products[i]
}
type ProductComparison func(p1, p2 Product) bool
type ProductSliceFlex struct {
ProductSlice
ProductComparison
}
func (flex ProductSliceFlex) Less(i, j int) bool {
return flex.ProductComparison(flex.ProductSlice[i], flex.ProductSlice[j])
}
func SortWith(prods []Product, f ProductComparison) {
sort.Sort(ProductSliceFlex{ prods, f})
}
████████████████████████████████████████████████████████████████████████
243.Putting Dates, Times, and Durations in Context
What are they?
The features provided by the time package are used to represent
specific moments in time and intervals or durations.
Why are they useful?
These features are useful in any application that needs to deal with
calendaring or alarm and for the development of any feature that
requires delays or notifications in the future.
How are they used?
The time package defines data types for representing dates and
individual units of time and functions for manipulating them. There
are also features integrated into the Go channel system.
Are there any pitfalls or limitations?
Dates can be complex, and care must be taken to deal with calendar
and time zone issues.
Are there any alternatives?
These are optional features, and their use is not required.
████████████████████████████████████████████████████████████████████████
244.The Functions in the time Package for Creating Time Values
Name Description
-------- -----------------------------
Now() This function creates a Time representing the current moment in time.
Date(y, m, d, h, min, sec, nsec, loc) This function creates a Time representing a specified moment in time, which is
expressed by the year, month, day, hour, minute, second, nanosecond, and Location
arguments. (The Location type is described in the “Parsing Time Values from Strings”
section.)
Unix(sec, nsec) This function creates a Time value from the number of seconds and nanoseconds since
January 1, 1970, UTC, commonly known as Unix time.
████████████████████████████████████████████████████████████████████████
245.The Methods for Accessing Time Components
Name Description
-------- ----------------------------
Date() This method returns the year, month, and day components. The year and day are
expressed as int values and the month as a Month value.
Clock() This method returns the hour, minutes, and seconds components of the Time.
Year() This method returns the year component, expressed as an int.
YearDay() This method returns the day of the year, expressed as an int between 1 and 366 (to accommodate leap years).
Month() This method returns the month component, expressed using the Month type.
Day() This method returns the day of the month, expressed as an int.
Weekday() This method returns the day of the week, expressed as a Weekday.
Hour() This method returns the hour of the day, expressed as an int between 0 and 23.
Minute() This method returns the number of minutes elapsed into the hour of the day, expressed as an int between 0 and 59.
Second() This method returns the number of seconds elapsed into the minute of the hour, expressed as an int between 0 and 59.
Nanosecond() This method returns the number of nanoseconds elapsed into the second of the minute,
expressed as an int between 0 and 999,999,999.
████████████████████████████████████████████████████████████████████████
246.The Types Used to Describe Time Components
Name Description
------- ------------------------------------------------------------------------
Month This type represents a month, and the time package defines constant values for the English-
language month names: January, February, etc. The Month type defines a String method that
uses these names when formatting strings.
Weekday This type represents a day of the week, and the time package defines constant values for the
English-language weekday names: Sunday, Monday, etc. The Weekday type defines a String
method that uses these names when formatting strings.
████████████████████████████████████████████████████████████████████████
247.Creating Time Values
example:
package main
import "time"
func PrintTime(label string, t *time.Time) {
Printfln("%s: Day: %v: Month: %v Year: %v",
label, t.Day(), t.Month(), t.Year())
}
func main() {
current := time.Now()
specific := time.Date(1995, time.June, 9, 0, 0, 0, 0, time.Local)
unix := time.Unix(1433228090, 0)
PrintTime("Current", ¤t)
PrintTime("Specific", &specific)
PrintTime("UNIX", &unix)
}
Output:
Current: Day: 15: Month: September Year: 2023
Specific: Day: 9: Month: June Year: 1995
UNIX: Day: 2: Month: June Year: 2015
example:
package main
import "time"
func PrintTime(label string, t *time.Time) {
Printfln("%s: Day: %v: Month: %v Year: %v",
label, t.Day(), t.Month(), t.Year())
}
func main() {
current := time.Now()
specific := time.Date(1993, time.June, 0, 0, 0, 0, 0, time.Local)
unix := time.Unix(0, 0)
PrintTime("Current", ¤t)
PrintTime("Specific", &specific)
PrintTime("UNIX", &unix)
}
Output:
Current: Day: 15: Month: September Year: 2023
Specific: Day: 31: Month: May Year: 1993
UNIX: Day: 31: Month: December Year: 1969
████████████████████████████████████████████████████████████████████████
248.The Time Method for Creating Formatted Strings
Name Description
-------------- --------------------
Format(layout) This method returns a formatted string, which is created using the specified layout.
example:
package main
import (
"fmt"
"time"
)
func PrintTime(label string, t *time.Time) {
layout := "Day: 02 Month: Jan Year: 2006"
fmt.Println(label, t.Format(layout))
}
func main() {
current := time.Now()
specific := time.Date(1993, time.June, 0, 0, 0, 0, 0, time.Local)
unix := time.Unix(0, 0)
PrintTime("Current", ¤t)
PrintTime("Specific", &specific)
PrintTime("UNIX", &unix)
}
Output:
Current Day: 16 Month: Sep Year: 2023
Specific Day: 31 Month: May Year: 1993
UNIX Day: 31 Month: Dec Year: 1969
████████████████████████████████████████████████████████████████████████
249.The Layout Constants Defined by the time Package
Name Reference Date Format
----------- ----------------------------------
ANSIC Mon Jan _2 15:04:05 2006
UnixDate Mon Jan _2 15:04:05 MST 2006
RubyDate Mon Jan 02 15:04:05 -0700 2006
RFC822 02 Jan 06 15:04 MST
RFC822Z 02 Jan 06 15:04 -0700
RFC850 Monday, 02-Jan-06 15:04:05 MST
RFC1123 Mon, 02 Jan 2006 15:04:05 MST
RFC1123Z Mon, 02 Jan 2006 15:04:05 -0700
RFC3339 2006-01-02T15:04:05Z07:00
RFC3339Nano 2006-01-02T15:04:05.999999999Z07:00
Kitchen 3:04PM
Stamp Jan _2 15:04:05
StampMilli Jan _2 15:04:05.000
StampMicro Jan _2 15:04:05.000000
StampNano Jan _2 15:04:05.000000000
████████████████████████████████████████████████████████████████████████
250.The time Package Functions for Parsing Strings into Time Values
Name Description
-------------------------------------- -----------------------------------
Parse(layout, str) This function parses a string using the specified layout to create a Time value.
An error is returned to indicate problems parsing the string.
ParseInLocation(layout, str, location) This function parses a string, using the specified layout and using the
Location if no time zone is included in the string. An error is returned to
indicate problems parsing the string.
example:
package main
import (
"fmt"
"time"
)
func PrintTime(label string, t *time.Time) {
fmt.Println(label, t.Format(time.RFC822Z))
}
func main() {
dates := []string{
"09 Jun 95 00:00 GMT",
"02 Jun 15 00:00 GMT",
}
for _, d := range dates {
time, err := time.Parse(time.RFC822, d)
if err == nil {
PrintTime("Parsed", &time)
} else {
Printfln("Error: %s", err.Error())
}
}
}
Output:
Parsed 09 Jun 95 00:00 +0000
Parsed 02 Jun 15 00:00 +0000
████████████████████████████████████████████████████████████████████████
251.time.ParseInLocation()
example:
package main
import (
"fmt"
"time"
)
func PrintTime(label string, t *time.Time) {
//layout := "Day: 02 Month: Jan Year: 2006"
fmt.Println(label, t.Format(time.RFC822Z))
}
func main() {
layout := "02 Jan 06 15:04"
date := "09 Jun 95 19:30"
london, lonerr := time.LoadLocation("Europe/London")
newyork, nycerr := time.LoadLocation("America/New_York")
Tehran, TehranErr := time.LoadLocation("Asia/Tehran")
if lonerr == nil && nycerr == nil && TehranErr == nil{
TehranTime, _ := time.ParseInLocation(layout, date, Tehran)
PrintTime("Tehran:",&TehranTime)
nolocation, _ := time.Parse(layout, date)
PrintTime("No location:", &nolocation)
londonTime, _ := time.ParseInLocation(layout, date, london)
PrintTime("London:", &londonTime)
newyorkTime, _ := time.ParseInLocation(layout, date, newyork)
PrintTime("New York:", &newyorkTime)
} else {
fmt.Println(lonerr.Error(), nycerr.Error())
}
}
Output:
Tehran: 09 Jun 95 19:30 +0430
No location: 09 Jun 95 19:30 +0000
London: 09 Jun 95 19:30 +0100
New York: 09 Jun 95 19:30 -0400
████████████████████████████████████████████████████████████████████████
252.The Functions for Creating Locations
Name Description
------------------------- -----------------------------------------
LoadLocation(name) This function returns a *Location for the specified name and an
error that indicates any problems.
LoadLocationFromTZData(name,data) This function returns a *Location from a byte slice that contains a
formatted time zone database.
FixedZone(name, offset) This function returns a *Location that always uses the specified
name and offset from UTC.
The place names are defined in the IANA time zone database,
https://www.iana.org/time-zones ,
and are listed by
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
████████████████████████████████████████████████████████████████████████
253.Embedding The Time Zone Database
example:
package main
import (
"fmt"
"time"
)
func PrintTime(label string, t *time.Time) {
//layout := "Day: 02 Month: Jan Year: 2006"
fmt.Println(label, t.Format(time.RFC822Z))
}
func main() {
layout := "02 Jan 06 15:04"
date := "09 Jun 95 19:30"
Tehran, TehranErr := time.LoadLocation("Asia/Tehran")
local, _ := time.LoadLocation("Local")
if TehranErr == nil{
TehranTime, _ := time.ParseInLocation(layout, date, Tehran)
PrintTime("Tehran:",&TehranTime)
localTime, _ := time.ParseInLocation(layout, date, local)
PrintTime("Local:", &localTime)
nolocation, _ := time.Parse(layout, date)
PrintTime("No location:", &nolocation)
} else {
fmt.Println(TehranErr.Error())
}
}
Output:
Tehran: 09 Jun 95 19:30 +0430
Local: 09 Jun 95 19:30 -0400
No location: 09 Jun 95 19:30 +0000
████████████████████████████████████████████████████████████████████████
254.Specifying Time Zones
The arguments to the FixedZone function are a name and the number of seconds offset from UTC. This
example creates three fixed time zones, one of which is an hour ahead of UTC, one of which is four hours
behind, and one of which has no offset.
example:
package main
import (
"fmt"
"time"
)
func PrintTime(label string, t *time.Time) {
//layout := "Day: 02 Month: Jan Year: 2006"
fmt.Println(label, t.Format(time.RFC822Z))
}
func main() {
layout := "02 Jan 06 15:04"
date := "09 Jun 95 19:30"
london := time.FixedZone("BST", 1*60*60)
newyork := time.FixedZone("EDT", -4*60*60)
local := time.FixedZone("Local", 0)
nolocation, _ := time.Parse(layout, date)
londonTime, _ := time.ParseInLocation(layout, date, london)
newyorkTime, _ := time.ParseInLocation(layout, date, newyork)
localTime, _ := time.ParseInLocation(layout, date, local)
PrintTime("No location:", &nolocation)
PrintTime("London:", &londonTime)
PrintTime("New York:", &newyorkTime)
PrintTime("Local:", &localTime)
}
Output:
No location: 09 Jun 95 19:30 +0000
London: 09 Jun 95 19:30 +0100
New York: 09 Jun 95 19:30 -0400
Local: 09 Jun 95 19:30 +0000
████████████████████████████████████████████████████████████████████████
255.The Methods for Working with Time Values
Name Description
---------------- -------------------------------------------------
Add(duration) This method adds the specified Duration to the Time and returns the result.
Sub(time) This method returns a Duration that expresses the difference between the Time on
which the method has been called and the Time provided as the argument.
AddDate(y, m, d) This method adds the specified number of years, months, and days to the Time and
returns the result.
After(time) This method returns true if the Time on which the method has been called occurs
after the Time provided as the argument.
Before(time) This method returns true if the Time on which the method has been called occurs
before the Time provided as the argument.
Equal(time) This method returns true if the Time on which the method has been called is equal
to the Time provided as the argument.
IsZero() This method returns true if the Time on which the method has been called
represents the zero-time instant, which is January 1, year 1, 00:00:00 UTC.
In(loc) This method returns the Time value, expressed in the specified Location.
Location() This method returns the Location that is associated with the Time, effectively
allowing a time to be expressed in a different time zone.
Round(duration) This method rounds the Time to the nearest interval represented by a Duration
value.
Truncate(duration) This method rounds the Time down to the nearest interval represented by a
Duration value.
████████████████████████████████████████████████████████████████████████
256.time.Parse()
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
t, err := time.Parse(time.RFC822, "09 Jun 95 04:59 BST")
if err == nil {
Printfln("After: %v", t.After(time.Now()))
Printfln("Round: %v", t.Round(time.Hour))
Printfln("Truncate: %v", t.Truncate(time.Hour))
} else {
fmt.Println(err.Error())
}
}
Output:
After: false
Round: 1995-06-09 05:00:00 +0100 BST
Truncate: 1995-06-09 04:00:00 +0100 BST
████████████████████████████████████████████████████████████████████████
257.Comparing Time Values
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
t1, _ := time.Parse(time.RFC822Z, "09 Jun 95 04:59 +0100")
t2, _ := time.Parse(time.RFC822Z, "08 Jun 95 23:59 -0400")
Printfln("Equal Method: %v", t1.Equal(t2))
Printfln("Equality Operator: %v", t1 == t2)
}
Output:
Equal Method: true
Equality Operator: false
████████████████████████████████████████████████████████████████████████
258.The Duration Constants in the time Package
Name Description
------------ ----------------------------------------
Hour This constant represents 1 hour.
Minute This constant represents 1 minute.
Second This constant represents 1 second.
Millisecond This constant represents 1 millisecond.
Microsecond This constant represents 1 microsecond.
Nanosecond This constant represents 1 nanosecond.
████████████████████████████████████████████████████████████████████████
259.The Duration Methods
Name Description
---------------- ---------------------------------------------
Hours() This method returns a float64 that represents the Duration in hours.
Minutes() This method returns a float64 that represents the Duration in minutes.
Seconds() This method returns a float64 that represents the Duration in seconds.
Milliseconds() This method returns an int64 that represents the Duration in milliseconds.
Microseconds() This method returns an int64 that represents the Duration in microseconds.
Nanoseconds() This method returns an int64 that represents the Duration in nanoseconds.
Round(duration) This method returns a Duration, which is rounded to the nearest multiple of the
specified Duration.
Truncate(duration) This method returns a Duration, which is rounded down to the nearest multiple of
the specified Duration.
████████████████████████████████████████████████████████████████████████
260.Hours() - Minutes() - Seconds() - rounded.Hours() - rounded.Minutes()
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
var d time.Duration = time.Hour + (30 * time.Minute)
Printfln("Hours: %v", d.Hours())
Printfln("Mins: %v", d.Minutes())
Printfln("Seconds: %v", d.Seconds())
Printfln("Millseconds: %v", d.Milliseconds())
rounded := d.Round(time.Hour)
Printfln("Rounded Hours: %v", rounded.Hours())
Printfln("Rounded Mins: %v", rounded.Minutes())
trunc := d.Truncate(time.Hour)
Printfln("Truncated Hours: %v", trunc.Hours())
Printfln("Rounded Mins: %v", trunc.Minutes())
}
Output:
Hours: 1.5
Mins: 90
Seconds: 5400
Millseconds: 5400000
Rounded Hours: 2
Rounded Mins: 120
Truncated Hours: 1
Rounded Mins: 60
████████████████████████████████████████████████████████████████████████
261.The time Functions for Creating Duration Values relative to a Time
Name Description
----------- ----------------------------------------
Since(time) This function returns a Duration expressing the elapsed time since the specified Time value.
Until(time) This function returns a Duration expressing the elapsed time until the specified Time value.
████████████████████████████████████████████████████████████████████████
262.time.Until(time) - Since(time)
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
toYears := func(d time.Duration) int {
return int(d.Hours() / (24 * 365))
}
future := time.Date(2051, 0, 0, 0, 0, 0, 0, time.Local)
past := time.Date(1965, 0, 0, 0, 0, 0, 0, time.Local)
Printfln("this year is %v.",time.Now().Year())
Printfln("Future: %v is %v.", toYears(time.Until(future)), future.Year())
Printfln("Past: %v is %v.", toYears(time.Since(past)), past.Year())
}
Output:
this year is 2023.
Future: 27 is 2050.
Past: 58 is 1964.
████████████████████████████████████████████████████████████████████████
263.time.ParseDuration function
This function returns a Duration and an error, indicating if there were problems
parsing the specified string.
The format of the strings supported by the ParseDuration function is a sequence of number values
followed by the unit indicators:
Unit Description
----- --------------------
h This unit denotes hours.
m This unit denotes minutes.
s This unit denotes seconds.
ms This unit denotes milliseconds.
us or μs These units denotes microseconds.
ns This unit denotes nanoseconds.
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func main() {
d, err := time.ParseDuration("1h30m")
if err == nil {
Printfln("Hours: %v", d.Hours())
Printfln("Mins: %v", d.Minutes())
Printfln("Seconds: %v", d.Seconds())
Printfln("Millseconds: %v", d.Milliseconds())
} else {
fmt.Println(err.Error())
}
}
Output:
Hours: 1.5
Mins: 90
Seconds: 5400
Millseconds: 5400000
████████████████████████████████████████████████████████████████████████
264.Using the Time Features for Goroutines and Channels
The time package provides a small set of functions that are useful for working with goroutines and channels.
The time Package Functions:
Name Description
---------------------- ----------------------------------------
Sleep(duration) This function pauses the current goroutine for at least the specified duration.
AfterFunc(duration,func) This function executes the specified function in its own goroutine after the
specified duration. The result is a *Timer, whose Stop method can be used to
cancel the execution of the function before the duration elapses.
After(duration) This function returns a channel that blocks for the specified duration and then
yields a Time value. See the “Receiving Timed Notifications” section for details.
Tick(duration) This function returns a channel that periodically sends a Time value, where the
period is specified as a duration.
████████████████████████████████████████████████████████████████████████
265.time.Sleep(time.Second * 1)
Pausing a Goroutine
The Sleep function pauses execution of the current goroutine for a specified duration
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func writeToChannel(channel chan<- string) {
names := []string{"Alice", "Bob", "Charlie", "Dora"}
for _, name := range names {
channel <- name
time.Sleep(time.Second * 1)
}
close(channel)
}
func main() {
nameChannel := make(chan string)
go writeToChannel(nameChannel)
for name := range nameChannel {
Printfln("Read name: %v", name)
}
}
Output:
Read name: Alice
// 1 second delaying
Read name: Bob
// 1 second delaying
Read name: Charlie
// 1 second delaying
Read name: Dora
████████████████████████████████████████████████████████████████████████
266.time.AfterFunc() function
The AfterFunc function is used to defer the execution of a function for a specified period
Deferring a Function:
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func writeToChannel(channel chan<- string) {
names := []string{"Alice", "Bob", "Charlie", "Dora"}
for _, name := range names {
channel <- name
// time.Sleep(time.Second * 1)
}
close(channel)
}
func main() {
nameChannel := make(chan string)
time.AfterFunc(time.Second*5, func() {
writeToChannel(nameChannel)
})
for name := range nameChannel {
Printfln("Read name: %v", name)
}
}
Output:
// It waits for 5 seconds and then continues the program.
Read name: Alice
Read name: Bob
Read name: Charlie
Read name: Dora
████████████████████████████████████████████████████████████████████████
267.time.After(time.Second * 2)
The result from the After function is a channel that carries Time values. The channel blocks for the
specified duration, when a Time value is sent, indicating the duration has passed. In this example, the
value sent over the channel acts as a signal and is not used directly, which is why it is assigned to the blank
identifier, like this:
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func writeToChannel(channel chan<- string) {
Printfln("Waiting for initial duration...")
_ = <-time.After(time.Second * 2)
Printfln("Initial duration elapsed.")
names := []string{"Alice", "Bob", "Charlie", "Dora"}
for _, name := range names {
channel <- name
time.Sleep(time.Second * 1)
}
close(channel)
}
func main() {
nameChannel := make(chan string)
go writeToChannel(nameChannel)
for name := range nameChannel {
Printfln("Read name: %v", name)
}
}
Output:
Waiting for initial duration... // Wait for 2 seconds
Initial duration elapsed.
Read name: Alice // wait for 1 second
Read name: Bob // wait for 1 second
Read name: Charlie // wait for 1 second
Read name: Dora // wait for 1 second
████████████████████████████████████████████████████████████████████████
268.time.Sleep(time.Second * 3) with select statement
Using a Timeout in a Select Statement
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func writeToChannel(channel chan<- string) {
Printfln("Waiting for initial duration...")
_ = <-time.After(time.Second * 2)
Printfln("Initial duration elapsed.")
names := []string{"Alice", "Bob", "Charlie", "Dora"}
for _, name := range names {
channel <- name
time.Sleep(time.Second * 3)
}
close(channel)
}
func main() {
nameChannel := make(chan string)
go writeToChannel(nameChannel)
channelOpen := true
for channelOpen {
Printfln("Starting channel read")
select {
case name, ok := <-nameChannel:
if !ok {
channelOpen = false
} else {
Printfln("Read name: %v", name)
}
case <-time.After(time.Second * 2):
Printfln("Timeout")
}
}
}
Output:
Starting channel read
Waiting for initial duration...
Timeout
Starting channel read
Initial duration elapsed.
Read name: Alice
Starting channel read
Timeout
Starting channel read
Read name: Bob
Starting channel read
Timeout
Starting channel read
Read name: Charlie
Starting channel read
Timeout
Starting channel read
Read name: Dora
Starting channel read
Timeout
Starting channel read
████████████████████████████████████████████████████████████████████████
269.NewTimer(duration)
This function returns a *Timer with the specified period.
Caution Be careful when stopping a timer.
The timer's channel is not closed, which means that reads from
the channel will continue to block even after the timer has stopped.
The Methods Defined by the Timer Struct:
Name Description
------------ -------------------------------------------
C This field returns the channel over which the Time will send its Time value.
Stop() This method stops the timer. The result is a bool that will be true if the timer has been
stopped and false if the timer had already sent its message.
Reset(duration) This method stops a timer and resets it so that its interval is the specified Duration.
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func writeToChannel(channel chan<- string) {
timer := time.NewTimer(time.Minute * 10)
go func() {
time.Sleep(time.Second * 2)
Printfln("Resetting timer")
timer.Reset(time.Second)
}()
Printfln("Waiting for initial duration...")
<-timer.C
Printfln("Initial duration elapsed.")
names := []string{"Alice", "Bob", "Charlie", "Dora"}
for _, name := range names {
channel <- name
}
close(channel)
}
func main() {
nameChannel := make(chan string)
go writeToChannel(nameChannel)
for name := range nameChannel {
Printfln("Read name: %v", name)
}
}
Output:
Waiting for initial duration...
Resetting timer
Initial duration elapsed.
Read name: Alice
Read name: Bob
Read name: Charlie
Read name: Dora
████████████████████████████████████████████████████████████████████████
270.time.Tick(time.Second)
Receiving Recurring Notifications دریافت اعلان های مکرر
The Tick function returns a channel over which Time values are sent at a specified interval
Tick is a convenience wrapper for NewTicker providing access to the ticking channel only.
While Tick is useful for clients that have no need to shut down the Ticker,
be aware that without a way to shut it down the underlying
Ticker cannot be recovered by the garbage collector; it "leaks".
Unlike NewTicker, Tick will return nil if d <= 0.
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func writeToChannel(nameChannel chan<- string) {
names := []string{"Alice", "Bob", "Charlie", "Dora"}
tickChannel := time.Tick(time.Second)
index := 0
for {
<-tickChannel
nameChannel <- names[index]
index++
if index == len(names) {
index = 0
}
}
}
func main() {
nameChannel := make(chan string)
go writeToChannel(nameChannel)
for name := range nameChannel {
Printfln("Read name: %v", name)
}
}
Output:
Read name: Alice
Read name: Bob
Read name: Charlie
Read name: Dora
Read name: Alice
Read name: Bob
Read name: Charlie
Read name: Dora
Read name: Alice
Read name: Bob
Read name: Charlie
Read name: Dora
...
████████████████████████████████████████████████████████████████████████
271.NewTicker(duration)
The result of the NewTicker function is a pointer to a Ticker struct, which defines the field and methods
The time Function for Creating a Ticker:
Name Description
--------------------- ---------------------------------
NewTicker(duration) This function returns a *Ticker with the specified period.
The Field and Methods Defined by the Ticker Struct:
Name Description
---------------- --------------------------------
C This field returns the channel over which the Ticker will send its Time values.
Stop() This method stops the ticker (but does not close the channel returned by the C field).
Reset(duration) This method stops a ticker and resets it so that its interval is the specified Duration.
████████████████████████████████████████████████████████████████████████
272.Creating a Ticker in the main.go
example:
package main
import (
"fmt"
"time"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
func writeToChannel(nameChannel chan<- string) {
names := []string{"Alice", "Bob", "Charlie", "Dora"}
ticker := time.NewTicker(time.Second / 10)
index := 0
for {
<-ticker.C
nameChannel <- names[index]
index++
if index == len(names) {
ticker.Stop()
close(nameChannel)
break
}
}
}
func main() {
nameChannel := make(chan string)
go writeToChannel(nameChannel)
for name := range nameChannel {
Printfln("Read name: %v", name)
}
}
Output:
// This is printed after milliseconds
Read name: Alice
Read name: Bob
Read name: Charlie
Read name: Dora
████████████████████████████████████████████████████████████████████████
273.Reading and Writing Data
These interfaces are used wherever data is read or written, which means that any
source or destination for data can be treated in much the same way so that writing data to a file, for example,
is just the same as writing data to a network connection.
What are they?
These interfaces define the basic methods required to read and write data.
Why are they useful?
This approach means that just about any data source can be used
in the same way, while still allowing specialized features to be
defined using the composition features.
How is it used?
The io package defines these interfaces, but the implementations
are available from a range of other packages
Are there any pitfalls or limitations?
These interfaces don't entirely hide the detail of sources or
destinations for data and additional methods are often required,
provided by interfaces that build on Reader and Writer.
Are there any alternatives?
The use of these interfaces is optional, but they are hard to avoid
because they are used throughout the standard library.
The Reader and Writer interfaces are defined by the io package and
provide abstract ways to read and write data,
without being tied to where the data is coming from or going to.
Preparing for This Chapter:
product.go:
package main
type Product struct {
Name, Category string
Price float64
}
var Kayak = Product{
Name: "Kayak",
Category: "Watersports",
Price: 279,
}
var Products = []Product{
{"Kayak", "Watersports", 279},
{"Lifejacket", "Watersports", 49.95},
{"Soccer Ball", "Soccer", 19.50},
{"Corner Flags", "Soccer", 34.95},
{"Stadium", "Soccer", 79500},
{"Thinking Cap", "Chess", 16},
{"Unsteady Chair", "Chess", 75},
{"Bling-Bling King", "Chess", 1200},
}
printer.go:
package main
import (
"fmt"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
main.go:
package main
func main() {
Printfln("Product: %v, Price : %v", Kayak.Name, Kayak.Price)
}
████████████████████████████████████████████████████████████████████████
274.The Reader interface
The Reader interface doesn't include any detail about where data comes from or how it is obtained—it
just defines the Read method. The details are left to the types that implement the interface, and there are
reader implementations in the standard library for different data sources.
defines a single method:
Name Description
-------------- -------------------------
Read(byteSlice) This method reads data into the specified []byte. The method returns the number of
bytes that were read, expressed as an int, and an error.
████████████████████████████████████████████████████████████████████████
275.io.Reader package
example:
package main
import (
"io"
"strings"
)
func processData(reader io.Reader) {
b := make([]byte, 2)
for {
count, err := reader.Read(b)
if count > 0 {
Printfln("Read %v bytes: %v", count, string(b[0:count]))
}
if err == io.EOF {
break
}
}
}
func main() {
r := strings.NewReader("Kayak")
processData(r)
}
Output:
Read 2 bytes: Ka
Read 2 bytes: ya
Read 1 bytes: k
████████████████████████████████████████████████████████████████████████
276.Writer interface
Write(byteSlice)
This method writes the data from the specified byte slice. The method returns the
number of bytes that were written and an error. The error will be non-nil if the
number of bytes written is less than the length of the slice.
The Writer interface doesn't include any details of how the written data is stored, transmitted, or
processed, all of which is left to the types that implement the interface.
████████████████████████████████████████████████████████████████████████
277.io.Writer
example:
package main
import(
"io"
"strings"
"asd/asd"
)
func processData(reader io.Reader, writer io.Writer) {
b := make([]byte, 2)
for {
count, err := reader.Read(b)
if count > 0 {
writer.Write(b[0:count])
asd.Printfln("Read %v bytes: %v", count, string(b[0:count]))
}
if err == io.EOF {
break
}
}
}
func main() {
r := strings.NewReader("Kayak")
var builder strings.Builder
processData(r, &builder)
asd.Printfln("String builder contents: %s", builder.String())
}
Output:
Read 2 bytes: Ka
Read 2 bytes: ya
Read 1 bytes: k
String builder contents: Kayak
████████████████████████████████████████████████████████████████████████
278.io.EOF
The io package defines a special error named EOF,
which is used to signal when the Reader reaches the end of the data.
If the error result from the Read function is equal to the EOF error,
then I break out of the for
loop that has been reading data from the Reader:
...
if err == io.EOF {
break
}
...
████████████████████████████████████████████████████████████████████████
279.the Utility Functions for Readers and Writers
Functions in the io Package for Readng and Writing Data
Name Description
------------------------ -------------------------------------------------------
Copy(w, r) This function copies data from a Reader to a Writer until EOF is returned or
another error is encountered. The results are the number of bytes copies and an
error used to describe any problems.
CopyBuffer(w, r, buffer) This function performs the same task as Copy but reads the data into the
specified buffer before it is passed to the Writer.
CopyN(w, r, count) This function copies count bytes from the Reader to the Writer. The results are
the number of bytes copies and an error used to describe any problems.
ReadAll(r) This function reads data from the specified Reader until EOF is reached. The
results are a byte slice containing the read data and an error, which is used to
describe any problems.
ReadAtLeast(r, byteSlice, min) This function reads at least the specified number of bytes from the reader,
placing them into the byte slice. An error is reported if fewer bytes than specified
are read.
ReadFull(r, byteSlice) This function fills the specified byte slice with data. The result is the number
of bytes read and an error. An error will be reported if EOF was encountered
before enough bytes to fill the slice were read.
WriteString(w, str) This function writes the specified string to a writer.
████████████████████████████████████████████████████████████████████████
280.io.Copy(writer, reader)
example:
package main
import(
"io"
"asd/asd"
"strings"
)
func processData(reader io.Reader, writer io.Writer) {
count, err := io.Copy(writer, reader)
if (err == nil) {
asd.Printfln("Read %v bytes", count)
} else {
asd.Printfln("Error: %v", err.Error())
}
}
func main() {
r := strings.NewReader("Kayak .")
var builder strings.Builder
processData(r, &builder)
asd.Printfln("String builder contents: %s", builder.String())
}
Output:
Read 7 bytes
String builder contents: Kayak .
████████████████████████████████████████████████████████████████████████
281.The io Package Functions for Specialized Readers and Writers
Name Description
----------- ----------------------------------------
Pipe() This function returns a PipeReader and a PipeWriter, which can be used to connect
functions that require a Reader and a Writer, as described in the “Using Pipes” section.
MultiReader(...readers) This function defines a variadic parameter that allows an arbitrary number of Reader
values to be specified. The result is a Reader that passes on the content from each of
its parameters in the sequence they are defined, as described in the “Concatenating
Multiple Readers” section.
MultiWriter(...writers) This function defines a variadic parameter that allows an arbitrary number of Writer
values to be specified. The result is a Writer that sends the same data to all the
specified writers, as described in the “Combining Multiple Writers” section.
LimitReader(r, limit) This function creates a Reader that will EOF after the specified number of bytes, as
described in the “Limiting Read Data” section.
████████████████████████████████████████████████████████████████████████
282.Pipe
Pipes are used to connect code that consumes data through a Reader
and code that produces code through a Writer.
The GenerateData function defines a Writer parameter, which it uses to write bytes from a string.
example:
data.go:
package main
import (
"io"
"asd/asd"
)
func GenerateData(writer io.Writer) {
data := []byte("Kayak, Lifejacket")
writeSize := 4
for i := 0; i < len(data); i += writeSize {
end := i + writeSize;
if (end > len(data)) {
end = len(data)
}
count, err := writer.Write(data[i: end])
asd.Printfln("Wrote %v byte(s): %v", count, string(data[i: end]))
if (err != nil) {
asd.Printfln("Error: %v", err.Error())
}
}
}
func ConsumeData(reader io.Reader) {
data := make([]byte, 0, 10)
slice := make([]byte, 2)
for {
count, err := reader.Read(slice)
if (count > 0) {
asd.Printfln("Read data: %v", string(slice[0:count]))
data = append(data, slice[0:count]...)
}
if (err == io.EOF) {
break
}
}
asd.Printfln("Read data: %v", string(data))
}
=====================================================================================
Notice the parentheses at the end of this statement. These are required when creating a goroutine for an
anonymous function, but it is easy to forget them.
main.go:
package main
import (
"io"
)
func main() {
pipeReader, pipeWriter := io.Pipe()
go func() {
GenerateData(pipeWriter)
pipeWriter.Close()
}()
ConsumeData(pipeReader)
}
=====================================================================================
The output highlights the fact that pipes are synchronous. The GenerateData function calls the writer's
Write method and then blocks until the data is read. This is why the first message in the output is from the
reader: the reader is consuming the data two bytes at a time, which means that two read operations are
required before the initial call to the Write method, which is used to send four bytes, completes, and the
message from the GenerateData function is displayed.
Output:
Read data: Ka
Read data: ya
Wrote 4 byte(s): Kaya
Read data: k,
Read data: L
Wrote 4 byte(s): k, L
Read data: if
Read data: ej
Wrote 4 byte(s): ifej
Read data: ac
Read data: ke
Wrote 4 byte(s): acke
Read data: t
Wrote 1 byte(s): t
Read data: Kayak, Lifejacket
████████████████████████████████████████████████████████████████████████
283.PipeReader and a PipeWriter
The io.Pipe function returns a PipeReader and a PipeWriter. The PipeReader and PipeWriter structs
implement the Closer interface
Name Description
------- -----------------------
Close() This method closes the reader or writer. The details are implementation specific, but, in
general, any subsequent reads from a closed Reader will return zero bytes and the EOF error,
while any subsequent writes to a closed Writer will return an error.
The PipeReader struct implements the Reader interface, which means I can use it as the argument to
the ConsumeData function. The ConsumeData function is executed in the main goroutine, which means that
the application won't exit until the function completes.
The effect is that data is written into the pipe using the PipeWriter and read from the pipe using the
PipeReader. When the GenerateData function is complete, the Close method is called on the PipeWriter,
which causes the next read by the PipeReader to produce EOF.
████████████████████████████████████████████████████████████████████████
284.io.MultiReader()
example:
main.go:
package main
import (
"io"
"strings"
)
func main() {
r1 := strings.NewReader("Kayak")
r2 := strings.NewReader("Lifejacket")
r3 := strings.NewReader("Canoe")
concatReader := io.MultiReader(r1, r2, r3)
ConsumeData(concatReader)
}
Output:
Read data: Ka
Read data: ya
Read data: k
Read data: Li
Read data: fe
Read data: ja
Read data: ck
Read data: et
Read data: Ca
Read data: no
Read data: e
Read data: KayakLifejacketCanoe
████████████████████████████████████████████████████████████████████████
285.io.MultiWriter()
example:
package main
import (
"io"
"strings"
"asd/asd"
)
func main() {
var w1 strings.Builder
var w2 strings.Builder
var w3 strings.Builder
combinedWriter := io.MultiWriter(&w1, &w2, &w3)
GenerateData(combinedWriter)
asd.Printfln("Writer #1: %v", w1.String())
asd.Printfln("Writer #2: %v", w2.String())
asd.Printfln("Writer #3: %v", w3.String())
}
Output:
Wrote 4 byte(s): Kaya
Wrote 4 byte(s): k, L
Wrote 4 byte(s): ifej
Wrote 4 byte(s): acke
Wrote 1 byte(s): t
Writer #1: Kayak, Lifejacket
Writer #2: Kayak, Lifejacket
Writer #3: Kayak, Lifejacket
████████████████████████████████████████████████████████████████████████
286.io.TeeReader(concatReader, &writer)
Echoing Reads to a Writer
The TeeReader function returns a Reader that echoes the data that it receives to a Writer.
example:
package main
import (
"asd/asd"
"io"
"strings"
)
func main() {
r1 := strings.NewReader("Kayak")
r2 := strings.NewReader("Lifejacket")
r3 := strings.NewReader("Canoe")
concatReader := io.MultiReader(r1, r2, r3)
var writer strings.Builder
teeReader := io.TeeReader(concatReader, &writer)
ConsumeData(teeReader)
asd.Printfln("Echo data: %v", writer.String())
}
Output:
Read data: Ka
Read data: ya
Read data: k
Read data: Li
Read data: fe
Read data: ja
Read data: ck
Read data: et
Read data: Ca
Read data: no
Read data: e
Read data: KayakLifejacketCanoe
Echo data: KayakLifejacketCanoe
████████████████████████████████████████████████████████████████████████
287.io.LimitReader(concatReader, 5)
The LimitReader function is used to restrict the amount of data that can be obtained from a Reader
example:
package main
import (
"io"
"strings"
)
func main() {
r1 := strings.NewReader("Kayak")
r2 := strings.NewReader("Lifejacket")
r3 := strings.NewReader("Canoe")
concatReader := io.MultiReader(r1, r2, r3)
limited := io.LimitReader(concatReader, 5)
ConsumeData(limited)
}
Output:
Read data: Ka
Read data: ya
Read data: k
Read data: Kayak
████████████████████████████████████████████████████████████████████████
288.bufio
The bufio package provides support for adding buffers to readers and writers.
example:
This code defined a struct type named CustomReader that acts as a wrapper around a Reader.
The implementation of the Read method generates output that reports how much data is read and
how many read operations are performed overall.
custom.go:
package main
import (
"io"
"asd/asd"
)
type CustomReader struct {
reader io.Reader
readCount int
}
func NewCustomReader(reader io.Reader) *CustomReader {
return &CustomReader{reader, 0}
}
func (cr *CustomReader) Read(slice []byte) (count int, err error) {
count, err = cr.reader.Read(slice)
cr.readCount++
asd.Printfln("Custom Reader: %v bytes", count)
if err == io.EOF {
asd.Printfln("Total Reads: %v", cr.readCount)
}
return
}
main.go:
package main
import (
"asd/asd"
"io"
"strings"
)
func main() {
text := "It was a boat. A small boat."
var reader io.Reader = NewCustomReader(strings.NewReader(text))
var writer strings.Builder
slice := make([]byte, 5)
for {
count, err := reader.Read(slice)
if count > 0 {
writer.Write(slice[0:count])
}
if err != nil {
break
}
}
asd.Printfln("Read data: %v", writer.String())
}
The NewCustomreader function is used to create a CustomReader that reads from a string and uses a for
loop to consume the data using a byte slice.
Output:
Custom Reader: 5 bytes
Custom Reader: 5 bytes
Custom Reader: 5 bytes
Custom Reader: 5 bytes
Custom Reader: 5 bytes
Custom Reader: 3 bytes
Custom Reader: 0 bytes
Total Reads: 7
Read data: It was a boat. A small boat.
████████████████████████████████████████████████████████████████████████
289.bufio Functions for Creating Buffered Readers
Name Description
---------------------- ---------------------------------
NewReader(r) This function returns a buffered Reader with the default buffer size (which is
4,096 bytes at the time of writing).
NewReaderSize(r, size) This function returns a buffered Reader with the specified buffer size.
████████████████████████████████████████████████████████████████████████
290.bufio.NewReader(reader)
The default buffer size is 4,096 bytes, which means that the buffered reader was able to read all the data
in a single read operation, plus an additional read to produce the EOF result. Introducing the buffer reduces
the overhead associated with the read operations, albeit at the cost of the memory used to buffer the data.
example:
package main
import (
"io"
"strings"
"bufio"
)
func main() {
text := "It was a boat. A small boat."
var reader io.Reader = NewCustomReader(strings.NewReader(text))
var writer strings.Builder
slice := make([]byte, 5)
reader = bufio.NewReader(reader)
for {
count, err := reader.Read(slice)
if (count > 0) {
writer.Write(slice[0:count])
}
if (err != nil) {
break
}
}
Printfln("Read data: %v", writer.String())
}
Output:
Custom Reader: 28 bytes
Custom Reader: 0 bytes
Total Reads: 2
Read data: It was a boat. A small boat.
████████████████████████████████████████████████████████████████████████
291.Additional Buffered Reader Methods
The NewReader and NewReaderSize functions return bufio.Reader values, which implement the io.
Reader interface and which can be used as drop-in wrappers for other types of Reader methods, seamlessly
introducing a read buffer.
████████████████████████████████████████████████████████████████████████
292.The Methods Defined by the Buffered Reader
Name Description
-------------- ------------------------------------------
Buffered() This method returns an int that indicates the number of bytes that can be read from the buffer.
Discard(count) This method discards the specified number of bytes.
Peek(count) This method returns the specified number of bytes without removing them from the
buffer, meaning they will be returned by subsequent calls to the Read method.
Reset(reader) This method discards the data in the buffer and performs subsequent reads from the
specified Reader.
Size() This method returns the size of the buffer, expressed int.
████████████████████████████████████████████████████████████████████████
293.buffered.Read(slice)
example:
package main
import (
"io"
"strings"
"bufio"
)
func main() {
text := "It was a boat. A small boat."
var reader io.Reader = NewCustomReader(strings.NewReader(text))
var writer strings.Builder
slice := make([]byte, 5)
buffered := bufio.NewReader(reader)
for {
count, err := buffered.Read(slice)
if (count > 0) {
Printfln("Buffer size: %v, buffered: %v",
buffered.Size(), buffered.Buffered())
writer.Write(slice[0:count])
}
if (err != nil) {
break
}
}
Printfln("Read data: %v", writer.String())
}
Output:
Custom Reader: 28 bytes
Buffer size: 4096, buffered: 23
Buffer size: 4096, buffered: 18
Buffer size: 4096, buffered: 13
Buffer size: 4096, buffered: 8
Buffer size: 4096, buffered: 3
Buffer size: 4096, buffered: 0
Custom Reader: 0 bytes
Total Reads: 2
Read data: It was a boat. A small boat.
████████████████████████████████████████████████████████████████████████
294.bufio Functions for Creating Buffered Writers
Name Description
----------------------- --------------------------------------
NewWriter(w) This function returns a buffered Writer with the default buffer size (which is
4,096 bytes at the time of writing).
NewWriterSize(w, size) This function returns a buffered Writer with the specified buffer size.
████████████████████████████████████████████████████████████████████████
295.Methods Defined by the bufio.Writer Struct
Name Description
-------------- -----------------------------------
Available() This method returns the number of available bytes in the buffer.
Buffered() This method returns the number of bytes that have been written to the buffer.
Flush() This method writes the contents of the buffer to the underlying Writer.
Reset(writer) This method discards the data in the buffer and performs subsequent writes to the
specified Writer.
Size() This method returns the capacity of the buffer in bytes.
████████████████████████████████████████████████████████████████████████
296.Defining a Custom Writer example
The NewCustomWriter constructor wraps a Writer with a CustomWriter struct, which reports on its
write operations.
custom.go:
package main
import (
"asd/asd"
"io"
)
type CustomReader struct {
reader io.Reader
readCount int
}
func NewCustomReader(reader io.Reader) *CustomReader {
return &CustomReader{reader, 0}
}
func (cr *CustomReader) Read(slice []byte) (count int, err error) {
count, err = cr.reader.Read(slice)
cr.readCount++
asd.Printfln("Custom Reader: %v bytes", count)
if err == io.EOF {
asd.Printfln("Total Reads: %v", cr.readCount)
}
return
}
type CustomWriter struct {
writer io.Writer
writeCount int
}
func NewCustomWriter(writer io.Writer) *CustomWriter {
return &CustomWriter{writer, 0}
}
func (cw *CustomWriter) Write(slice []byte) (count int, err error) {
count, err = cw.writer.Write(slice)
cw.writeCount++
asd.Printfln("Custom Writer: %v bytes", count)
return
}
func (cw *CustomWriter) Close() (err error) {
if closer, ok := cw.writer.(io.Closer); ok {
closer.Close()
}
asd.Printfln("Total Writes: %v", cw.writeCount)
return
}
main.go:
package main
import (
"strings"
"asd/asd"
)
func main() {
text := "It was a boat. A small boat."
var builder strings.Builder
var writer = NewCustomWriter(&builder)
for i := 0; true; {
end := i + 5
if end >= len(text) {
writer.Write([]byte(text[i:]))
break
}
writer.Write([]byte(text[i:end]))
i = end
}
asd.Printfln("Written data: %v", builder.String())
}
Output:
Custom Writer: 5 bytes
Custom Writer: 5 bytes
Custom Writer: 5 bytes
Custom Writer: 5 bytes
Custom Writer: 5 bytes
Custom Writer: 3 bytes
Written data: It was a boat. A small boat.
████████████████████████████████████████████████████████████████████████
297.Using a Buffered Writer in the main.go example
example:
main.go:
package main
import (
"strings"
"asd/asd"
"bufio"
)
func main() {
text := "It was a boat. A small boat."
var builder strings.Builder
var writer = bufio.NewWriterSize(NewCustomWriter(&builder), 20)
for i := 0; true; {
end := i + 5
if end >= len(text) {
writer.Write([]byte(text[i:]))
writer.Flush()
break
}
writer.Write([]byte(text[i:end]))
i = end
}
asd.Printfln("Written data: %v", builder.String())
}
Output:
Custom Writer: 20 bytes
Custom Writer: 8 bytes
Written data: It was a boat. A small boat.
████████████████████████████████████████████████████████████████████████
298.Scanning from a Reader
example:
main.go
package main
import (
"io"
"strings"
"asd/asd"
"fmt"
)
func scanFromReader(reader io.Reader, template string,
vals ...interface{}) (int, error) {
return fmt.Fscanf(reader, template, vals...)
}
func main() {
reader := strings.NewReader("Kayak Watersports $279.00")
var name, category string
var price float64
scanTemplate := "%s %s $%f"
_, err := scanFromReader(reader, scanTemplate, &name, &category, &price)
if err != nil {
asd.Printfln("Error: %v", err.Error())
} else {
asd.Printfln("Name: %v", name)
asd.Printfln("Category: %v", category)
asd.Printfln("Price: %.2f", price)
}
}
Output:
Name: Kayak
Category: Watersports
Price: 279.00
████████████████████████████████████████████████████████████████████████
299.Scanning Gradually اسکن به تدریج
example:
main.go
package main
import (
"io"
"strings"
"asd/asd"
"fmt"
)
func scanSingle(reader io.Reader, val interface{}) (int, error) {
return fmt.Fscan(reader, val)
}
func main() {
reader := strings.NewReader("Kayak Watersports $279.00")
for {
var str string
_, err := scanSingle(reader, &str)
if err != nil {
if err != io.EOF {
asd.Printfln("Error: %v", err.Error())
}
break
}
asd.Printfln("Value: %v", str)
}
}
Output:
Value: Kayak
Value: Watersports
Value: $279.00
████████████████████████████████████████████████████████████████████████
300.Writing Formatted Strings to a Writer
The fmt package also provides functions for writing formatted strings to a Writer
The writeFormatted function uses the fmt.Fprintf function to write a string formatted with a template
to a Writer.
example:
main.go:
package main
import (
"io"
"strings"
"fmt"
)
func writeFormatted(writer io.Writer, template string, vals ...interface{}) {
fmt.Fprintf(writer, template, vals...)
}
func main() {
var writer strings.Builder
template := "Name: %s, Category: %s, Price: $%.2f"
writeFormatted(&writer, template, "Kayak", "Watersports", float64(279))
fmt.Println(writer.String())
}
Output:
Name: Kayak, Category: Watersports, Price: $279.00
████████████████████████████████████████████████████████████████████████
301.strings.Replacer struct
The strings.Replacer struct can be used to perform replacements on a string and output the modified
result to a Writer.
example:
package main
import (
"fmt"
"io"
"strings"
)
func writeReplaced(writer io.Writer, str string, subs ...string) {
replacer := strings.NewReplacer(subs...)
replacer.WriteString(writer, str)
}
func main() {
text := "It was a boat. A small boat."
subs := []string{"boat", "kayak", "small", "huge"}
var writer strings.Builder
writeReplaced(&writer, text, subs...)
fmt.Println(writer.String())
}
Output:
It was a kayak. A huge kayak.
████████████████████████████████████████████████████████████████████████
302.Working with JSON Data
Putting Working with JSON Data in Context
What is it?
JSON data is the de facto standard for exchanging data, especially in HTTP applications.
Why is it useful?
JSON is simple enough to be supported by any language but can represent
relatively complex data.
How is it used?
The encoding/json package provides support for encoding and decoding JSON data.
Are there any pitfalls or limitations?
Not all Go data types can be represented in JSON, which requires the developer
to be mindful of how Go data types will be expressed.
Are there any alternatives?
There are many other data encodings available, some of which are supported by
the Go standard library.
The safest approach is to define a map with string keys and empty interface values, which ensures that
all the key-value pairs in the JSON data can be decoded into the map
Problem Solution
----------- ------------------------
Encode JSON dataCreate an Encoder with a Writer and invoke the Encode method
Control struct encoding Use JSON struct tags or implement the Mashaler interface
Decode JSON data Create a Decoder with a Reader and invoke the Decode method
Control struct decoding Use JSON struct tags or implement the Unmarshaler interface
████████████████████████████████████████████████████████████████████████
303.The encoding/json Constructor Functions for JSON Data
Name Description
------------------- --------------------------------------------
NewEncoder(writer) This function returns an Encoder, which can be used to encode JSON data and
write it to the specified Writer.
NewDecoder(reader) This function returns a Decoder, which can be used to read JSON data from the
specified Reader and decode it.
████████████████████████████████████████████████████████████████████████
304.The Functions for Creating and Parsing JSON Data
Name Description
------------------------ -----------------------------------------------
Marshal(value) This function encodes the specified value as JSON. The results are the
JSON content expressed in a byte slice and an error, which indicates any
encoding problems.
Unmarshal(byteSlice, val) This function parses JSON data contained in the specified slice of bytes
and assigns the result to the specified value.
████████████████████████████████████████████████████████████████████████
305.The Encoder Methods
The NewEncoder constructor function is used to create an Encoder, which can be used to write JSON data to a Writer.
Name Description
------------------------- --------------------------------------------------
Encode(val) This method encodes the specified value as JSON and writes it to the Writer.
SetEscapeHTML(on) This method accepts a bool argument that, when true, encodes
characters that would be dangerous in HTML to be escaped. The default
behavior is to escape these characters.
SetIndent(prefix, indent) This method specifies a prefix and indentation that is applied to the name
of each field in the JSON output.
████████████████████████████████████████████████████████████████████████
306.Expressing the Basic Go Data Types in JSON
Data TypeDescription
---------------- ------------------------------------
bool Go bool values are expressed as JSON true or false.
string Go string values are expressed as JSON strings. By default, unsafe HTML
characters are escaped.
float32, float64 Go floating-point values are expressed as JSON numbers.
int, int<size> Go integer values are expressed as JSON numbers.
uint, uint<size> Go integer values are expressed as JSON numbers.
byte Go bytes are expressed as JSON numbers.
rune Go runes are expressed as JSON numbers.
nil The Go nil value is expressed as the JSON null value.
Pointers The JSON encoder follows pointers and encodes the value at the pointer's location.
████████████████████████████████████████████████████████████████████████
307.encoding/json
example:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
var b bool = true
var str string = "Hello"
var fval float64 = 99.99
var ival int = 200
var pointer *int = &ival
var writer strings.Builder
encoder := json.NewEncoder(&writer)
for _, val := range []interface{}{b, str, fval, ival, pointer} {
encoder.Encode(val)
}
fmt.Print(writer.String())
}
Output:
true
"Hello"
99.99
200
200
████████████████████████████████████████████████████████████████████████
308.Encoding Slices and Arrays
Example:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
names := []string{"Kayak", "Lifejacket", "Soccer Ball"}
numbers := [3]int{10, 20, 30}
var byteArray [5]byte
copy(byteArray[0:], []byte(names[0]))
byteSlice := []byte(names[0])
var writer strings.Builder
encoder := json.NewEncoder(&writer)
encoder.Encode(names)
encoder.Encode(numbers)
encoder.Encode(byteArray)
encoder.Encode(byteSlice)
fmt.Print(writer.String())
}
Output:
["Kayak","Lifejacket","Soccer Ball"]
[10,20,30]
[75,97,121,97,107]
"S2F5YWs="
████████████████████████████████████████████████████████████████████████
309.Encoding Maps
Go maps are encoded as JSON objects, with the map keys used as the object keys. The values contained in
the map are encoded based on their type.
Maps can also be useful for creating custom JSON representations of Go data.
encoder.Encode()
example:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
m := map[string]float64{
"Kayak": 279,
"Lifejacket": 49.95,
}
var writer strings.Builder
encoder := json.NewEncoder(&writer)
encoder.Encode(m)
fmt.Print(writer.String())
}
Output:
{"Kayak":279,"Lifejacket":49.95}
████████████████████████████████████████████████████████████████████████
310.Encoding Structs
The Encoder expresses struct values as JSON objects, using the exported struct field names as the object's
keys and the field values as the object's values
example:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
var writer strings.Builder
encoder := json.NewEncoder(&writer)
encoder.Encode(Kayak)
fmt.Print(writer.String())
}
Output:
{"Name":"Kayak","Category":"Watersports","Price":279}
████████████████████████████████████████████████████████████████████████
311.Effect of Promotion in JSON in Encoding
When a struct defines an embedded field that is also a struct, the fields of the embedded struct are promoted
and encoded as though they are defined by the enclosing type.
discount.go:
package main
type DiscountedProduct struct {
*Product
Discount float64
}
main.go:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
var writer strings.Builder
encoder := json.NewEncoder(&writer)
dp := DiscountedProduct {
Product: &Kayak,
Discount: 10.50,
}
encoder.Encode(&dp)
fmt.Print(writer.String())
}
Output:
{"Name":"Kayak","Category":"Watersports","Price":279,"Discount":10.5}
████████████████████████████████████████████████████████████████████████
312.Customizing the JSON Encoding of Structs
How a struct is encoded can be customized using struct tags, which are string literals that follow fields. Struct
tags are part of the Go support for reflection,
that tags follow fields and can be used to alter two aspects of how a field is encoded in JSON.
example:
discount.go:
package main
type DiscountedProduct struct {
*Product ^json:"product"^ // -------------------------> Use the symbol ^ above the Tab button instead
Discount float64
}
===============================================================
Output:
{"product":{"Name":"Kayak","Category":"Watersports","Price":279},"Discount":10.5}
████████████████████████████████████████████████████████████████████████
313.Omitting a Field
The Encoder skips fields decorated with a tag that specifies a hyphen (the - character) for the name
The new tag tells the Encoder to skip the Discount field when creating the JSON representation of a
DIscountedProduct value.
exampe:
discount.go:
package main
type DiscountedProduct struct {
*Product ^json:"product"^ // -------------------------> Use the symbol ^ above the Tab button instead
Discount float64 ^json:"-"^ // -------------------------> Use the symbol ^ above the Tab button instead
}
Output:
{"product":{"Name":"Kayak","Category":"Watersports","Price":279}}
████████████████████████████████████████████████████████████████████████
314.Omitting Unassigned Fields
By default, the JSON Encoder includes struct fields, even when they have not been assigned a value
example:
main.go:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
var writer strings.Builder
encoder := json.NewEncoder(&writer)
dp := DiscountedProduct{
Product: &Kayak,
Discount: 10.50,
}
encoder.Encode(&dp)
dp2 := DiscountedProduct { Discount: 10.50 }
encoder.Encode(&dp2)
fmt.Print(writer.String())
}
Output:
{"product":{"Name":"Kayak","Category":"Watersports","Price":279}}
{"product":null}
To omit a nil field, the omitempty keyword is added to the tag for the field
discount.go:
package main
type DiscountedProduct struct {
// *Product ^json:"product"^ // -------------------------> Use the symbol ^ above the Tab button instead
*Product ^json:"product,omitempty"^ // -------------------------> Use the symbol ^ above the Tab button instead
Discount float64 ^json:"-"^
}
Output:
{"product":{"Name":"Kayak","Category":"Watersports","Price":279}}
{}
To skip a nil field without changing the name or field promotion, specify the omitempty keyword without a name
discount.go:
package main
type DiscountedProduct struct {
// *Product ^json:"product"^
// *Product ^json:"product,omitempty"^
*Product ^json:",omitempty"^ // -------------------------> Use the symbol ^ above the Tab button instead
Discount float64 ^json:"-"^ // -------------------------> Use the symbol ^ above the Tab button instead
}
Output:
{"Name":"Kayak","Category":"Watersports","Price":279}
{}
████████████████████████████████████████████████████████████████████████
315.Forcing Fields to be Encoded as Strings
Struct tags can be used to force a field value to be encoded as a string, overriding the normal encoding for
the field type
example:
discount.go:
package main
type DiscountedProduct struct {
*Product ^json:",omitempty"^
// Discount float64 ^json:"-"^ // -------------------------> Use the symbol ^ above the Tab button
Discount float64 ^json:",string"^ // -------------------------> Use the symbol ^ above the Tab button
}
Output:
{"Name":"Kayak","Category":"Watersports","Price":279,"Discount":"10.5"}
{"Discount":"10.5"}
████████████████████████████████████████████████████████████████████████
316.Encoding Interfaces
The JSON encoder can be used on values assigned to interface variables, but it is the dynamic type that
is encoded.
No aspect(جنبه) of the interface is used to adapt the JSON, and all the exported fields of each value in the slice
are included in the JSON. This can be a useful feature, but care must be taken when decoding this kind of
JSON, because each value can have a different set of fields
example:
interface.go:
package main
type Named interface{ GetName() string }
type Person struct{ PersonName string }
func (p *Person) GetName() string { return p.PersonName }
func (p *DiscountedProduct) GetName() string { return p.Name }
main.go:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
var writer strings.Builder
encoder := json.NewEncoder(&writer)
dp := DiscountedProduct{
Product: &Kayak,
Discount: 10.50,
}
namedItems := []Named { &dp, &Person{ PersonName: "Alice"}}
encoder.Encode(namedItems)
fmt.Print(writer.String())
}
Output:
[{"Name":"Kayak","Category":"Watersports","Price":279,"Discount":"10.5"},{"PersonName":"Alice"}]
████████████████████████████████████████████████████████████████████████
317.The Marshaler Method
Creating Completely Custom JSON Encodings
The Encoder checks to see whether a struct implements the Marshaler interface, which denotes a type that
has a custom encoding and which defines the method.
Name Description
-------------- ------------------------------------------
MarshalJSON() This method is invoked to create a JSON representation of a value and returns a byte
slice containing the JSON and an error indicating encoding problems.
████████████████████████████████████████████████████████████████████████
318.json.Marshal()
The MarshalJSON method can generate JSON in any way that suits the project.
I define a map with string keys and use the
empty interface for the values. This allows me to build the JSON by adding key-value pairs to the map
and then pass the map to the Marshal function, which uses the built-in support
to encode each of the values contained in the map.
example:
discount.go:
package main
import "encoding/json"
type DiscountedProduct struct {
*Product ^json:",omitempty"^
Discount float64 ^json:",string"^
}
func (dp *DiscountedProduct) MarshalJSON() (jsn []byte, err error) {
if dp.Product != nil {
m := map[string]interface{}{
"product": dp.Name,
"cost": dp.Price - dp.Discount,
}
jsn, err = json.Marshal(m)
}
return
}
main.go:
package main
import (
"encoding/json"
"fmt"
"strings"
)
func main() {
var writer strings.Builder
encoder := json.NewEncoder(&writer)
dp := DiscountedProduct{
Product: &Kayak,
Discount: 10.50,
}
namedItems := []Named { &dp, &Person{ PersonName: "Alice"}}
encoder.Encode(namedItems)
fmt.Print(writer.String())
}
Output:
[{"cost":268.5,"product":"Kayak"},{"PersonName":"Alice"}]
████████████████████████████████████████████████████████████████████████
319.Decoding JSON Data
The NewDecoder constructor function creates a Decoder, which can be used to decode JSON data obtained
from a Reader.
The Decoder Methods:
Name Description
--------------------- --------------------------------------------
Decode(value) This method reads and decodes data, which is used to create the specified
value. The method returns an error that indicates problems decoding the
data to the required type or EOF.
DisallowUnknownFields() By default, when decoding a struct type, the Decoder ignores any key in
the JSON data for which there is no corresponding struct field. Calling this
method causes the Decode to return an error, rather than ignoring the key.
UseNumber() By default, JSON number values are decoded into float64 values. Calling
this method uses the Number type instead, as described in the “Decoding
Number Values” section.
████████████████████████████████████████████████████████████████████████
320.Decoding Basic Data Types
example:
main.go:
package main
import (
"encoding/json"
"io"
"strings"
"asd/asd"
)
func main() {
reader := strings.NewReader(^true "Hello" 99.99 200^) // -------------------------> Use the symbol ^ above the Tab button
vals := []interface{}{}
decoder := json.NewDecoder(reader)
for {
var decodedVal interface{}
err := decoder.Decode(&decodedVal)
if err != nil {
if err != io.EOF {
asd.Printfln("Error: %v", err.Error())
}
break
}
vals = append(vals, decodedVal)
}
for _, val := range vals {
asd.Printfln("Decoded (%T): %v", val, val)
}
}
Output:
Decoded (bool): true
Decoded (string): Hello
Decoded (float64): 99.99
Decoded (float64): 200
████████████████████████████████████████████████████████████████████████
321.Decoding Number Values
The Methods Defined by the Number Type
Name Description
--------- ------------------------------------
Int64() This method returns the decoded value as a int64 and an error that indicates if the value
cannot be converted.
Float64() This method returns the decoded value as a float64 and an error that indicates if the
value cannot be converted.
String() This method returns the unconverted string from the JSON data.
Not all JSON number values can be expressed as
Go int64 values, so this is the method that is typically called first.
If attempting to convert to an integer
fails, then the Float64 method can be called.
If a number cannot be converted to either Go type, then the
String method can be used to get the unconverted string from the JSON data.
████████████████████████████████████████████████████████████████████████
322.Decoding Numbers
example:
main.go:
package main
import (
"encoding/json"
"io"
"strings"
)
func main() {
reader := strings.NewReader(^true "Hello" 99.99 200^) // -------------------------> Use the symbol ^ above the Tab button
vals := []interface{}{}
decoder := json.NewDecoder(reader)
for {
var decodedVal interface{}
err := decoder.Decode(&decodedVal)
if err != nil {
if err != io.EOF {
Printfln("Error: %v", err.Error())
}
break
}
vals = append(vals, decodedVal)
}
for _, val := range vals {
if num, ok := val.(json.Number); ok {
if ival, err := num.Int64(); err == nil {
Printfln("Decoded Integer: %v", ival)
} else if fpval, err := num.Float64(); err == nil {
Printfln("Decoded Floating Point: %v", fpval)
} else {
Printfln("Decoded String: %v", num.String())
}
} else {
Printfln("Decoded (%T): %v", val, val)
}
}
}
Output:
Decoded (bool): true
Decoded (string): Hello
Decoded (float64): 99.99
Decoded (float64): 200
████████████████████████████████████████████████████████████████████████
323.Specifying Types for Decoding
example:
main.go:
package main
import (
"encoding/json"
"strings"
)
func main() {
reader := strings.NewReader(^true "Hello" 99.99 200^) // -------------------------> Use the symbol ^ above the Tab button
var bval bool
var sval string
var fpval float64
var ival int
vals := []interface{}{&bval, &sval, &fpval, &ival}
decoder := json.NewDecoder(reader)
for i := 0; i < len(vals); i++ {
err := decoder.Decode(vals[i])
if err != nil {
Printfln("Error: %v", err.Error())
break
}
}
Printfln("Decoded (%T): %v", bval, bval)
Printfln("Decoded (%T): %v", sval, sval)
Printfln("Decoded (%T): %v", fpval, fpval)
Printfln("Decoded (%T): %v", ival, ival)
}
Output:
Decoded (bool): true
Decoded (string): Hello
Decoded (float64): 99.99
Decoded (int): 200
████████████████████████████████████████████████████████████████████████
324.Decoding Arrays
The Decoder processes arrays automatically, but care must be taken because JSON allows arrays to contain
values of different types, which conflicts with the strict type rules enforced by Go.
The source JSON data contains two arrays, one of which contains only numbers and one of which mixes
numbers and strings. The Decoder doesn't try to figure out if a JSON array can be represented using a single
Go type and decodes every array into an empty interface slice:
example:
main.go:
package main
import (
"encoding/json"
"io"
"strings"
)
func main() {
reader := strings.NewReader(^[10,20,30]["Kayak","Lifejacket",279]^) // -------------------------> Use the symbol ^ above the Tab button
vals := []interface{}{}
decoder := json.NewDecoder(reader)
for {
var decodedVal interface{}
err := decoder.Decode(&decodedVal)
if err != nil {
if err != io.EOF {
Printfln("Error: %v", err.Error())
}
break
}
vals = append(vals, decodedVal)
}
for _, val := range vals {
Printfln("Decoded (%T): %v", val, val)
}
}
Output:
Decoded ([]interface {}): [10 20 30]
Decoded ([]interface {}): [Kayak Lifejacket 279]
████████████████████████████████████████████████████████████████████████
325.Specifying the Decoded Array Type
The second array contains a mix of values, which means that I have to specify
the empty interface as the target type. The literal slice syntax is awkward when using the empty interface
because two sets of braces are required:
...
mixed := []interface{} {}
example:
main.go:
package main
import (
"encoding/json"
"strings"
)
func main() {
reader := strings.NewReader(^[10,20,30]["Kayak","Lifejacket",279]^) // -------------------------> Use the symbol ^ above the Tab button
ints := []int {}
mixed := []interface{} {}
vals := []interface{} { &ints, &mixed}
decoder := json.NewDecoder(reader)
for i := 0; i < len(vals); i++ {
err := decoder.Decode(vals[i])
if err != nil {
Printfln("Error: %v", err.Error())
break
}
}
Printfln("Decoded (%T): %v", ints, ints)
Printfln("Decoded (%T): %v", mixed, mixed)
}
Output:
Decoded ([]int): [10 20 30]
Decoded ([]interface {}): [Kayak Lifejacket 279]
████████████████████████████████████████████████████████████████████████
326.Decoding Maps
The safest approach is to define a map with string keys and empty interface values, which ensures that
all the key-value pairs in the JSON data can be decoded into the map
JavaScript objects are expressed as key-value pairs, which makes it easy to decode them into Go maps
example:
main.go:
package main
import (
"encoding/json"
"strings"
)
func main() {
reader := strings.NewReader(^{"Kayak" : 279, "Lifejacket" : 49.95}^) // -------------------------> Use the symbol ^ above the Tab button
m := map[string]interface{}{}
decoder := json.NewDecoder(reader)
err := decoder.Decode(&m)
if err != nil {
Printfln("Error: %v", err.Error())
} else {
Printfln("Map: %T, %v", m, m)
for k, v := range m {
Printfln("Key: %v, Value: %v", k, v)
}
}
}
Output:
Map: map[string]interface {}, map[Kayak:279 Lifejacket:49.95]
Key: Kayak, Value: 279
Key: Lifejacket, Value: 49.95
████████████████████████████████████████████████████████████████████████
327.a Specific Value Type
A single JSON object can be used for multiple data types as values, but if you know in advance that you
will be decoding a JSON object that has a single value type, then you can be more specific when defining the
map into which the data will be decoded.
example:
main.go:
package main
import (
"encoding/json"
"strings"
)
func main() {
reader := strings.NewReader(^{"Kayak" : 279, "Lifejacket" : 49.95}^) // -------------------------> Use the symbol ^ above the Tab button
m := map[string]float64 {}
decoder := json.NewDecoder(reader)
err := decoder.Decode(&m)
if err != nil {
Printfln("Error: %v", err.Error())
} else {
Printfln("Map: %T, %v", m, m)
for k, v := range m {
Printfln("Key: %v, Value: %v", k, v)
}
}
}
Output:
Map: map[string]float64, map[Kayak:279 Lifejacket:49.95]
Key: Kayak, Value: 279
Key: Lifejacket, Value: 49.95
████████████████████████████████████████████████████████████████████████
328.Decoding Structs
The Decoder decodes the JSON object and uses the keys to set the values of the exported struct fields.
The capitalization of the fields and JSON keys don't have to match, and the Decoder will ignore any JSON
key for which there isn't a struct field and ignore any struct field for which there is no JSON key.
The JSON objectscontain different capitalization and have more or fewer keys than the Product struct
fields. The Decoder processes the data as best as it can.
example:
main.go:
package main
import (
"strings"
"encoding/json"
"io"
)
func main() {
reader := strings.NewReader(^ // -------------------------> Use the symbol ^ above the Tab button
{"Name":"Kayak","Category":"Watersports","Price":279}
{"Name":"Lifejacket","Category":"Watersports" }
{"name":"Canoe","category":"Watersports", "price": 100, "inStock": true }
^) // -------------------------> Use the symbol ^ above the Tab button
decoder := json.NewDecoder(reader)
for {
var val Product
err := decoder.Decode(&val)
if err != nil {
if err != io.EOF {
Printfln("Error: %v", err.Error())
}
break
} else {
Printfln("Name: %v, Category: %v, Price: %v",
val.Name, val.Category, val.Price)
}
}
}
Output:
Name: Kayak, Category: Watersports, Price: 279
Name: Lifejacket, Category: Watersports, Price: 0
Name: Canoe, Category: Watersports, Price: 100
████████████████████████████████████████████████████████████████████████
329.Decoding to Interface Types
As I explained earlier in the chapter, the JSON encoder deals with interfaces by encoding the value
using the exported fields of the dynamic type. This is because JSON deals with key-value pairs and
has no way to express methods. As a consequence, you cannot decode directly to an interface variable
from JSON. Instead, you must decode to a struct or map and then assign the value that is created to an
interface variable.
████████████████████████████████████████████████████████████████████████
330.Disallowing Unused Keys
By default, the Decoder will ignore JSON keys for which there is no corresponding struct field. This behavior
can be changed by calling the DisallowUnknownFields method
example:
Disallowing Unused Keys in the main.go
...
decoder := json.NewDecoder(reader)
decoder.DisallowUnknownFields()
...
output:
Name: Kayak, Category: Watersports, Price: 279
Name: Lifejacket, Category: Watersports, Price: 0
Error: json: unknown field "inStock"
████████████████████████████████████████████████████████████████████████
331.Struct Tags
The tag applied to the Discount field tells the Decoder that the value for this field should be obtained
from the JSON key named offer and that the value will be parsed from a string, instead of the JSON
number that would usually be expected for a Go float64 value.
example:
discount.go:
package main
import "encoding/json"
type DiscountedProduct struct {
*Product ^json:",omitempty"^ // -------------------------> Use the symbol ^ above the Tab button
Discount float64 ^json:"offer,string"^ // -------------------------> Use the symbol ^ above the Tab button
}
func (dp *DiscountedProduct) MarshalJSON() (jsn []byte, err error) {
if (dp.Product != nil) {
m := map[string]interface{} {
"product": dp.Name,
"cost": dp.Price - dp.Discount,
}
jsn, err = json.Marshal(m)
}
return
}
main.go:
package main
import (
"strings"
"encoding/json"
"io"
)
func main() {
reader := strings.NewReader(^
{"Name":"Kayak","Category":"Watersports","Price":279, "Offer": "10"}^) // -------------------------> Use the symbol ^ above the Tab button
decoder := json.NewDecoder(reader)
for {
var val DiscountedProduct
err := decoder.Decode(&val)
if err != nil {
if err != io.EOF {
Printfln("Error: %v", err.Error())
}
break
} else {
Printfln("Name: %v, Category: %v, Price: %v, Discount: %v",
val.Name, val.Category, val.Price, val.Discount)
}
}
}
Output:
Name: Kayak, Category: Watersports, Price: 279, Discount: 10
████████████████████████████████████████████████████████████████████████
332.Creating Completely Custom JSON Decoders
The Unmarshaler Method
Name Description
------------------------ ------------------------------------------
UnmarshalJSON(byteSlice) This method is invoked to decode JSON data contained in the specified
byte slice. The result is an error indicating encoding problems.
████████████████████████████████████████████████████████████████████████
333.Defining a Custom Decoder
This implementation of the UnmarshalJSON method uses the Unmarshal method to decode the JSON
data into a map and then checks the type of each value required for the DiscountedProduct struct.
example:
discount.go:
package main
import (
"encoding/json"
"strconv"
)
type DiscountedProduct struct {
*Product ^json:",omitempty"^ // -------------------------> Use the symbol ^ above the Tab button
Discount float64 ^json:"offer,string"^ // -------------------------> Use the symbol ^ above the Tab button
}
func (dp *DiscountedProduct) MarshalJSON() (jsn []byte, err error) {
if dp.Product != nil {
m := map[string]interface{}{
"product": dp.Name,
"cost": dp.Price - dp.Discount,
}
jsn, err = json.Marshal(m)
}
return
}
func (dp *DiscountedProduct) UnmarshalJSON(data []byte) (err error) {
mdata := map[string]interface{}{}
err = json.Unmarshal(data, &mdata)
if dp.Product == nil {
dp.Product = &Product{}
}
if err == nil {
if name, ok := mdata["Name"].(string); ok {
dp.Name = name
}
if category, ok := mdata["Category"].(string); ok {
dp.Category = category
}
if price, ok := mdata["Price"].(float64); ok {
dp.Price = price
}
if discount, ok := mdata["Offer"].(string); ok {
fpval, fperr := strconv.ParseFloat(discount, 64)
if fperr == nil {
dp.Discount = fpval
}
}
}
return
}
Output:
Name: Kayak, Category: Watersports, Price: 279, Discount: 10
████████████████████████████████████████████████████████████████████████
334.Working with Files
Putting Working with Files in Context
Answer Question
--------------------------- -------------------------------------------------------
What are they? These features provide access to the file system so that files can be read and written.
Why are they useful? Files are used for everything from logging to configuration files.
How are they used? These features are accessed through the os package, which provides platform-
neutral access to the file system.
Are there any pitfallsor limitations? Some consideration of the underlying file system must be made, especially when
dealing with paths.
Are there any alternatives? Go supports alternative ways of storing data, such as databases, but there are no
alternative mechanisms for accessing files.
Preparing
printer.go:
package main
import (
"fmt"
)
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
product.go:
package main
type Product struct {
Name, Category string
Price float64
}
var Kayak = Product{
Name: "Kayak",
Category: "Watersports",
Price: 279,
}
var Products = []Product{
{"Kayak", "Watersports", 279},
{"Lifejacket", "Watersports", 49.95},
{"Soccer Ball", "Soccer", 19.50},
{"Corner Flags", "Soccer", 34.95},
{"Stadium", "Soccer", 79500},
{"Thinking Cap", "Chess", 16},
{"Unsteady Chair", "Chess", 75},
{"Bling-Bling King", "Chess", 1200},
}
main.go:
package main
func main() {
for _, p := range Products {
Printfln("Product: %v, Category: %v, Price: $%.2f",
p.Name, p.Category, p.Price)
}
}
Output:
Product: Kayak, Category: Watersports, Price: $279.00
Product: Lifejacket, Category: Watersports, Price: $49.95
Product: Soccer Ball, Category: Soccer, Price: $19.50
Product: Corner Flags, Category: Soccer, Price: $34.95
Product: Stadium, Category: Soccer, Price: $79500.00
Product: Thinking Cap, Category: Chess, Price: $16.00
Product: Unsteady Chair, Category: Chess, Price: $75.00
Product: Bling-Bling King, Category: Chess, Price: $1200.00
████████████████████████████████████████████████████████████████████████
335.The os Package Functions for Reading Files
Name Description
------------- -------------------------------------
ReadFile(name) This function opens the specified file and reads its contents. The results are a byte
slice containing the file content and an error indicating problems opening or reading
the file.
Open(name) This function opens the specified file for reading. The result is a File struct and an
error that indicates problems opening the file.
One of the most common reasons to read a file is to load configuration data. The JSON format is well-
suited for configuration files because it is simple to process, has good support in the Go standard library
The Contents of the config.json File in the files Folder:
{
"Username": "Alice",
"AdditionalProducts": [
{"name": "Hat", "category": "Skiing", "price": 10},
{"name": "Boots", "category":"Skiing", "price": 220.51 },
{"name": "Gloves", "category":"Skiing", "price": 40.20 }
]
}
████████████████████████████████████████████████████████████████████████
336.os.ReadFile()
The LoadConfig function uses the ReadFile function to read the contents of the config.json file.
The file will be read from the current working directory when the application is executed,
which means that I can open the file just with its name.
The contents of the file are returned as a byte slice, which is converted to a string and written out. The
LoadConfig function is invoked by an initialization function, which ensures the configuration file is read.
example:
readconfig.go:
package main
import "os"
func LoadConfig() (err error) {
data, err := os.ReadFile("config.json")
if err == nil {
Printfln(string(data))
}
return
}
func init() {
err := LoadConfig()
if err != nil {
Printfln("Error Loading Config: %v", err.Error())
}
}
Output:
{
"Username": "Alice",
"AdditionalProducts": [
{"name": "Hat", "category": "Skiing", "price": 10},
{"name": "Boots", "category":"Skiing", "price": 220.51 },
{"name": "Gloves", "category":"Skiing", "price": 40.20 }
]
}
Product: Kayak, Category: Watersports, Price: $279.00
Product: Lifejacket, Category: Watersports, Price: $49.95
Product: Soccer Ball, Category: Soccer, Price: $19.50
Product: Corner Flags, Category: Soccer, Price: $34.95
Product: Stadium, Category: Soccer, Price: $79500.00
Product: Thinking Cap, Category: Chess, Price: $16.00
Product: Unsteady Chair, Category: Chess, Price: $75.00
Product: Bling-Bling King, Category: Chess, Price: $1200.00
████████████████████████████████████████████████████████████████████████
337.Decoding the JSON Data
example:
readconfig.go:
package main
import (
"encoding/json"
"os"
"strings"
)
type ConfigData struct {
UserName string
AdditionalProducts []Product
}
var Config ConfigData
func LoadConfig() (err error) {
data, err := os.ReadFile("config.json")
if err == nil {
decoder := json.NewDecoder(strings.NewReader(string(data)))
err = decoder.Decode(&Config)
}
return
}
func init() {
err := LoadConfig()
if err != nil {
Printfln("Error Loading Config: %v", err.Error())
} else {
Printfln("Username: %v", Config.UserName)
Products = append(Products, Config.AdditionalProducts...)
}
}
Output:
Username: Alice
Product: Kayak, Category: Watersports, Price: $279.00
Product: Lifejacket, Category: Watersports, Price: $49.95
Product: Soccer Ball, Category: Soccer, Price: $19.50
Product: Corner Flags, Category: Soccer, Price: $34.95
Product: Stadium, Category: Soccer, Price: $79500.00
Product: Thinking Cap, Category: Chess, Price: $16.00
Product: Unsteady Chair, Category: Chess, Price: $75.00
Product: Bling-Bling King, Category: Chess, Price: $1200.00
Product: Hat, Category: Skiing, Price: $10.00
Product: Boots, Category: Skiing, Price: $220.51
Product: Gloves, Category: Skiing, Price: $40.20
████████████████████████████████████████████████████████████████████████
338.os.Open("config.json")
Using the File Struct to Read a File
The Open function opens a file for reading and returns a File value, which represents the open file, and an
error, which is used to indicate problems opening the file. The File struct implements the Reader interface,
which makes it simple to read and process the example JSON data, without reading the entire file into a byte
slice.
The File struct also implements the Closer interface, which defines a Close method.
The defer keyword can be used to call the Close method when the enclosing function completes,
like this:
defer file.Close()
using the defer keyword ensures that the file is closed even when a function returns early.
example:
readconfig.go:
package main
import (
"encoding/json"
"os"
"time"
)
type ConfigData struct {
UserName string
AdditionalProducts []Product
}
var Config ConfigData
func LoadConfig() (err error) {
file, err := os.Open("config.json")
if (err == nil) {
defer file.Close()
decoder := json.NewDecoder(file)
err = decoder.Decode(&Config)
}
return
}
func init() {
time.Sleep(time.Second)
}
func init() {
err := LoadConfig()
if err != nil {
Printfln("Error Loading Config: %v", err.Error())
} else {
Printfln("Username: %v", Config.UserName)
Products = append(Products, Config.AdditionalProducts...)
}
}
Output:
Username: Alice
Product: Kayak, Category: Watersports, Price: $279.00
Product: Lifejacket, Category: Watersports, Price: $49.95
Product: Soccer Ball, Category: Soccer, Price: $19.50
Product: Corner Flags, Category: Soccer, Price: $34.95
Product: Stadium, Category: Soccer, Price: $79500.00
Product: Thinking Cap, Category: Chess, Price: $16.00
Product: Unsteady Chair, Category: Chess, Price: $75.00
Product: Bling-Bling King, Category: Chess, Price: $1200.00
Product: Hat, Category: Skiing, Price: $10.00
Product: Boots, Category: Skiing, Price: $220.51
Product: Gloves, Category: Skiing, Price: $40.20
████████████████████████████████████████████████████████████████████████
339.Methods Defined by the File Struct for Reading at a Specific Location
Name Description
-------------------- ------------------------------------------
ReadAt(slice, offset) This method is defined by the ReaderAt interface and performs a read into the
specific slice at the specified position offset in the file.
Seek(offset, how) This method is defined by the Seeker interface and moves the offset into
جستجو کنید the file for the next read. The offset is determined by the combination of the
two arguments: the first argument specifies the number of bytes to offset,
and the second argument determines how the offset is applied—a value of 0
means the offset is relative to the start of the file, a value of 1 means the offset
is relative to the current read position, and a value of 2 means the offset is
relative to the end of the file.
Reading from specific locations requires knowledge of the file structure.
In this example, I know the location of the data I want to read,
which allows me to use the ReadAt method to read the username value
and the Seek method to jump to the start of the product data.
example:
readconfig.go:
package main
import (
"os"
"encoding/json"
//"strings"
)
type ConfigData struct {
UserName string
AdditionalProducts []Product
}
var Config ConfigData
func LoadConfig() (err error) {
file, err := os.Open("config.json")
if (err == nil) {
defer file.Close()
nameSlice := make([]byte, 5)
file.ReadAt(nameSlice, 19)
Config.UserName = string(nameSlice)
file.Seek(55, 0)
decoder := json.NewDecoder(file)
err = decoder.Decode(&Config.AdditionalProducts)
}
return
}
func init() {
err := LoadConfig()
if err != nil {
Printfln("Username: %v", Config.UserName)
Products = append(Products, Config.AdditionalProducts...)
} else {
Printfln("Error Loading Config: %v", err.Error())
}
}
Output:
Username: Alice
Product: Kayak, Category: Watersports, Price: $279.00
Product: Lifejacket, Category: Watersports, Price: $49.95
Product: Soccer Ball, Category: Soccer, Price: $19.50
Product: Corner Flags, Category: Soccer, Price: $34.95
Product: Stadium, Category: Soccer, Price: $79500.00
Product: Thinking Cap, Category: Chess, Price: $16.00
Product: Unsteady Chair, Category: Chess, Price: $75.00
Product: Bling-Bling King, Category: Chess, Price: $1200.00
████████████████████████████████████████████████████████████████████████
340.The os Package Function for Writing Files
Name Description
------------------------------- ----------------------------------------
WriteFile(name,slice, modePerms) This function creates a file with the specified name, mode, and permissions and
writes the content of the specified byte slice. If the file already exists, its contents
will be replaced with the byte slice. The result is an error that reports any problems
creating the file or writing the data.
OpenFile(name, flag, modePerms) The function opens the file with the specified name, using the flags to control how
the file is opened. If a new file is created, then the specified mode and permissions
are applied. The result is a File value that provides access to the file contents and
an error that indicates problems opening the file.
████████████████████████████████████████████████████████████████████████
341.the Write Convenience Function
The file mode is used to specify special characteristics for the file,
but a value of zero is used for regular files, as in the example.
You can find a list of the file mode values and their settings at
https://golang.org/pkg/io/fs/#FileMode
https://cs.opensource.google/go/go/+/go1.21.1:src/io/fs/fs.go;l=165
type FileMode
type FileMode uint32
A FileMode represents a file's mode and permission bits.
The bits have the same definition on all systems,
so that information about files can be moved from one system to another portably.
Not all bits apply to all systems.
The only required bit is ModeDir for directories.
const (
// The single letters are the abbreviations
// used by the String method's formatting.
ModeDir FileMode = 1 << (32 - 1 - iota) // d: is a directory
ModeAppend // a: append-only
ModeExclusive // l: exclusive use
ModeTemporary // T: temporary file; Plan 9 only
ModeSymlink // L: symbolic link
ModeDevice // D: device file
ModeNamedPipe // p: named pipe (FIFO)
ModeSocket // S: Unix domain socket
ModeSetuid // u: setuid
ModeSetgid // g: setgid
ModeCharDevice // c: Unix character device, when ModeDevice is set
ModeSticky // t: sticky
ModeIrregular // ?: non-regular file; nothing else is known about this file
// Mask for the type bits. For regular files, none will be set.
ModeType = ModeDir | ModeSymlink | ModeNamedPipe | ModeSocket | ModeDevice | ModeCharDevice | ModeIrregular
ModePerm FileMode = 0777 // Unix permission bits
)
The defined file mode bits are the most significant bits of the FileMode.
The nine least-significant bits are the standard Unix rwxrwxrwx permissions.
The values of these bits should be considered part of the public API and
may be used in wire protocols or disk representations: they must not be changed,
although new bits might be added.
example:
main.go:
package main
import (
"fmt"
"os"
"time"
)
func main() {
total := 0.0
for _, p := range Products {
total += p.Price
}
dataStr := fmt.Sprintf("Time: %v, Total: $%.2f\n",time.Now().Format("Mon 15:04:05"), total)
err := os.WriteFile("output.txt", []byte(dataStr), 0666)
if err == nil {
fmt.Println("Output file created")
} else {
Printfln("Error: %v", err.Error())
}
}
Output: a file create with this name= output.txt
Time: Thu 07:27:34, Total: $279.00
████████████████████████████████████████████████████████████████████████
342.Using the File Struct to Write to a File
The OpenFile function opens a file and returns a File value.
Unlike the Open function, the OpenFile function accepts one or
more flags that specify how the file should be opened.
The flags are defined as constants in the os package,
Care must be taken with these flags, not all of which are supported by every operating system.
████████████████████████████████████████████████████████████████████████
343.The File Opening Flags
Name Description
-------- ------------------------------------------
O_RDONLY This flag opens the file read-only so that it can be read from but not written to.
O_WRONLY This flag opens the file write-only so that it can be written to but not read from.
O_RDWR This flag opens the file read-write so that it can be written to and read from.
O_APPEND This flag will append writes to the end of the file.
O_CREATE This flag will create the file if it doesn't exist.
O_EXCL This flag is used in conjunction with O_CREATE to ensure that a new file is created. If the file
already exists, this flag will trigger an error.
O_SYNC This flag enables synchronous writes, such that data is written to the storage device before
the write function/method returns.
O_TRUNC This flag truncates the existing content in the file.
████████████████████████████████████████████████████████████████████████
344.Writing to a File
example:
main.go:
package main
import (
"fmt"
"time"
"os"
)
func main() {
total := 0.0
for _, p := range Products {
total += p.Price
}
dataStr := fmt.Sprintf("Time: %v, Total: $%.2f\n",
time.Now().Format("Mon 15:04:05"), total)
file, err := os.OpenFile("output.txt",os.O_WRONLY | os.O_CREATE | os.O_APPEND, 0666)
if (err == nil) {
defer file.Close()
file.WriteString(dataStr)
} else {
Printfln("Error: %v", err.Error())
}
}
I combined the O_WRONLY flag to open the file for writing,
the O_CREATE file to create if it doesn't already
exist, and the O_APPEND flag to append any written data to the end of the file.
Output:
appended to file exist:
Time: Thu 07:27:34, Total: $279.00
Time: Thu 08:17:05, Total: $81174.40
Time: Thu 08:17:09, Total: $81174.40
████████████████████████████████████████████████████████████████████████
345.The File Methods for Writing Data
Name Description
----------------------- ---------------------------------------------------
Seek(offset, how) This method sets the location for subsequent operations.
Write(slice) This method writes the contents of the specified byte slice to the file.
The results are the number of bytes written and an error that indicates
problems writing the data.
WriteAt(slice, offset) This method writes the data in the slice at the specified location and is the
counterpart to the ReadAt method.
WriteString(str) This method writes a string to the file. This is a convenience method that
converts the string to a byte slice, invokes the Write method, and returns the
results it receives.
████████████████████████████████████████████████████████████████████████
346.Writing JSON Data to a File
example:
main.go:
package main
import (
// "fmt"
// "time"
"encoding/json"
"os"
)
func main() {
cheapProducts := []Product{}
for _, p := range Products {
if p.Price < 100 {
cheapProducts = append(cheapProducts, p)
}
}
file, err := os.OpenFile("cheap.json", os.O_WRONLY|os.O_CREATE, 0666)
if err == nil {
defer file.Close()
encoder := json.NewEncoder(file)
encoder.Encode(cheapProducts)
} else {
Printfln("Error: %v", err.Error())
}
}
Output:
create file = cheap.json
content of this:
[{"Name":"Lifejacket","Category":"Watersports","Price":49.95},{"Name":"Soccer Ball","Category":"Soccer","Price":19.5},{"Name":"Corner Flags","Category":"Soccer","Price":34.95},{"Name":"Thinking Cap","Category":"Chess","Price":16},{"Name":"Unsteady Chair","Category":"Chess","Price":75}]
████████████████████████████████████████████████████████████████████████
347.the Convenience Functions to Create New Files
The os Package Functions for Creating Files
The CreateTemp function can be useful, but it is important to understand that the purpose of this
function is to generate a random filename and that in all other respects, the file that is created is just a
regular file. The file that is created isn't removed automatically and will remain on the storage device after
the application has been executed.
Name Description
------------------------- --------------------------------------------
Create(name) This function is equivalent to calling OpenFile with the O_RDWR, O_CREATE, and
O_TRUNC flags. The results are the File, which can be used for reading and writing,
and an error that is used to indicate problems creating the file. Note that this
combination of flags means that if a file exists with the specified name, it will be
opened, and its contents will be deleted.
CreateTemp(dirName, fileName) This function creates a new file in the directory with the specified name. If the
name is the empty string, then the system temporary directory is used, obtained
using the TempDir function. The file is created with a
name that contains a random sequence of characters, as demonstrated in the text
after the table. The file is opened with the O_RDWR, O_CREATE, and O_EXCL flags.
The file isn't removed when it is closed.
████████████████████████████████████████████████████████████████████████
348.Creating a Temporary File
The location of the temporary file is specified with a period, meaning the current working directory.
if the empty string is used, then the file will be created in the default temporary
directory, which is obtained using the TempDir function described.
The name of the file can include an asterisk (the * character),
and if this is present, the random part of the filename will replace it.
If the filename does not contain an asterisk,
then the random part of the filename will be added to the end of the name.
ompile and execute the project, and once execution is complete, you will see a new file in the files
folder. The file in my project is named tempfile-1732419518.json, but your filename will be different, and
you will see a new file and a unique name each time the program is executed.
example:
main.go:
package main
import (
"os"
"encoding/json"
)
func main() {
cheapProducts := []Product {}
for _, p := range Products {
if (p.Price < 100) {
cheapProducts = append(cheapProducts, p)
}
}
file, err := os.CreateTemp(".", "tempfile-*.json")
if (err == nil) {
defer file.Close()
encoder := json.NewEncoder(file)
encoder.Encode(cheapProducts)
} else {
Printfln("Error: %v", err.Error())
}
}
Output:
tempfile-1982129407.json:
[{"Name":"Lifejacket","Category":"Watersports","Price":49.95},{"Name":"Soccer Ball","Category":"Soccer","Price":19.5},{"Name":"Corner Flags","Category":"Soccer","Price":34.95},{"Name":"Thinking Cap","Category":"Chess","Price":16},{"Name":"Unsteady Chair","Category":"Chess","Price":75}]
████████████████████████████████████████████████████████████████████████
349.Working with File Paths
If you want to read and write files in
other locations, then you must specify file paths. The issue is that not all of the operating systems that Go
supports express file paths in the same way. For example, the path to a file named mydata.json in my home
directory on a Linux system might be expressed like this:
/home/adam/mydata.json
where the path to the same in my home directory is expressed like this:
C:\Users\adam\mydata.json
████████████████████████████████████████████████████████████████████████
350.The Common Location Functions Defined by the os Package
Name Description
-------------- ------------------------------------
Getwd() This function returns the current working directory, expressed as a string, and an
error that indicates problems obtaining the value.
UserHomeDir() This function returns the user's home directory and an error that indicates problems
obtaining the path.
UserCacheDir() This function returns the default directory for user-specific cached data and an error
that indicates problems obtaining the path.
UserConfigDir() This function returns the default directory for user-specific configuration data and an
error that indicates problems obtaining the path.
TempDir() This function returns the default directory for temporary files and an error that
indicates problems obtaining the path.
████████████████████████████████████████████████████████████████████████
351.The path/filepath Functions for Paths
Name Description
------------ ------------------------------------------
Abs(path) This function returns an absolute path, which is useful if you have a relative path,
such as a filename.
IsAbs(path) This function returns true if the specified path is absolute.
Base(path) This function returns the last element from the path.
Clean(path) This function tidies up path strings by removing duplicate separators and relative references.
Dir(path) This function returns all but the last element of the path.
EvalSymlinks(path) This function evaluates a symbolic link and returns the resulting path.
Ext(path) This function returns the file extension from the specified path, which is
assumed to be the suffix following the final period in the path string.
FromSlash(path) This function replaces each forward slash with the platform's file separator character.
ToSlash(path) This function replaces the platform's file separator with forward slashes.
Join(...elements) This function combines multiple elements using the platform's file separator.
Match(pattern, path) This function returns true if the path is matched by the specified pattern.
Split(path) This function returns the components on either side of the final path separator in
the specified path.
SplitList(path) This function splits a path into its components, which are returned as a string slice.
VolumeName(path) This function returns the volume component of the specified path or the empty
string if the path does not contain a volume.
████████████████████████████████████████████████████████████████████████
352.Working with a Path
example:
main.go
package main
import (
// "fmt"
// "time"
"os"
//"encoding/json"
"path/filepath"
)
func main() {
path, err := os.UserHomeDir()
if (err == nil) {
path = filepath.Join(path, "MyApp", "MyTempFile.json")
}
Printfln("Full path: %v", path)
Printfln("Volume name: %v", filepath.VolumeName(path))
Printfln("Dir component: %v", filepath.Dir(path))
Printfln("File component: %v", filepath.Base(path))
Printfln("File extension: %v", filepath.Ext(path))
}
Output:
Username: Alice
Full path: /home/sina/MyApp/MyTempFile.json
Volume name:
Dir component: /home/sina/MyApp
File component: MyTempFile.json
File extension: .json
received on the Windows machine:
Username: Alice
Full path: C:\Users\adam\MyApp\MyTempFile.json
Volume name: C:
Dir component: C:\Users\adam\MyApp
File component: MyTempFile.json
File extension: .json
████████████████████████████████████████████████████████████████████████
353.The os Package Functions for Managing Files and Directories
Name Description
----------------- ------------------------------------------
Chdir(dir) This function changes the current working directory to the specified directory.
The result is an error that indicates problems making the change.
Mkdir(name, modePerms) This function creates a directory with the specified name and mode/
permissions. The result is an error that is nil if the directory is created or that
describes a problem if one arises.
MkdirAll(name, modePerms) This function performs the same task as Mkdir but creates any parent directories
in the specified path.
MkdirTemp(parentDir, name) This function is similar to CreateTemp but creates a directory rather than a file.
A random string is added to the end of the specified name or in place of an
asterisk, and the new directory is created within the specified parent. The results
are the name of the directory and an error indicating problems.
Remove(name) This function removes the specified file or directory. The result is an error that
describes any problems that arise.
RemoveAll(name) This function removes the specified file or directory. If the name specifies a
directory, then any children it contains are also removed. The result is an error
that describes any problems that arise.
Rename(old, new) This function renames the specified file or folder. The result is an error that
describes any problems that arise.
Symlink(old, new) This function creates a symbolic link to the specified file. The result is an error
that describes any problems that arise.
████████████████████████████████████████████████████████████████████████
354.Creating Directories
example:
main.go:
package main
import (
// "fmt"
// "time"
"encoding/json"
"os"
"path/filepath"
)
func main() {
path, err := os.UserHomeDir()
if err == nil {
path = filepath.Join(path, "0-Repo/TEST-2/MyApp", "MyTempFile.json")
}
Printfln("Full path: %v", path)
err = os.MkdirAll(filepath.Dir(path), 0766)
if err == nil {
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666)
if err == nil {
defer file.Close()
encoder := json.NewEncoder(file)
encoder.Encode(Products)
}
}
if err != nil {
Printfln("Error %v", err.Error())
}
}
Output:
print this:
Username: Alice
Full path: /home/sina/0-Repo/TEST-2/MyApp/MyTempFile.json
Create this:
MyTempFile.json in that directory with content:
[{"Name":"Kayak","Category":"Watersports","Price":279},{"Name":"Lifejacket","Category":"Watersports","Price":49.95},{"Name":"Soccer Ball","Category":"Soccer","Price":19.5},{"Name":"Corner Flags","Category":"Soccer","Price":34.95},{"Name":"Stadium","Category":"Soccer","Price":79500},{"Name":"Thinking Cap","Category":"Chess","Price":16},{"Name":"Unsteady Chair","Category":"Chess","Price":75},{"Name":"Bling-Bling King","Category":"Chess","Price":1200}]
████████████████████████████████████████████████████████████████████████
355.ReadDir(name)
The os Package Function for Listing Directories
This function reads the specified directory and returns a DirEntry slice, each of which
describes an item in the directory.
The result of the ReadDir function is a slice of values that implement the DirEntry interface, which
defines the methods.
████████████████████████████████████████████████████████████████████████
356.The Methods Defined by the DirEntry Interface
Name Description
-------- --------------------------
Name() This method returns the name of the file or directory described by the DirEntry value.
IsDir() This method returns true if the DirEntry value represents a directory.
Type() This method returns a FileMode value, which is an alias to uint32, which describes the file more
and the permissions of the file or directory represented by the DirEntry value.
Info() This method returns a FileInfo value that provides additional details about the file or directory
represented by the DirEntry value.
████████████████████████████████████████████████████████████████████████
357.Useful Methods Defined by the FileInfo Interface
Name Description
---------- ------------------------------------
Name() This method returns a string containing the name of the file or directory.
Size() This method returns the size of the file, expressed as an int64 value.
Mode() This method returns the file mode and permission settings for the file or directory.
ModTime() This method returns the last modified time of the file or directory.
████████████████████████████████████████████████████████████████████████
358.Stat(path)
The os Package Function for Inspecting a File
This function accepts a path string. It returns a FileInfo value that describes the file and an
error, which indicates problems inspecting the file.
████████████████████████████████████████████████████████████████████████
359.Enumerating Files
example:
main.go:
package main
import (
"os"
)
func main() {
path, err := os.Getwd()
if err == nil {
dirEntries, err := os.ReadDir(path)
if err == nil {
for _, dentry := range dirEntries {
Printfln("Entry name: %v, IsDir: %v", dentry.Name(), dentry.IsDir())
}
}
}
if err != nil {
Printfln("Error %v", err.Error())
}
}
Output:
Username: Alice
Entry name: .git, IsDir: true
Entry name: .vscode, IsDir: true
Entry name: README.md, IsDir: false
Entry name: U.sh, IsDir: false
Entry name: cheap.json, IsDir: false
Entry name: config.json, IsDir: false
Entry name: go.mod, IsDir: false
Entry name: main.go, IsDir: false
Entry name: output.txt, IsDir: false
Entry name: printer.go, IsDir: false
Entry name: product.go, IsDir: false
Entry name: readconfig.go, IsDir: false
████████████████████████████████████████████████████████████████████████
360.Determining Whether a File Exists
Checking Whether a File Exists:
main.go:
package main
import (
"os"
)
func main() {
targetFiles := []string { "no_such_file.txt", "config.json" }
for _, name := range targetFiles {
info, err := os.Stat(name)
if os.IsNotExist(err) {
Printfln("File does not exist: %v", name)
} else if err != nil {
Printfln("Other error: %v", err.Error())
} else {
Printfln("File %v, Size: %v", info.Name(), info.Size())
}
}
}
=============================
Output:
Username: Alice
File does not exist: no_such_file.txt
File config.json, Size: 253
████████████████████████████████████████████████████████████████████████
361.The path/filepath Function for Locating Files with a Pattern
Name Description
-------------------- -------------------------------
Match(pattern, name) This function matches a single path against a pattern. The results are a bool,
which indicates if there is a match, and an error, which indicates problems
with the pattern or with performing the match.
Glob(pathPatten) This function finds all the files that match the specified pattern. The results
are a string slice containing the matched paths and an error that indicates
problems with performing the search.
████████████████████████████████████████████████████████████████████████
362.The Search Pattern Syntax for the path/filepath Functions
Term Description
-------- -------------------
* This term matches any sequence of characters, excluding the path separator.
? This term matches any single character, excluding the path separator.
[a-Z] This term matches any character in the specified range.
████████████████████████████████████████████████████████████████████████
363.Locating Files
example:
main.go:
package main
import (
"os"
"path/filepath"
)
func main() {
path, err := os.Getwd()
if err == nil {
matches, err := filepath.Glob(filepath.Join(path, "*.json"))
if err == nil {
for _, m := range matches {
Printfln("Match: %v", m)
}
}
}
if err != nil {
Printfln("Error %v", err.Error())
}
}
=============================
Output:
Username: Alice
Match: /home/sina/0-Repo/TEST-2/cheap.json
Match: /home/sina/0-Repo/TEST-2/config.json
████████████████████████████████████████████████████████████████████████
364.The Function Provided by the path/filepath Package
Name Description
----------------------- ----------------------------------
WalkDir(directory, func) This function calls the specified function for each file and directory in the
specified directory.
████████████████████████████████████████████████████████████████████████
365.Walking a Directory
example:
main.go:
package main
import (
"os"
"path/filepath"
)
func callback(path string, dir os.DirEntry, dirErr error) (err error) {
info, _ := dir.Info()
Printfln("Path %v, Size: %v", path, info.Size())
return
}
func main() {
path, err := os.Getwd()
if err == nil {
err = filepath.WalkDir(path, callback)
} else {
Printfln("Error %v", err.Error())
}
}
=============================
Output:
Username: Alice
Path /home/sina/0-Repo/TEST-2, Size: 4096
Path /home/sina/0-Repo/TEST-2/.git, Size: 4096
Path /home/sina/0-Repo/TEST-2/.git/COMMIT_EDITMSG, Size: 4
Path /home/sina/0-Repo/TEST-2/.git/FETCH_HEAD, Size: 92
Path /home/sina/0-Repo/TEST-2/.git/refs/remotes/origin/main, Size: 41
Path /home/sina/0-Repo/TEST-2/.git/refs/tags, Size: 4096
Path /home/sina/0-Repo/TEST-2/.vscode, Size: 4096
Path /home/sina/0-Repo/TEST-2/.vscode/extensions.json, Size: 79
Path /home/sina/0-Repo/TEST-2/.vscode/settings.json, Size: 405
Path /home/sina/0-Repo/TEST-2/README.md, Size: 9
Path /home/sina/0-Repo/TEST-2/U.sh, Size: 68
Path /home/sina/0-Repo/TEST-2/cheap.json, Size: 487
Path /home/sina/0-Repo/TEST-2/config.json, Size: 253
Path /home/sina/0-Repo/TEST-2/go.mod, Size: 22
Path /home/sina/0-Repo/TEST-2/main.go, Size: 8579
Path /home/sina/0-Repo/TEST-2/output.txt, Size: 109
Path /home/sina/0-Repo/TEST-2/printer.go, Size: 129
Path /home/sina/0-Repo/TEST-2/product.go, Size: 474
Path /home/sina/0-Repo/TEST-2/readconfig.go, Size: 704
████████████████████████████████████████████████████████████████████████
366.Putting HTML and Text Templates in Context
Question Answer:
What are they?
These templates allow HTML and text content to be generated dynamically
from Go data values.
Why are they useful?
Templates are useful when large amounts of content are required, such that
defining the content as strings would be unmanageable.
How are they used?
The templates are HTML or text files, which are annotated with instructions
for the template processing engine. When a template is rendered, the
instructions are processed to generate HTML or text content.
Are there any pitfalls or limitations?
The template syntax is counterintuitive and is not checked by the Go
compiler. This means that care must be taken to use the correct syntax,
which can be a frustrating process.
Are there any alternatives?
Templates are optional, and smaller amounts of content can be produced
using strings.
Summary:
Problem Solution
------------------ -----------------------------------
Generate an HTML document Define an HTML template with actions that
incorporate data values into the output. Load and
execute the templates, providing data for the actions.
Enumerate loaded templates Enumerate the results of the Templates method.
Locate a specific template Use the Lookup method.
Produce dynamic content Use a template action.
Format a data value Use the formatting functions.
Suppress whitespace Add hyphens to the template.
Process a slice Use the slice functions.
Conditionally execute template content Use the conditional actions and functions.
Create a nested template Use the define and template actions.
Define a default template Use the block and template actions.
Create functions for use in a template Define template functions.
Disable encoding for function results Return one of the type aliases defined by the html/template package.
Store data values for later use in a template Define template variables.
Generate a text document Use the text/template package.
Preparing for This Chapter:
1- go mod init htmltext
2- printer.go:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
3- product.go:
package main
type Product struct {
Name, Category string
Price float64
}
var Kayak = Product {
Name: "Kayak",
Category: "Watersports",
Price: 279,
}
var Products = []Product {
{ "Kayak", "Watersports", 279 },
{ "Lifejacket", "Watersports", 49.95 },
{ "Soccer Ball", "Soccer", 19.50 },
{ "Corner Flags", "Soccer", 34.95 },
{ "Stadium", "Soccer", 79500 },
{ "Thinking Cap", "Chess", 16 },
{ "Unsteady Chair", "Chess", 75 },
{ "Bling-Bling King", "Chess", 1200 },
}
func (p *Product) AddTax() float64 {
return p.Price * 1.2
}
func (p * Product) ApplyDiscount(amount float64) float64 {
return p.Price - amount
}
4- main.go:
package main
func main() {
for _, p := range Products {
Printfln("Product: %v, Category: %v, Price: $%.2f",
p.Name, p.Category, p.Price)
}
}
████████████████████████████████████████████████████████████████████████
367.Creating HTML Templates
The html/template package provides support for creating templates that are processed using a data
structure to generate dynamic HTML output.
Templates contain static content mixed with expressions that are enclosed in double curly braces,
known as actions.
The template uses the simplest action, which is a period (the . character)
and which prints out the data used to execute the template,
which I explain in the next section.
example:
template.html:
<h1>Template Value: {{ . }}</h1>
A project can contain multiple templates files.
extras.html:
<h1>Extras Template Value: {{ . }}</h1>
The new template uses the same action as the previous example but has different static content to make
it clear which template has been executed in the next section. Once I have described the basic techniques for
using templates, I'll introduce more complex template actions.
████████████████████████████████████████████████████████████████████████
368.Loading and Executing Templates
Using templates is a two-step process.
First, the templates files are loaded and processed to create Template values.
The html/template Functions for Loading Template Files:
Name Description
--------------------- --------------------------------------------
ParseFiles(...files) This function loads one or more files, which are specified by name. The result
is a Template that can be used to generate content and an error that reports
problems loading the templates.
ParseGlob(pattern) This function loads one or more files, which are selected with a pattern. The
result is a Template that can be used to generate content and an error that
reports problems loading the templates.
If you name your template files consistently,
then you can use the ParseGlob function to load them with a simple pattern.
If you want specific files—or the files are not named consistently—then you can specify
individual files using the ParseFiles function.
████████████████████████████████████████████████████████████████████████
369.The Template Methods for Selecting and Executing Templates
Name Description
---------------------------- -------------------------------------------
Templates() This function returns a slice containing pointers to the Template values that
have been loaded.
Lookup(name) This function returns a *Template for the specified loaded template.
Name() This method returns the name of the Template.
Execute(writer, data) This function executes the Template, using the specified data and writes
the output to the specified Writer.
ExecuteTemplate(writer, templateName, data) This function executes the template with the specified name and data and
writes the output to the specified Writer.
████████████████████████████████████████████████████████████████████████
370.Loading and Executing a Template
example:
main.go:
package main
import (
"fmt"
"html/template"
"os"
)
func main() {
t, err := template.ParseFiles("templates/template.html")
if (err == nil) {
t.Execute(os.Stdout, &Kayak)
fmt.Println()
} else {
Printfln("Error: %v", err.Error())
}
}
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
████████████████████████████████████████████████████████████████████████
371.Loading Multiple Templates
There are two approaches to working with multiple templates.
The first is to create a separate Template value
for each of them and execute them separately.
example:
Using Separate Templates:
main.go:
package main
import (
"fmt"
"html/template"
"os"
)
func main() {
t1, err1 := template.ParseFiles("templates/template.html")
t2, err2 := template.ParseFiles("templates/extras.html")
if (err1 == nil && err2 == nil) {
t1.Execute(os.Stdout, &Kayak)
os.Stdout.WriteString("\n")
t2.Execute(os.Stdout, &Kayak)
os.Stdout.WriteString("\n")
} else {
Printfln("Error: %v %v", err1.Error(), err2.Error())
}
}
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
<h1>Extras Template Value: {Kayak Watersports 279}</h1>
████████████████████████████████████████████████████████████████████████
372.Using a Combined Template
When multiple files are loaded with the ParseFiles,
the result is a Template value on which the
ExecuteTemplate method can be called to execute a specified template.
The filename is used as the template name,
which means that the templates in this example are named template.html and extras.html.
You can call the Execute method on the Template returned by the ParseFiles or ParseGlob
function, and the first template that was loaded will be selected
and used to produce the output.
Take care when using the ParseGlob function because
the first template loaded—and therefore the template that will be
executed—may not be the file you expect.
example:
main.go:
package main
import (
"html/template"
"os"
)
func main() {
allTemplates, err1 := template.ParseFiles("templates/template.html",
"templates/extras.html")
if (err1 == nil) {
allTemplates.ExecuteTemplate(os.Stdout, "template.html", &Kayak)
os.Stdout.WriteString("\n")
allTemplates.ExecuteTemplate(os.Stdout, "extras.html", &Kayak)
} else {
Printfln("Error: %v %v", err1.Error())
}
}
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
<h1>Extras Template Value: {Kayak Watersports 279}</h1>
████████████████████████████████████████████████████████████████████████
373.Enumerating Loaded Templates
It can be useful to enumerate the templates that have been loaded,
especially when using the ParseGlob function,
to make sure that all the expected files have been discovered.
example:
main.go:
package main
import (
"html/template"
)
func main() {
allTemplates, err := template.ParseGlob("templates/*.html")
if (err == nil) {
for _, t := range allTemplates.Templates() {
Printfln("Template name: %v", t.Name())
}
} else {
Printfln("Error: %v %v", err.Error())
}
}
=============================
Output:
Template name: extras.html
Template name: template.html
████████████████████████████████████████████████████████████████████████
374.Looking Up a Specific Template
An alternative to specifying a name is to use the Lookup method to select a template,
which is useful when
you want to pass a template as an argument to a function
example:
main.go:
package main
import (
"html/template"
"os"
)
func Exec(t *template.Template) error {
return t.Execute(os.Stdout, &Kayak)
}
func main() {
allTemplates, err := template.ParseGlob("templates/*.html")
if err == nil {
selectedTemplated := allTemplates.Lookup("template.html")
err = Exec(selectedTemplated)
}
if err != nil {
Printfln("Error: %v %v", err.Error())
}
}
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
████████████████████████████████████████████████████████████████████████
375.The Template Actions
Action Description
------------ -----------------------------------------
{{ value }} This action inserts a data value or the result of an expression into the
{{ expr }} template. A period is used to refer to the data value passed to the Execute or
ExecuteTemplate function. See the “Inserting Data Values” section for details.
{{ value.fieldname }} This action inserts the value of a struct field. See the “Inserting Data Values” section for details.
{{ value.method arg }} This action invokes a method and inserts the result into the template
output. Parentheses are not used, and arguments are separated by
spaces. See the “Inserting Data Values” section for details.
{{ func arg }} This action invokes a function and inserts the result into the output.
There are built-in functions for common tasks, such as formatting
data values, and custom functions can be defined, as described in the
“Defining Template Functions” section.
{{ expr | value.method }} Expressions can be chained together using a vertical bar so that the result
{{ expr | func of the first expression is used as the last argument in the second expression.
{{ range value }} This action iterates through the specified slice and adds the content
... between the range and end keyword for each element. The actions within
{{ end }} the nested content are executed, with the current element accessible
through the period. See the “Using Slices in Templates” section for details.
{{ range value }} This action is similar to the range/end combination but defines a section
... of nested content that is used if the slice contains no elements.
{{ else }}
...
{{ end }}
{{ if expr }} This action evaluates an expression and executes the nested template
... content if the result is true, as demonstrated in the “Conditionally
{{ end }} Executing Template Content” section. This action can be used with
optional else and else if clauses.
{{ with expr }} This action evaluates an expression and executes the nested template
... content if the result isn't nil or the empty string. This action can be used
{{ end }} with optional clauses.
{{ define "name" }} This action defines a template with the specified name
...
{{ end }}
{{ template "name" expr }} This action executes the template with the specified name and data and
inserts the result in the output.
{{ block "name" expr }} This action defines a template with the specified name and invokes it
... with the specified data. This is typically used to define a template that
{{ end }} can be replaced by one loaded from another file, as demonstrated in the
“Defining Template Blocks” section.
████████████████████████████████████████████████████████████████████████
376.The Template Expressions for Inserting Values into Templates
Inserting Data Values
Expression Description
------------ -----------------------------------------------
. This expression inserts the value passed to the Execute or ExecuteTemplate method into the
template output.
.Field This expression inserts the value of the specified field into the template output.
.Method This expression calls the specified method without arguments and inserts the result into the
template output.
.Method This expression calls the specified method with the specified argument and inserts the result
arg into the template output.
call This expression invokes a struct function field, using the specified arguments, which are
.Field arg separated by spaces. The result from the function is inserted into the template output.
████████████████████████████████████████████████████████████████████████
377.Inserting Data Values in the template.html
Unlike Go code, methods are not invoked with parentheses, and arguments
are simply specified after the name, separated by spaces.
It is the responsibility of the developer to ensure
that arguments are of a type that can be used by the method or function.
example:
templates/template.html:
<h1>Template Value: {{ . }}</h1>
<h1>Name: {{ .Name }}</h1>
<h1>Category: {{ .Category }}</h1>
<h1>Price: {{ .Price }}</h1>
<h1>Tax: {{ .AddTax }}</h1>
<h1>Discount Price: {{ .ApplyDiscount 10 }}</h1>
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
<h1>Name: Kayak</h1>
<h1>Category: Watersports</h1>
<h1>Price: 279</h1>
<h1>Tax: 334.8</h1>
<h1>Discount Price: 269</h1>
████████████████████████████████████████████████████████████████████████
378.Understanding Contextual Escaping
Values are automatically escaped to make them safe for inclusion in HTML, CSS, and JavaScript code,
with the appropriate escaping rules applied based on context. For example, a string value such as
"It was a <big> boat" used as the text content of an HTML element would be inserted into the
template as "It was a <big> boat" but as "It was a \u003cbig\u003e boat" when used
as a string literal value in JavaScript code. Full details of how values are escaped can be found at
https://golang.org/pkg/html/template.
████████████████████████████████████████████████████████████████████████
379.The Built-in Templates Functions for Formatting Data
Name Description
------- --------------------------------
print This is an alias to the fmt.Sprint function.
printf This is an alias to the fmt.Sprintf function.
println This is an alias to the fmt.Sprintln function.
html This function encodes a value for safe inclusion in an HTML document.
js This function encodes a value for safe inclusion in a JavaScript document.
urlquery This function encodes a value for use in a URL query string.
example:
template.html:
<h1>Template Value: {{ . }}</h1>
<h1>Name: {{ .Name }}</h1>
<h1>Category: {{ .Category }}</h1>
<h1>Price: {{ printf "$%.3f" .Price }}</h1>
<h1>Tax: {{ printf "$%.2f" .AddTax }}</h1>
<h1>Discount Price: {{ .ApplyDiscount 10 }}</h1>
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
<h1>Name: Kayak</h1>
<h1>Category: Watersports</h1>
<h1>Price: $279.000</h1>
<h1>Tax: $334.80</h1>
<h1>Discount Price: 269</h1>
████████████████████████████████████████████████████████████████████████
380.Chaining Expressions
Chaining expressions creates a pipeline for values,
which allows the output from one method or function
to be used as the input for another.
example:
template.html:
<h1>Template Value: {{ . }}</h1>
<h1>Name: {{ .Name }}</h1>
<h1>Category: {{ .Category }}</h1>
<h1>Price: {{ printf "$%.2f" .Price }}</h1>
<h1>Tax: {{ printf "$%.2f" .AddTax }}</h1>
<h1>Discount Price: {{ .ApplyDiscount 10 | printf "$%.2f" }}</h1>
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
<h1>Name: Kayak</h1>
<h1>Category: Watersports</h1>
<h1>Price: $279.00</h1>
<h1>Tax: $334.80</h1>
<h1>Discount Price: $269.00</h1>
████████████████████████████████████████████████████████████████████████
381.Using Parentheses in html
Chaining can be used only for the last argument provided to a function.
An alternative approach—and
one that can be used to set other function arguments is to use parentheses
example:
template.html:
<h1>Template Value: {{ . }}</h1>
<h1>Name: {{ .Name }}</h1>
<h1>Category: {{ .Category }}</h1>
<h1>Price: {{ printf "$%.2f" .Price }}</h1>
<h1>Tax: {{ printf "$%.2f" .AddTax }}</h1>
<h1>Discount Price: {{ printf "$%.2f" (.ApplyDiscount 10) }}</h1>
=============================
Output:
<h1>Template Value: {Kayak Watersports 279}</h1>
<h1>Name: Kayak</h1>
<h1>Category: Watersports</h1>
<h1>Price: $279.00</h1>
<h1>Tax: $334.80</h1>
<h1>Discount Price: $269.00</h1>
████████████████████████████████████████████████████████████████████████
382.Trimming Whitespace
HTML isn't sensitive to the whitespace between elements,
but whitespace can still cause problems for text content and attribute values,
especially when you want to structure the content
of a template to make it easy to read.
example:
template.html
<h1>
Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{ printf "$%.2f" .Price }}
</h1>
=============================
Output:
<h1>
Name: Kayak, Category: Watersports, Price,
$279.00
</h1>
████████████████████████████████████████████████████████████████████████
383.The minus sign must
The effect is to remove all of the whitespace to before or after the action.
The minus sign can be used to trim whitespace,
applied immediately after or before the braces
that open or close an action.
example:
template.html:
<h1>
Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{ printf "$%.2f" .Price }}
</h1>
<h1>
Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- printf "$%.2f" .Price -}}
</h1>
=============================
Output:
<h1>
Name: Kayak, Category: Watersports, Price,
$279.00
</h1>
<h1>
Name: Kayak, Category: Watersports, Price,$279.00</h1>
████████████████████████████████████████████████████████████████████████
384.Trimming Additional Whitespace
The whitespace around the final action has been removed,
but there is still a newline character after
the opening h1 tag because the whitespace trimming applies only to actions.
example:
template.html:
<h1>
{{- "" -}} Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- printf "$%.2f" .Price -}}
</h1>
=============================
Output:
<h1>Name: Kayak, Category: Watersports, Price,$279.00</h1>
████████████████████████████████████████████████████████████████████████
385.Slices in Templates
Template actions can be used to generate content for slices
example:
Processing a Slice in the template.html
{{ range . -}}
<h1>Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- printf "$%.2f" .Price }}</h1>
{{ end }}
main.go:
package main
import (
"html/template"
"os"
)
func Exec(t *template.Template) error {
return t.Execute(os.Stdout, Products)
}
func main() {
allTemplates, err := template.ParseGlob("templates/*.html")
if err == nil {
selectedTemplated := allTemplates.Lookup("template.html")
err = Exec(selectedTemplated)
}
if err != nil {
Printfln("Error: %v %v", err.Error())
}
}
=============================
Output:
<h1>Name: Kayak, Category: Watersports, Price,$279.00</h1>
<h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1>
<h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1>
<h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1>
<h1>Name: Stadium, Category: Soccer, Price,$79500.00</h1>
<h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1>
<h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1>
<h1>Name: Bling-Bling King, Category: Chess, Price,$1200.00</h1>
████████████████████████████████████████████████████████████████████████
386.The Built-in Template Functions for Slices
Go text templates support the built-in functions
Name Description
------ --------------
slice This function creates a new slice. Its arguments are the original slice, the start index, and the end index.
index This function returns the element at the specified index.
len This function returns the length of the specified slice.
example:
Built-in Functions in the template.html:
<h1>There are {{ len . }} products in the source data.</h1>
<h1>First product: {{ index . 0 }}</h1>
{{ range slice . 3 6 -}}
<h1>Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- printf "$%.2f" .Price }}</h1>
{{ end }}
=============================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>First product: {Kayak Watersports 279}</h1>
<h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1>
<h1>Name: Stadium, Category: Soccer, Price,$79500.00</h1>
<h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1>
████████████████████████████████████████████████████████████████████████
387.Conditionally Executing Template Content
Actions can be used to conditionally insert content into the output based on the evaluation of their
expressions.
The Template Conditional Functions:
Function Description
------------ -----------------------------------------------
eq arg1 arg2 This function returns true if arg1 == arg2.
ne arg1 arg2 This function returns true if arg1 != arg2.
lt arg1 arg2 This function returns true if arg1 < arg2.
le arg1 arg2 This function returns true if arg1 <= arg2.
gt arg1 arg2 This function returns true if arg1 > arg2.
ge arg1 arg2 This function returns true if arg1 >= arg2.
and arg1 arg2 This function returns true if both arg1 and arg2 are true.
not arg1 This function returns true if arg1 is false, and false if it is true.
example:
a Conditional Action in the template.html:
<h1>There are {{ len . }} products in the source data.</h1>
<h1>First product: {{ index . 0 }}</h1>
{{ range . -}}
{{ if lt .Price 100.00 -}}
<h1>Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- printf "$%.2f" .Price }}</h1>
{{ end -}}
{{ end }}
=============================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>First product: {Kayak Watersports 279}</h1>
<h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1>
<h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1>
<h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1>
<h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1>
<h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1>
Despite the use of the minus sign to trim whitespace,
the output is oddly formatted because of the
way I chose to structure the template.
████████████████████████████████████████████████████████████████████████
388.Using the Optional Conditional Actions
The if action can be used with optional else and else if keywords
example:
template.html:
<h1>There are {{ len . }} products in the source data.</h1>
<h1>First product: {{ index . 0 }}</h1>
{{ range . -}}
{{ if lt .Price 100.00 -}}
<h1>Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- printf "$%.2f" .Price }}</h1>
{{ else if gt .Price 1500.00 -}}
<h1>Expensive Product {{ .Name }} ({{ printf "$%.2f" .Price}})</h1>
{{ else -}}
<h1>Midrange Product: {{ .Name }} ({{ printf "$%.2f" .Price}})</h1>
{{ end -}}
{{ end }}
main.go:
package main
import (
"html/template"
"os"
)
func Exec(t *template.Template) error {
return t.Execute(os.Stdout, Products)
}
func main() {
allTemplates, err := template.ParseGlob("templates/*.html")
if err == nil {
selectedTemplated := allTemplates.Lookup("template.html")
err = Exec(selectedTemplated)
}
if err != nil {
Printfln("Error: %v %v", err.Error())
}
}
=============================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>First product: {Kayak Watersports 279}</h1>
<h1>Midrange Product: Kayak ($279.00)</h1>
<h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1>
<h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1>
<h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1>
<h1>Expensive Product Stadium ($79500.00)</h1>
<h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1>
<h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1>
<h1>Midrange Product: Bling-Bling King ($1200.00)</h1>
████████████████████████████████████████████████████████████████████████
389.Creating Named Nested Templates
The define action is used to create a nested template that can be executed by name,
which allows content to
be defined once and used repeatedly with the template action
example:
template.html:
{{ define "currency" }}{{ printf "$%.2f" . }}{{ end }}
{{ define "basicProduct" -}}
Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- template "currency" .Price }}
{{- end }}
{{ define "expensiveProduct" -}}
Expensive Product {{ .Name }} ({{ template "currency" .Price }})
{{- end }}
<h1>There are {{ len . }} products in the source data.</h1>
<h1>First product: {{ index . 0 }}</h1>
{{ range . -}}
{{ if lt .Price 100.00 -}}
<h1>{{ template "basicProduct" . }}</h1>
{{ else if gt .Price 1500.00 -}}
<h1>{{ template "expensiveProduct" . }}</h1>
{{ else -}}
<h1>Midrange Product: {{ .Name }} ({{ printf "$%.2f" .Price}})</h1>
{{ end -}}
{{ end }}
=============================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>First product: {Kayak Watersports 279}</h1>
<h1>Midrange Product: Kayak ($279.00)</h1>
<h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1>
<h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1>
<h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1>
<h1>Expensive Product Stadium ($79500.00)</h1>
<h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1>
<h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1>
<h1>Midrange Product: Bling-Bling King ($1200.00)</h1>
The define keyword is followed by the template name in quotes,
and the template is terminated by the end keyword.
The template keyword is used to execute a named template,
specifying the template name and a data value:
...
{{- template "currency" .Price }}
...
████████████████████████████████████████████████████████████████████████
390.Selecting a Named Template in the main.go
Using the define and end keywords for the main template content excludes the whitespace used to
separate the other named templates.
Adding a Named Template in the template.html:
{{ define "currency" }}{{ printf "$%.2f" . }}{{ end }}
{{ define "basicProduct" -}}
Name: {{ .Name }}, Category: {{ .Category }}, Price,
{{- template "currency" .Price }}
{{- end }}
{{ define "expensiveProduct" -}}
Expensive Product {{ .Name }} ({{ template "currency" .Price }})
{{- end }}
{{ define "mainTemplate" -}}
<h1>There are {{ len . }} products in the source data.</h1>
<h1>First product: {{ index . 0 }}</h1>
{{ range . -}}
{{ if lt .Price 100.00 -}}
<h1>{{ template "basicProduct" . }}</h1>
{{ else if gt .Price 1500.00 -}}
<h1>{{ template "expensiveProduct" . }}</h1>
{{ else -}}
<h1>Midrange Product: {{ .Name }} ({{ printf "$%.2f" .Price}})</h1>
{{ end -}}
{{ end }}
{{- end}}
=============================
main.go:
package main
import (
"html/template"
"os"
)
func Exec(t *template.Template) error {
return t.Execute(os.Stdout, Products)
}
func main() {
allTemplates, err := template.ParseGlob("templates/*.html")
if (err == nil) {
selectedTemplated := allTemplates.Lookup("mainTemplate")
err = Exec(selectedTemplated)
}
if (err != nil) {
Printfln("Error: %v %v", err.Error())
}
}
=============================
Any of the named templates can be executed directly, but I have selected the mainTemplate
Output:
<h1>There are 8 products in the source data.</h1>
<h1>First product: {Kayak Watersports 279}</h1>
<h1>Midrange Product: Kayak ($279.00)</h1>
<h1>Name: Lifejacket, Category: Watersports, Price,$49.95</h1>
<h1>Name: Soccer Ball, Category: Soccer, Price,$19.50</h1>
<h1>Name: Corner Flags, Category: Soccer, Price,$34.95</h1>
<h1>Expensive Product Stadium ($79500.00)</h1>
<h1>Name: Thinking Cap, Category: Chess, Price,$16.00</h1>
<h1>Name: Unsteady Chair, Category: Chess, Price,$75.00</h1>
<h1>Midrange Product: Bling-Bling King ($1200.00)</h1>
████████████████████████████████████████████████████████████████████████
391.Defining Template Blocks
Template blocks are used to define a template with default content that can be overridden in another
template file, which requires multiple templates to be loaded and executed together.
This is often used to common content, such as a layout.
The templates must be loaded so that the file that contains the block action is loaded before the file that
contains the define action that redefines the template.
When the templates are loaded, the template defined
in the list.html file redefines the template named body so that the content in the list.html file replaces
the content in the template.html file.
example:
template.html File in the templates Folder
{{ define "mainTemplate" -}}
<h1>This is the layout header</h1>
{{ block "body" . }}
<h2>There are {{ len . }} products in the source data.</h2>
{{ end }}
<h1>This is the layout footer</h1>
{{ end }}
Output:
<h1>This is the layout header</h1>
<h2>There are 8 products in the source data.</h2>
<h1>This is the layout footer</h1>
When used alone, the output from the template file includes the content in the block.
But this content can be redefined by another template file.
example:
list.html:
{{ define "body" }}
{{ range . }}
<h2>Product: {{ .Name }} ({{ printf "$%.2f" .Price}})</h2>
{{ end -}}
{{ end }}
========================
main.go:
package main
import (
"html/template"
"os"
)
func Exec(t *template.Template) error {
return t.Execute(os.Stdout, Products)
}
func main() {
allTemplates, err := template.ParseFiles("templates/template.html","templates/list.html")
if err == nil {
selectedTemplated := allTemplates.Lookup("mainTemplate")
err = Exec(selectedTemplated)
}
if err != nil {
Printfln("Error: %v %v", err.Error())
}
}
=============================
Output:
<h1>This is the layout header</h1>
<h2>Product: Kayak ($279.00)</h2>
<h2>Product: Lifejacket ($49.95)</h2>
<h2>Product: Soccer Ball ($19.50)</h2>
<h2>Product: Corner Flags ($34.95)</h2>
<h2>Product: Stadium ($79500.00)</h2>
<h2>Product: Thinking Cap ($16.00)</h2>
<h2>Product: Unsteady Chair ($75.00)</h2>
<h2>Product: Bling-Bling King ($1200.00)</h2>
<h1>This is the layout footer</h1>
████████████████████████████████████████████████████████████████████████
392.Defining Template Functions
example:
main.go File in the htmltext Folder:
package main
import (
"html/template"
"os"
)
func GetCategories(products []Product) (categories []string) {
catMap := map[string]string {}
for _, p := range products {
if (catMap[p.Category] == "") {
catMap[p.Category] = p.Category
categories = append(categories, p.Category)
}
}
return
}
func Exec(t *template.Template) error {
return t.Execute(os.Stdout, Products)
}
func main() {
allTemplates := template.New("allTemplates")
allTemplates.Funcs(map[string]interface{} {
"getCats": GetCategories,
})
allTemplates, err := allTemplates.ParseGlob("templates/*.html")
if (err == nil) {
selectedTemplated := allTemplates.Lookup("mainTemplate")
err = Exec(selectedTemplated)
}
if (err != nil) {
Printfln("Error: %v %v", err.Error())
}
}
=============================
Output:
<h1>This is the layout header</h1>
<h2>There are 8 products in the source data.</h2>
<h1>This is the layout footer</h1>
████████████████████████████████████████████████████████████████████████
393.Using a Custom Function in the template.html
example:
template.html:
{{ define "mainTemplate" -}}
<h1>There are {{ len . }} products in the source data.</h1>
{{ range getCats . -}}
<h1>Category: {{ . }}</h1>
{{ end }}
{{- end }}
=============================================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>Category: Watersports</h1>
<h1>Category: Soccer</h1>
<h1>Category: Chess</h1>
████████████████████████████████████████████████████████████████████████
394.Creating an HTML Fragment in the main.go
example:
main.go:
...
func GetCategories(products []Product) (categories []string) {
catMap := map[string]string {}
for _, p := range products {
if (catMap[p.Category] == "") {
catMap[p.Category] = p.Category
categories = append(categories, "<b>p.Category</b>")
}
}
return
}
...
===============================================================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>Category: <b>p.Category</b></h1>
<h1>Category: <b>p.Category</b></h1>
<h1>Category: <b>p.Category</b></h1>
████████████████████████████████████████████████████████████████████████
395.The Types Aliases Used to Denote Content Types
Name Description
-------- -----------
CSS This type denotes CSS content.
HTML This type denotes a fragment of HTML.
HTMLAttr This type denotes a value that will be used as the value for an HTML attribute.
JS This type denotes a fragment of JavaScript code.
JSStr This type denotes a value that is intended to appear between quotes in a JavaScript expression.
Srcset This type denotes a value that can be used in the srcset attribute of an img element.
URL This type denotes a URL.
████████████████████████████████████████████████████████████████████████
396.Returning HTML Content in the main.go
example:
main.go:
...
func GetCategories(products []Product) (categories []template.HTML) {
catMap := map[string]string {}
for _, p := range products {
if (catMap[p.Category] == "") {
catMap[p.Category] = p.Category
categories = append(categories, "<b>p.Category</b>")
}
}
return
}
...
=======================================================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>Category: <b>p.Category</b></h1>
<h1>Category: <b>p.Category</b></h1>
<h1>Category: <b>p.Category</b></h1>
████████████████████████████████████████████████████████████████████████
397.Providing Access to Standard Library Functions
Adding a Function Mapping in the main.go
example:
main.go:
package main
import (
"html/template"
"os"
"strings"
)
func GetCategories(products []Product) (categories []string) {
catMap := map[string]string{}
for _, p := range products {
if catMap[p.Category] == "" {
catMap[p.Category] = p.Category
categories = append(categories, p.Category)
}
}
return
}
func Exec(t *template.Template) error {
return t.Execute(os.Stdout, Products)
}
func main() {
allTemplates := template.New("allTemplates")
allTemplates.Funcs(map[string]interface{}{
"getCats": GetCategories,
"lower": strings.ToLower,
})
allTemplates, err := allTemplates.ParseGlob("templates/*.html")
if err == nil {
selectedTemplated := allTemplates.Lookup("mainTemplate")
err = Exec(selectedTemplated)
}
if err != nil {
Printfln("Error: %v %v", err.Error())
}
}
=============================================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>Category: Watersports</h1>
<h1>Category: Soccer</h1>
<h1>Category: Chess</h1>
████████████████████████████████████████████████████████████████████████
398.Using a Template Function in the template.html
example:
template.html:
{{ define "mainTemplate" -}}
<h1>There are {{ len . }} products in the source data.</h1>
{{ range getCats . -}}
<h1>Category: {{ lower . }}</h1>
{{ end }}
{{- end }}
=============================================================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>Category: watersports</h1>
<h1>Category: soccer</h1>
<h1>Category: chess</h1>
████████████████████████████████████████████████████████████████████████
399.Defining Template Variables
Defining and Using a Template Variable in the template.html
example:
template.html:
{{ define "mainTemplate" -}}
{{ $length := len . }}
<h1>There are {{ $length }} products in the source data.</h1>
{{ range getCats . -}}
<h1>Category: {{ lower . }}</h1>
{{ end }}
{{- end }}
=============================================================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>Category: watersports</h1>
<h1>Category: soccer</h1>
<h1>Category: chess</h1>
████████████████████████████████████████████████████████████████████████
400.Defining and Using a Template Variable in the template.html
example:
template.html:
{{ define "mainTemplate" -}}
<h1>There are {{ len . }} products in the source data.</h1>
{{- range getCats . -}}
{{ if ne ($char := slice (lower .) 0 1) "s" }}
<h1>{{$char}}: {{.}}</h1>
{{- end }}
{{- end }}
{{- end }}
==============================================
Output:
<h1>There are 8 products in the source data.</h1>
<h1>w: Watersports</h1>
<h1>c: Chess</h1>
████████████████████████████████████████████████████████████████████████
401.Using Template Variables in Range Actions
Enumerating a Map in the template.html
example:
main.go:
...
func Exec(t *template.Template) error {
productMap := map[string]Product {}
for _, p := range Products {
productMap[p.Name] = p
}
return t.Execute(os.Stdout, &productMap)
}
...
template.html:
{{ define "mainTemplate" -}}
{{ range $key, $value := . -}}
<h1>{{ $key }}: {{ printf "$%.2f" $value.Price }}</h1>
{{ end }}
{{- end }}
Output:
<h1>Bling-Bling King: $1200.00</h1>
<h1>Corner Flags: $34.95</h1>
<h1>Kayak: $279.00</h1>
<h1>Lifejacket: $49.95</h1>
<h1>Soccer Ball: $19.50</h1>
<h1>Stadium: $79500.00</h1>
<h1>Thinking Cap: $16.00</h1>
<h1>Unsteady Chair: $75.00</h1>
████████████████████████████████████████████████████████████████████████
402.Creating Text Templates
Loading and Executing a Text Template in the main.go
The Contents of the template.txt
example:
templates/template.txt:
{{ define "mainTemplate" -}}
{{ range $key, $value := . -}}
{{ $key }}: {{ printf "$%.2f" $value.Price }}
{{ end }}
{{- end }}
---------------------------------------
main.go:
package main
import (
"text/template"
"os"
"strings"
)
func GetCategories(products []Product) (categories []string) {
catMap := map[string]string {}
for _, p := range products {
if (catMap[p.Category] == "") {
catMap[p.Category] = p.Category
categories = append(categories, p.Category)
}
}
return
}
func Exec(t *template.Template) error {
productMap := map[string]Product {}
for _, p := range Products {
productMap[p.Name] = p
}
return t.Execute(os.Stdout, &productMap)
}
func main() {
allTemplates := template.New("allTemplates")
allTemplates.Funcs(map[string]interface{} {
"getCats": GetCategories,
"lower": strings.ToLower,
})
allTemplates, err := allTemplates.ParseGlob("templates/*.txt")
if (err == nil) {
selectedTemplated := allTemplates.Lookup("mainTemplate")
err = Exec(selectedTemplated)
}
if (err != nil) {
Printfln("Error: %v %v", err.Error())
}
}
Output:
Bling-Bling King: $1200.00
Corner Flags: $34.95
Kayak: $279.00
Lifejacket: $49.95
Soccer Ball: $19.50
Stadium: $79500.00
Thinking Cap: $16.00
Unsteady Chair: $75.00
████████████████████████████████████████████████████████████████████████
403.Creating HTTP Servers
What are they?
The features described in this chapter make it easy for Go applications to create HTTP servers.
Why are they useful?
HTTP is one of the most widely used protocols and is useful for both user-facing
applications and web services.
How is it used?
The features of the net/http package are used to create a server and handle requests.
Are there any pitfalls or limitations?
These features are well-designed and easy to use.
Are there any alternatives?
The standard library includes support for other network protocols and also for
opening and using lower-level network connections.
See https://pkg.go.dev/net@go1.17.1 for details of the net package and its subpackages,
such as net/smtp, for example, which implements the SMTP protocol.
Problem Solution
-------- -------------
Create an HTTP or HTTPS server Use the ListenAndServe or ListenAndServeTLS functions
Inspect an HTTP request Use the features of the Request struct
Produce a response Use the ResponseWriter interface or the
convenience functions
Handle requests to specific URLs Use the integrated router
Serve static content Use the FileServer and StripPrefix function
Use a template to produce a response Write the content to the ResponseWriter
or produce a JSON response
Handle form data Use the Request methods
Set or read cookies Use the Cookie, Cookies, and SetCookie methods
Preparing for This Chapter:
1- go mod init httpserver
2- printer.go:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
3- product.go:
package main
type Product struct {
Name, Category string
Price float64
var Products = []Product{
{"Kayak", "Watersports", 279},
{"Lifejacket", "Watersports", 49.95},
{"Soccer Ball", "Soccer", 19.50},
{"Corner Flags", "Soccer", 34.95},
{"Stadium", "Soccer", 79500},
{"Thinking Cap", "Chess", 16},
{"Unsteady Chair", "Chess", 75},
{"Bling-Bling King", "Chess", 1200},
}
4- main.go:
package main
func main() {
for _, p := range Products {
Printfln("Product: %v, Category: %v, Price: $%.2f",
p.Name, p.Category, p.Price)
}
}
=================================
Output:
Product: Kayak, Category: Watersports, Price: $279.00
Product: Lifejacket, Category: Watersports, Price: $49.95
Product: Soccer Ball, Category: Soccer, Price: $19.50
Product: Corner Flags, Category: Soccer, Price: $34.95
Product: Stadium, Category: Soccer, Price: $79500.00
Product: Thinking Cap, Category: Chess, Price: $16.00
Product: Unsteady Chair, Category: Chess, Price: $75.00
Product: Bling-Bling King, Category: Chess, Price: $1200.00
████████████████████████████████████████████████████████████████████████
404.Creating a Simple HTTP Server
example:
main.go:
package main
import (
"net/http"
"io"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter,
request *http.Request) {
io.WriteString(writer, sh.message)
}
func main() {
err := http.ListenAndServe(":5000", StringHandler{ message: "Hello, World"})
if (err != nil) {
Printfln("Error: %v", err.Error())
}
}
=================================
Output: go run .
in Web Browser, search localhost:5000/
Hello, World
████████████████████████████████████████████████████████████████████████
405.Creating the HTTP Listener and Handler
The net/http Convenience Functions
The ListenAndServe function starts listening for HTTP requests on a specified network address.
The ListenAndServeTLS function does the same for HTTP requests.
The addresses accepted by the functions can be used to restrict the HTTP server so that it
only accepts requests on a specific interface or to listen for requests on any interface.
Name Description
--------- ----------------------------------------
ListenAndServe(addr, handler) This function starts listening for HTTP requests on a specified
address and passes requests onto the specified handler.
ListenAndServeTLS(addr, cert, key, handler) This function starts listening for HTTPS requests. The arguments are the address
████████████████████████████████████████████████████████████████████████
406.The Method Defined by the Handler Interface
Name Description
------------------------ ----------------
ServeHTTP(writer, request) This method is invoked to process a HTTP request. The request is described by a
Request value, and the response is written using a ResponseWriter, both of which
are received as parameters.
████████████████████████████████████████████████████████████████████████
407.Inspecting the Request
The Basic Fields Defined by the Request Struct
Name Description
------- ------------------------
Method This field provides the HTTP method (GET, POST, etc.) as a string. The net/http package defines
constants for the HTTP methods, such as MethodGet and MethodPost.
URL This field returns the requested URL, expressed as a URL value.
Proto This field returns a string that indicates the version of HTTP used for the request.
Host This field returns a string containing the requested hos.
Header This field returns a Header value, which is an alias to map[string][]string and contains the
request headers. The map keys are the names of the headers, and the values are string slices
containing the header values.
Trailer This field returns a map[string]string that contains any additional headers that are included in
the request after the body.
Body This filed returns a ReadCloser, which is an interface that combines the Read method of the
Reader interface with the Close method of the Closer interface
████████████████████████████████████████████████████████████████████████
408.Writing Request Fields in the main.go
example:
main.go:
package main
import (
"io"
"net/http"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
Printfln("Method: %v", request.Method)
Printfln("URL: %v", request.URL)
Printfln("HTTP Version: %v", request.Proto)
Printfln("Host: %v", request.Host)
for name, val := range request.Header {
Printfln("Header: %v, Value: %v", name, val)
}
Printfln("---")
io.WriteString(writer, sh.message)
}
func main() {
err := http.ListenAndServe(":5000", StringHandler{message: "Hello, World"})
if err != nil {
Printfln("Error: %v", err.Error())
}
}
===================================================================================
Output: Compile and execute the project and request http://localhost:5000
Method: GET
URL: /
HTTP Version: HTTP/1.1
Host: localhost:5000
Header: Accept, Value: [text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8]
Header: Accept-Language, Value: [en-US,en;q=0.5]
Header: Accept-Encoding, Value: [gzip, deflate, br]
Header: Sec-Fetch-Mode, Value: [navigate]
Header: Sec-Fetch-Site, Value: [none]
Header: User-Agent, Value: [Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0]
Header: Connection, Value: [keep-alive]
Header: Upgrade-Insecure-Requests, Value: [1]
Header: Sec-Fetch-Dest, Value: [document]
Header: Sec-Fetch-User, Value: [?1]
---
Method: GET
URL: /favicon.ico
HTTP Version: HTTP/1.1
Host: localhost:5000
Header: Accept-Encoding, Value: [gzip, deflate, br]
Header: Connection, Value: [keep-alive]
Header: Sec-Fetch-Dest, Value: [image]
Header: Sec-Fetch-Mode, Value: [no-cors]
Header: Sec-Fetch-Site, Value: [same-origin]
Header: User-Agent, Value: [Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/118.0]
Header: Accept, Value: [image/avif,image/webp,*/*]
Header: Accept-Language, Value: [en-US,en;q=0.5]
Header: Referer, Value: [http://localhost:5000/]
---
████████████████████████████████████████████████████████████████████████
409.Save All Logs in a txt file:
Server.go:
package server
import (
"io"
"log"
"net/http"
"os"
)
type StringHandler struct {
Message string
}
func MyServer() {
logFile, err := os.OpenFile("Server/LogFile/logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer logFile.Close()
log.SetOutput(logFile)
err = http.ListenAndServe(":5000", StringHandler{Message: "Hello, World"})
if err != nil {
log.Printf("Error: %v", err.Error())
}
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
log.Printf("Method: %v", request.Method)
log.Printf("URL: %v", request.URL)
log.Printf("HTTP Version: %v", request.Proto)
log.Printf("Host: %v", request.Host)
for name, val := range request.Header {
log.Printf("Header: %v, Value: %v", name, val)
}
io.WriteString(writer, sh.Message)
log.Printf("============================================◉🧭🧭🧭🧭🧭🧭🧭◉==========================================")
}
Output: Create a TXT file in Server/LogFile/logs.txt and save appendation in to it.
████████████████████████████████████████████████████████████████████████
410.Filtering Requests and Generating Responses
Useful Fields and Methods Defined by the URL Struct
The ResponseWriter interface defines the methods that are available when creating a response.
Name Description
------- -----------------------------
Scheme This field returns the scheme component of the URL.
Host This field returns the host component of the URL, which may include the port.
RawQuery This field returns the query string from the URL. Use the Query method to process the query
string into a map.
Path This field returns the path component of the URL.
Fragment This field returns the fragment component of the URL, without the # character.
Hostname() This method returns the hostname component of the URL as a string.
Port()T his method returns the port component of the URL as a string.
Query() This method returns a map[string][]string (a map with string keys and string slice
values), containing the query string fields.
User() This method returns the user information associated with the request.
String() This method returns a string representation of the URL.
████████████████████████████████████████████████████████████████████████
411.The ResponseWriter Methods
Name Description
-------------- ------------------------
Header() This method returns a Header, which is an alias to map[string][]string, that can be
used to set the response headers.
WriteHeader(code) This method sets the status code for the response, specified as an int. The net/http
package defines constants for most status codes.
Write(data) This method writes data to the response body and implements the Writer interface.
████████████████████████████████████████████████████████████████████████
412.Producing Difference Responses in the main.go
example:
main.go:
package main
import (
"net/http"
"io"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter,
request *http.Request) {
if (request.URL.Path == "/favicon.ico") {
Printfln("Request for icon detected - returning 404")
writer.WriteHeader(http.StatusNotFound)
return
}
Printfln("Request for %v", request.URL.Path)
io.WriteString(writer, sh.message)
}
func main() {
err := http.ListenAndServe(":5000", StringHandler{ message: "Hello, World"})
if (err != nil) {
Printfln("Error: %v", err.Error())
}
}
Output: in Terminal
Request for /
Request for icon detected - returning 404
████████████████████████████████████████████████████████████████████████
413.Get Logs and Handle request if URL not Exist:
example:
main.go:
package main
import (
"io"
"log"
"net/http"
"os"
)
type StringHandler struct {
Message string
}
func main() {
logFile, err := os.OpenFile("LogFile/logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer logFile.Close()
log.SetOutput(logFile)
err = http.ListenAndServe(":5000", StringHandler{Message: "Hello, World"})
if err != nil {
log.Printf("Error: %v", err.Error())
}
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
if (request.URL.Path != "/") {
Printfln("Request for icon detected - returning 404")
writer.WriteHeader(http.StatusNotFound)
return
}
Printfln("Request for %v", request.URL.Path)
io.WriteString(writer, sh.Message)
log.Printf("Method: %v", request.Method)
log.Printf("URL: %v", request.URL)
log.Printf("HTTP Version: %v", request.Proto)
log.Printf("Host: %v", request.Host)
for name, val := range request.Header {
log.Printf("Header: %v, Value: %v", name, val)
}
io.WriteString(writer, sh.Message)
log.Printf("============================================◉🧭🧭🧭🧭🧭🧭🧭◉==========================================")
}
Output: so we have to logs, one of that is logs about user request,
another that is about path URL in Terminal
████████████████████████████████████████████████████████████████████████
414.Using the Response Convenience Functions
Name Description
----------------------- ----------------------------
Error(writer, message, code) This function sets the header to the specified code, sets the Content-Type header
to text/plain, and writes the error message to the response. The X-Content-
Type-Options header is also set to stop browsers from interpreting the response as
anything other than text.
NotFound(writer, request) This function calls Error and specifies a 404 error code.
Redirect(writer, request, url, code) This function sends a redirection response to the specified URL and with the
specified status code.
ServeFile(writer, request, fileName) This function sends a response containing the contents of the specified file. The
Content-Type header is set based on the file name but can be overridden by
explicitly setting the header before calling the function. See the “Creating a Static
HTTP Server” section for an example that serves files.
████████████████████████████████████████████████████████████████████████
415.Convenience Functions in the main.go
example:
main.go:
package main
import (
"io"
"log"
"net/http"
"os"
)
type StringHandler struct {
Message string
}
func main() {
logFile, err := os.OpenFile("LogFile/logs.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatal(err)
}
defer logFile.Close()
log.SetOutput(logFile)
err = http.ListenAndServe(":5000", StringHandler{Message: "Hello, World"})
if err != nil {
log.Printf("Error: %v", err.Error())
}
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
Printfln("Request for %v", request.URL.Path)
switch request.URL.Path {
case "/favicon.ico":
http.NotFound(writer, request)
case "/message":
io.WriteString(writer, sh.Message)
default:
http.Redirect(writer, request, "/message", http.StatusTemporaryRedirect)
}
log.Printf("Method: %v", request.Method)
log.Printf("URL: %v", request.URL)
log.Printf("HTTP Version: %v", request.Proto)
log.Printf("Host: %v", request.Host)
for name, val := range request.Header {
log.Printf("Header: %v, Value: %v", name, val)
}
io.WriteString(writer, sh.Message)
log.Printf("============================================◉🧭🧭🧭🧭🧭🧭🧭◉==========================================")
}
Output: Will write in Terminal and File for feture analyzing.
████████████████████████████████████████████████████████████████████████
416.Using the Convenience Functions in the main.go
example:
main.go:
package main
import (
"io"
"net/http"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
Printfln("Request for %v", request.URL.Path)
switch request.URL.Path {
case "/favicon.ico":
http.NotFound(writer, request)
case "/message":
io.WriteString(writer, sh.message)
default:
http.Redirect(writer, request, "/message", http.StatusTemporaryRedirect)
}
}
func main() {
err := http.ListenAndServe(":5000", StringHandler{message: "Hello, World"})
if err != nil {
Printfln("Error: %v", err.Error())
}
}
Output:
every wrong links redirect to specific link.
uses a switch statement to decide how to respond to a request.
████████████████████████████████████████████████████████████████████████
417.Using the Convenience Routing Handler
The process of inspecting the URL and selecting a response can produce complex code that is difficult to
read and maintain.
To simplify the process, the net/http package provides a Handler implementation that
allows matching the URL to be separated from producing a request.
example:
main.go:
package main
import (
"io"
"net/http"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
Printfln("Request for %v", request.URL.Path)
io.WriteString(writer, sh.message)
}
func main() {
http.Handle("/message", StringHandler{"Hello, World"})
http.Handle("/favicon.ico", http.NotFoundHandler())
http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect))
err := http.ListenAndServe(":5000", nil)
if err != nil {
Printfln("Error: %v", err.Error())
}
}
Output:
don't show wrong search.
████████████████████████████████████████████████████████████████████████
418.The net/http Functions for Creating Routing Rules
Name Description
----------------- ---------------------------------
Handle(pattern, handler) This function creates a rule that invokes the specified ServeHTTP method of the
specified Hander for requests that match the pattern.
HandleFunc(pattern, handlerFunc) This function creates a rule that invokes the specified function for requests that match
the pattern. The function is invoked with ResponseWriter and Request arguments.
████████████████████████████████████████████████████████████████████████
419.he net/http Functions for Creating Request Handlers
Name Description
------------------------------- -----------------
FileServer(root) This function creates a Handler that produces responses using the ServeFile
function. See the “Creating a Static HTTP Server” section for an example that
serves files.
NotFoundHandler() This function creates a Handler that produces responses using the NotFound function.
RedirectHandler(url, code) This function creates a Handler that produces responses using the Redirect function.
StripPrefix(prefix, handler) This function creates a Handler that removes the specified prefix from the
request URL and passes on the request to the specified Handler. See the
“Creating a Static HTTP Server” section for details.
TimeoutHandler(handler, duration, message) This function passes on the request to the specified Handler but generates an error
response if the response hasn't been produced within the specified duration.
████████████████████████████████████████████████████████████████████████
420.Supporting HTTPS Requests
The net/http package provides integrated support for HTTPS.
To prepare for HTTPS, you will need to add
two files to the httpserver folder:
a certificate file and a private key file.
████████████████████████████████████████████████████████████████████████
421.Getting Certificates for HTTPS
A good way to get started with HTTPS is with a self-signed certificate,
which can be used for development and testing.
If you don't already have a self-signed certificate,
then you can create one online using sites such as
https://getacert.com
or
https://www.selfsignedcertificate.com
both of which will let you create a self-signed certificate easily
and without charge.
Two files are required to use HTTPS, regardless of whether your certificate
is self-signed or not. The first is the certificate file,
which usually has a cer or cert file extension.
The second is the private key file, which usually has a key file extension.
after ready to deploy:
https://letsencrypt.org
The ListenAndServeTLS function is used to enable HTTPS,
where the additional arguments specify the
certificate and private key files,
which are named certificate.cer and certificate.key in my project
████████████████████████████████████████████████████████████████████████
422.Enabling HTTPS in the main.go
example:
main.go:
package main
import (
"io"
"net/http"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
Printfln("Request for %v", request.URL.Path)
io.WriteString(writer, sh.message)
}
func main() {
http.Handle("/message", StringHandler{"Hello, World"})
http.Handle("/favicon.ico", http.NotFoundHandler())
http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect))
go func() {
err := http.ListenAndServeTLS(":5500", "certificate.cer",
"certificate.key", nil)
if err != nil {
Printfln("HTTPS Error: %v", err.Error())
}
}()
err := http.ListenAndServe(":5000", nil)
if err != nil {
Printfln("Error: %v", err.Error())
}
}
Output:
HTTPS Error: open certificate.cer: no such file or directory
Request for /message
████████████████████████████████████████████████████████████████████████
423.Redirecting HTTP Requests to HTTPS
A common requirement when creating web servers is to redirect HTTP requests to the HTTPS port.
This can be done by creating a custom handler
example:
Redirecting to HTTPS in the main.go:
package main
import (
"net/http"
"io"
"strings"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter,
request *http.Request) {
Printfln("Request for %v", request.URL.Path)
io.WriteString(writer, sh.message)
}
func HTTPSRedirect(writer http.ResponseWriter,
request *http.Request) {
host := strings.Split(request.Host, ":")[0]
target := "https://" + host + ":5500" + request.URL.Path
if len(request.URL.RawQuery) > 0 {
target += "?" + request.URL.RawQuery
}
http.Redirect(writer, request, target, http.StatusTemporaryRedirect)
}
func main() {
http.Handle("/message", StringHandler{ "Hello, World"})
http.Handle("/favicon.ico", http.NotFoundHandler())
http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect))
go func () {
err := http.ListenAndServeTLS(":5520", "certificate.cer",
"certificate.key", nil)
if (err != nil) {
Printfln("HTTPS Error: %v", err.Error())
}
}()
err := http.ListenAndServe(":5000", http.HandlerFunc(HTTPSRedirect))
if (err != nil) {
Printfln("Error: %v", err.Error())
}
}
Output:
Not work for my Browser but you have to try, maybe work it for you.
████████████████████████████████████████████████████████████████████████
424.Creating a Static HTTP Server
The net/http package includes built-in support for responding to requests with the contents of files.
To prepare for the static HTTP server,
create the httpserver/static folder and add to it a file named index.html
example:
static/index.html:
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
<link href="bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="m-1 p-2 bg-primary text-white h2">
Hello, World
</div>
</body>
</html>
store.html:
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
<link href="bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="m-1 p-2 bg-primary text-white h2 text-center">
Products
</div>
<table class="table table-sm table-bordered table-striped">
<thead>
<tr>
<th>Name</th>
<th>Category</th>
<th>Price</th>
</tr>
</thead>
<tbody>
<tr>
<td>Kayak</td>
<td>Watersports</td>
<td>$279.00</td>
</tr>
<tr>
<td>Lifejacket</td>
<td>Watersports</td>
<td>$49.95</td>
</tr>
</tbody>
</table>
</body>
</html>
The HTML files depend on the Bootstrap CSS package to style the HTML content. Run the command
shown in here in the httpserver folder to download the Bootstrap CSS file into the static folder.
(You may have to install the curl command.):
curl https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css --output static/bootstrap.min.css
Output:
ootstrap.min.css
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 152k 0 152k 0 0 163k 0 --:--:-- --:--:-- --:--:-- 163k
████████████████████████████████████████████████████████████████████████
425.Creating the Static File Route
example:
Defining a Route in the main.go:
package main
import (
"net/http"
"io"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter,
request *http.Request) {
Printfln("Request for %v", request.URL.Path)
io.WriteString(writer, sh.message)
}
func main() {
http.Handle("/message", StringHandler{ "Hello, World"})
http.Handle("/favicon.ico", http.NotFoundHandler())
http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect))
fsHandler := http.FileServer(http.Dir("./static"))
http.Handle("/files/", http.StripPrefix("/files", fsHandler))
err := http.ListenAndServe(":5000", nil)
if (err != nil) {
Printfln("Error: %v", err.Error())
}
}
Output:
redirect from => https://localhost:5500/files/store.html
to => static/store.html
████████████████████████████████████████████████████████████████████████
426.Using Templates to Generate Responses
example:
templates/products.html:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Pro Go</title>
<link rel="stylesheet" href="/files/bootstrap.min.css">
</head>
<body>
<h3 class="bg-primary text-white text-center p-2 m-2">Products</h3>
<div class="p-2">
<table class="table table-sm table-striped table-bordered">
<thead>
<tr>
<th>Index</th>
<th>Name</th>
<th>Category</th>
<th class="text-end">Price</th>
</tr>
</thead>
<tbody>
{{ range $index, $product := .Data }}
<tr>
<td>{{ $index }}</td>
<td>{{ $product.Name }}</td>
<td>{{ $product.Category }}</td>
<td class="text-end">
{{ printf "$%.2f" $product.Price }}
</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</body>
</html>
dynamic.go:
package main
import (
"html/template"
"net/http"
"strconv"
)
type Context struct {
Request *http.Request
Data []Product
}
var htmlTemplates *template.Template
func HandleTemplateRequest(writer http.ResponseWriter, request *http.Request) {
path := request.URL.Path
if (path == "") {
path = "products.html"
}
t := htmlTemplates.Lookup(path)
if (t == nil) {
http.NotFound(writer, request)
} else {
err := t.Execute(writer, Context{ request, Products})
if (err != nil) {
http.Error(writer, err.Error(), http.StatusInternalServerError)
}
}
}
func init() {
var err error
htmlTemplates = template.New("all")
htmlTemplates.Funcs(map[string]interface{} {
"intVal": strconv.Atoi,
})
htmlTemplates, err = htmlTemplates.ParseGlob("templates/*.html")
if (err == nil) {
http.Handle("/templates/", http.StripPrefix("/templates/",
http.HandlerFunc(HandleTemplateRequest)))
} else {
panic(err)
}
}
Output:
The initialization function loads all the templates with the html extension in the templates folder and
sets up a route so that requests that start with /templates/ are processed by the HandleTemplateRequest
function. This function looks up the template, falling back to the products.html file if no file path is
specified, executes the template, and writes the response.
████████████████████████████████████████████████████████████████████████
427.Understanding Content Type Sniffing
which implements the MIME Sniffing algorithm defined by:
https://mimesniff.spec.whatwg.org
The sniffing process can't detect every content type,
but it does well with standard web types, such as HTML, CSS, and JavaScript.
The DetectContentType function returns a MIME type,
which is used as the value for the Content-Type header.
████████████████████████████████████████████████████████████████████████
428.Responding with JSON Data
JSON responses are widely used in web services,
which provide access to an application's data for clients
that don't want to receive HTML, such as Angular or React JavaScript clients.
example:
json.go:
package main
import (
"net/http"
"encoding/json"
)
func HandleJsonRequest(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Content-Type", "application/json")
json.NewEncoder(writer).Encode(Products)
}
func init() {
http.HandleFunc("/json", HandleJsonRequest)
}
=========================================================================
main.go:
package main
import (
"net/http"
"io"
)
type StringHandler struct {
message string
}
func (sh StringHandler) ServeHTTP(writer http.ResponseWriter,
request *http.Request) {
Printfln("Request for %v", request.URL.Path)
io.WriteString(writer, sh.message)
}
func main() {
http.Handle("/message", StringHandler{ "Hello, World"})
http.Handle("/favicon.ico", http.NotFoundHandler())
http.Handle("/", http.RedirectHandler("/message", http.StatusTemporaryRedirect))
fsHandler := http.FileServer(http.Dir("./static"))
http.Handle("/files/", http.StripPrefix("/files", fsHandler))
err := http.ListenAndServe(":5000", nil)
if (err != nil) {
Printfln("Error: %v", err.Error())
}
}
===================================================================
Output:
The initialization function creates a route,
which means that requests for /json will be processed by the
HandleJsonRequest function.
████████████████████████████████████████████████████████████████████████
429.Handling Form Data
The net/http package provides support for easily receiving and processing form data.
This template makes use of template variables, expressions,
and functions to get the query string from
the request and select the first index value,
which is converted to an int and used to retrieve a Product value
from the data provided to the template.
example:
edit.html:
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Pro Go</title>
<link rel="stylesheet" href="/files/bootstrap.min.css">
</head>
<body>
{{ $index := intVal (index (index .Request.URL.Query "index") 0) }}
{{ if lt $index (len .Data)}}
{{ with index .Data $index}}
<h3 class="bg-primary text-white text-center p-2 m-2">Product</h3>
<form method="POST" action="/forms/edit" class="m-2">
<div class="form-group">
<label>Index</label>
<input name="index" value="{{$index}}" class="form-control" disabled />
<input name="index" value="{{$index}}" type="hidden" />
</div>
<div class="form-group">
<label>Name</label>
<input name="name" value="{{.Name}}" class="form-control" />
</div>
<div class="form-group">
<label>Category</label>
<input name="category" value="{{.Category}}" class="form-control" />
</div>
<div class="form-group">
<label>Price</label>
<input name="price" value="{{.Price}}" class="form-control" />
</div>
<div class="mt-2">
<button type="submit" class="btn btn-primary">Save</button>
<a href="/templates/" class="btn btn-secondary">Cancel</a>
</div>
</form>
{{ end }}
{{ else }}
<h3 class="bg-danger text-white text-center p-2">
No Product At Specified Index
</h3>
{{end }}
</body>
</html>
████████████████████████████████████████████████████████████████████████
430.Reading Form Data from Requests
The Request Form Data Fields and Methods
Name Description
-------- ------------------------------
Form This field returns a map[string][]string containing the parsed form data and the
query string parameters. The ParseForm method must be called before this field is read.
PostForm This field is similar to Form but excludes the query string parameters so that only
data from the request body is contained in the map. The ParseForm method must
be called before this field is read.
MultipartForm This field returns a multipart form represented using the Form struct defined in the
mime/multipart package. The ParseMultipartForm method must be called before
this field is read.
FormValue(key) This method returns the first value for the specified form key and returns the
empty string if there is no value. The source of data for this method is the Form
field, and calling the FormValue method automatically calls ParseForm or
ParseMultipartForm to parse the form.
PostFormValue(key) This method returns the first value for the specified form key and returns the
empty string if there is no value. The source of data for this method is the PostForm
field, and calling the PostFormValue method automatically calls ParseForm or
ParseMultipartForm to parse the form.
FormFile(key) This method provides access to the first file with the specified key in the form.
The results are a File and FileHeader, both of which are defined in the mime/
multipart package, and an error. Calling this function causes the ParseForm or
ParseMultipartForm functions to be invoked to parse the form.
ParseForm() This method parses a form and populates the Form and PostForm fields. The result
is an error that describes any parsing problems.
ParseMultipart This method parses a MIME multipart form and populates the MultipartForm field.
Form(max) The argument specifies the maximum number of bytes to allocate to the form data,
and the result is an error that describes any problems processing the form.
The init function sets up a new route so that the ProcessFormData function handles requests whose
path is /forms/edit. Within the ProcessFormData function, the request method is checked, and the form
data in the request is used to create a Product struct and replace the existing data value. In a real project,
validating the data submitted in the form is essential, but for this chapter I trust that the form contains
valid data.
Processing form data:
https://localhost:5500/templates/edit.html?index=2
example:
The Contents of the forms.go File in the httpserver Folder:
package main
import (
"net/http"
"strconv"
)
func ProcessFormData(writer http.ResponseWriter, request *http.Request) {
if (request.Method == http.MethodPost) {
index, _ := strconv.Atoi(request.PostFormValue("index"))
p := Product {}
p.Name = request.PostFormValue("name")
p.Category = request.PostFormValue("category")
p.Price, _ = strconv.ParseFloat(request.PostFormValue("price"), 64)
Products[index] = p
}
http.Redirect(writer, request, "/templates", http.StatusTemporaryRedirect)
}
func init() {
http.HandleFunc("/forms/edit", ProcessFormData)
}
Output:
without view of upload you can add data to templates URL.
████████████████████████████████████████████████████████████████████████
431.Reading Multipart Forms
example:
upload.html:
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
<link href="bootstrap.min.css" rel="stylesheet" />
</head>
<body>
<div class="m-1 p-2 bg-primary text-white h2 text-center">
Upload File
</div>
<form method="POST" action="/forms/upload" class="p-2"
enctype="multipart/form-data">
<div class="form-group">
<label class="form-label">Name</label>
<input class="form-control" type="text" name="name">
</div>
<div class="form-group">
<label class="form-label">City</label>
<input class="form-control" type="text" name="city">
</div>
<div class="form-group">
<label class="form-label">Choose Files</label>
<input class="form-control" type="file" name="files" multiple>
</div>
<button type="submit" class="btn btn-primary mt-2">Upload</button>
</form>
</body>
</html>
====================================================================================
upload.go:
package main
import (
"fmt"
"io"
"net/http"
)
func HandleMultipartForm(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Name: %v, City: %v\n", request.FormValue("name"),
request.FormValue("city"))
fmt.Fprintln(writer, "------")
file, header, err := request.FormFile("files")
if err == nil {
defer file.Close()
fmt.Fprintf(writer, "Name: %v, Size: %v\n", header.Filename, header.Size)
for k, v := range header.Header {
fmt.Fprintf(writer, "Key: %v, Value: %v\n", k, v)
}
fmt.Fprintln(writer, "------")
io.Copy(writer, file)
} else {
http.Error(writer, err.Error(), http.StatusInternalServerError)
}
}
func init() {
http.HandleFunc("/forms/upload", HandleMultipartForm)
}
Output: Search in Browser: http://localhost:5000/files/upload.html
You Can Upload
████████████████████████████████████████████████████████████████████████
432.The FileHeader Fields and Method
Name Description
------- ------------------------------
Name This field returns a string containing the name of the file.
Size This field returns an int64 containing the size of the file.
Header This field returns a map[string][]string, which contains the headers for the MIME part that
contains the file.
Open() This method returns a File that can be used to read the content associated with the header, as
demonstrated in the next section.
████████████████████████████████████████████████████████████████████████
433.Reading and Setting Cookies
The net/http Function for Setting Cookies
Name Description
--------------- ------------------------------------
SetCookie(writer, cookie) This function adds a Set-Cookie header to the specified ResponseWriter. The
cookie is described using a pointer to a Cookie struct, which is described next.
Cookies can be complex, and care must be taken to configure them correctly.
The detail of how cookies work is beyond the scope of this book,
but there is a good description available at
https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
and a detailed breakdown of the cookie
fields at:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie.
████████████████████████████████████████████████████████████████████████
434.The Fields Defined by the Cookie Struct
Name Description
------- --------------------------------
Name This field represents the name of the cookie, expressed as a string.
Value This field represents the cookie value, expressed as a string.
Path This optional field specifies the cookie path.
Domain This optional field specifies the host/domain to which the cookie will be set.
Expires This field specifies the cookie expiry, expressed as a time.Time value.
MaxAge This field specifies the number of seconds until the cookie expires, expressed as an int.
Secure When this bool field is true, the client will only send the cookie over HTTPS connections.
HttpOnly When this bool field is true, the client will prevent JavaScript code from accessing the cookie.
SameSite This field specifies the cross-origin policy for the cookie using the SameSite constants, which
defines SameSiteDefaultMode, SameSiteLaxMode, SameSiteStrictMode, and SameSiteNoneMode.
████████████████████████████████████████████████████████████████████████
435.The Request Methods for Cookies
Name Description
----------- ----------------
Cookie(name) This method returns a pointer to the Cookie value with the specified name and an error
that indicates when there is no matching cookie.
Cookies() This method returns a slice of Cookie pointers.
example:
cookies.go:
package main
import (
"net/http"
"fmt"
"strconv"
)
func GetAndSetCookie(writer http.ResponseWriter, request *http.Request) {
counterVal := 1
counterCookie, err := request.Cookie("counter")
if (err == nil) {
counterVal, _ = strconv.Atoi(counterCookie.Value)
counterVal++
}
http.SetCookie(writer, &http.Cookie{
Name: "counter", Value: strconv.Itoa(counterVal),
})
if (len(request.Cookies()) > 0) {
for _, c := range request.Cookies() {
fmt.Fprintf(writer, "Cookie Name: %v, Value: %v", c.Name, c.Value)
}
} else {
fmt.Fprintln(writer, "Request contains no cookies")
}
}
func init() {
http.HandleFunc("/cookies", GetAndSetCookie)
}
======================================================================================
Compile and execute the project and use a browser to request http://localhost:5000/cookies
Output:
search-1:
Request contains no cookies
search-2:
Cookie Name: counter, Value: 1
search-3:
Cookie Name: counter, Value: 2
...
████████████████████████████████████████████████████████████████████████
436.Creating HTTP Clients
Putting HTTP Clients in Context
What are they?
HTTP requests are used to retrieve data from HTTP servers
Why are they useful?
HTTP is one of the most widely used protocols and is commonly used to provide
access to content that can be presented to the user as well as data that is consumed
programmatically.
How is it used?
The features of the net/http package are used to create and send requests and
process responses.
Are there any pitfalls or limitations?
These features are well-designed and easy to use, although some features require a
specific sequence to use.
Are there any alternatives?
The standard library includes support for other network protocols and also for
opening and using lower-level network connections.
See the
https://pkg.go.dev/net@go1.17.1
https://pkg.go.dev/net
for details of the net package and its subpackages, such as net/smtp,
for example, which implements the SMTP protocol.
Chapter Summary:
Problem Solution
-------- ------------
Send HTTP requests Use the convenience methods for specific HTTP methods
Configure HTTP requests Use the fields and methods defined by the Client struct
Create a preconfigured request Use the NewRequest convenience functions
Use cookies in a request Use a cookie jar
Configure how redirections are processed Use the CheckRedirect field to register a function that is
invoked to deal with a redirection
Send multipart forms Use the mime/multipart package
Preparing for This Chapter
1- go mod init httpclient
2- printer.go
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
3- httpclient folder -> file named product.go
package main
type Product struct {
Name, Category string
Price float64
}
var Products = []Product {
{ "Kayak", "Watersports", 279 },
{ "Lifejacket", "Watersports", 49.95 },
{ "Soccer Ball", "Soccer", 19.50 },
{ "Corner Flags", "Soccer", 34.95 },
{ "Stadium", "Soccer", 79500 },
{ "Thinking Cap", "Chess", 16 },
{ "Unsteady Chair", "Chess", 75 },
{ "Bling-Bling King", "Chess", 1200 },
}
4- The Contents of the index.html File in the httpclient Folder
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
</head>
<body>
<h1>Hello, World</div>
</body>
</html>
5- The Contents of the server.go File in the httpclient Folder
package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
)
func init() {
http.HandleFunc("/html",
func(writer http.ResponseWriter, request *http.Request) {
http.ServeFile(writer, request, "./index.html")
})
http.HandleFunc("/json",
func(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Content-Type", "application/json")
json.NewEncoder(writer).Encode(Products)
})
http.HandleFunc("/echo",
func(writer http.ResponseWriter, request *http.Request) {
writer.Header().Set("Content-Type", "text/plain")
fmt.Fprintf(writer, "Method: %v\n", request.Method)
for header, vals := range request.Header {
fmt.Fprintf(writer, "Header: %v: %v\n", header, vals)
}
fmt.Fprintln(writer, "----")
data, err := io.ReadAll(request.Body)
if err == nil {
if len(data) == 0 {
fmt.Fprintln(writer, "No body")
} else {
writer.Write(data)
}
} else {
fmt.Fprintf(os.Stdout, "Error reading body: %v\n", err.Error())
}
})
}
6- The Contents of the main.go File in the httpclient Folder
package main
import (
"net/http"
)
func main() {
Printfln("Starting HTTP Server")
http.ListenAndServe(":5000", nil)
}
=======================================================================================
Output:
The code in httpclient folder will be compiled and executed.
Use a web browser to request http://localhost:5000/html and http://localhost:5000/json,
To see the echo result, request http://localhost:5000/echo
████████████████████████████████████████████████████████████████████████
437.Sending Simple HTTP Requests
The net/http package provides a set of convenience functions that make basic HTTP requests.
The functions are named after the HTTP method of the request they created.
The Convenience Methods for HTTP Requests
Name Description
----------------- -----------------------------
Get(url) This function sends a GET request to the specified HTTP or HTTPS URL. The
results are a Response and an error that reports problems with the request.
Head(url) This function sends a HEAD request to the specified HTTP or HTTPS URL.
A HEAD request returns the headers that would be returned for a GET request.
The results are a Response and an error that reports problems with the request.
Post(url, contentType, reader) This function sends a POST request to the specified HTTP or HTTPS URL, with
the specified Content-Type header value. The content for the form is provided
by the specified Reader. The results are a Response and an error that reports
problems with the request.
PostForm(url, data) This function sends a POST request to the specified HTTP or HTTPS URL, with
the Content-Type header set to application/x-www-form-urlencoded. The
content for the form is provided by a map[string][]string. The results are a
Response and an error that reports problems with the request.
████████████████████████████████████████████████████████████████████████
438.Sending a GET Request in the main.go
main.go:
package main
import (
"net/http"
"os"
"time"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
response, err := http.Get("http://localhost:5000/html")
if (err == nil) {
response.Write(os.Stdout)
} else {
Printfln("Error: %v", err.Error())
}
}
===================================================================================================
The argument to the Get function is a string that contains the URL to request.
The results are a Response value and an error that reports any problems sending the request.
Output:
HTTP/1.1 200 OK
Content-Length: 171
Accept-Ranges: bytes
Content-Type: text/html; charset=utf-8
Date: Thu, 12 Oct 2023 16:17:10 GMT
Last-Modified: Wed, 11 Oct 2023 12:44:50 GMT
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
</head>
<body>
<h1>Hello, World</div>
</body>
</html>
========================================================================================================
example-2:
main.go:
package main
import (
"net/http"
"os"
// "time"
)
func main() {
// go http.ListenAndServe(":5000", nil)
// time.Sleep(time.Second)
response, err := http.Get("https://www.google.com")
if (err == nil) {
response.Write(os.Stdout)
} else {
Printfln("Error: %v", err.Error())
}
}
Output:
HTTP/2.0 403 Forbidden
Content-Length: 1579
Alt-Svc: h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
Content-Type: text/html; charset=UTF-8
Date: Thu, 12 Oct 2023 16:15:23 GMT
Referrer-Policy: no-referrer
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
<title>Error 403 (Forbidden)!!1</title>
<style>
*{margin:0;padding:0}html,code{font:15px/22px arial,sans-serif}html{background:#fff;color:#222;padding:15px}body{margin:7% auto 0;max-width:390px;min-height:180px;padding:30px 0 15px}* > body{background:url(//www.google.com/images/errors/robot.png) 100% 5px no-repeat;padding-right:205px}p{margin:11px 0 22px;overflow:hidden}ins{color:#777;text-decoration:none}a img{border:0}@media screen and (max-width:772px){body{background:none;margin-top:0;max-width:none;padding-right:0}}#logo{background:url(//www.google.com/images/branding/googlelogo/1x/googlelogo_color_150x54dp.png) no-repeat;margin-left:-5px}@media only screen and (min-resolution:192dpi){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat 0% 0%/100% 100%;-moz-border-image:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) 0}}@media only screen and (-webkit-min-device-pixel-ratio:2){#logo{background:url(//www.google.com/images/branding/googlelogo/2x/googlelogo_color_150x54dp.png) no-repeat;-webkit-background-size:100% 100%}}#logo{display:inline-block;height:54px;width:150px}
</style>
<a href=//www.google.com/><span id=logo aria-label=Google></span></a>
<p><b>403.</b> <ins>That's an error.</ins>
<p>Your client does not have permission to get URL <code>/</code> from this server. <ins>That's all we know.</ins>
████████████████████████████████████████████████████████████████████████
439.The Fields and Methods Defined by the Response Struct
Name Description
---------- -------------------------------------
StatusCode This field returns the response status code, expressed as an int.
Status This field returns a string containing the status description.
Proto This field returns a string containing the response HTTP protocol.
Header This field returns a map[string][]string that contains the response headers.
Body This field returns a ReadCloser, which is a Reader that defines a Close method and
which provides access to the response body.
Trailer This field returns a map[string][]string that contains the response trailers.
ContentLength This field returns the value of the Content-Length header, parsed into an int64 value.
TransferEncoding This field returns the set of Transfer-Encoding header values.
Close This bool field returns true if the response contains a Connection header set to close,
which indicates that the HTTP connection should be closed.
Uncompressed This field returns true if the server sent a compressed response that was
decompressed by the net/http package.
Request This field returns the Request that was used to obtain the response. The Request struct
is described in Chapter 24.
TLS This field provides details of the HTTPS connection.
Cookies() This method returns a []*Cookie, which contains the Set-Cookie headers in the
response. The Cookie struct is described in Chapter 24.
Location() This method returns the URL from the response Location header and an error that
indicates when the response does not contain this header.
Write(writer) This method writes a summary of the response to the specified Writer.
████████████████████████████████████████████████████████████████████████
440.Reading the Response Body in the main.go
main.go:
package main
import (
"net/http"
"os"
"time"
"io"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
response, err := http.Get("http://localhost:5000/html")
if (err == nil && response.StatusCode == http.StatusOK) {
data, err := io.ReadAll(response.Body)
if (err == nil) {
defer response.Body.Close()
os.Stdout.Write(data)
}
} else {
Printfln("Error: %v, Status Code: %v", err.Error(), response.StatusCode)
}
}
====================================================================
Output: in Terminal
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
</head>
<body>
<h1>Hello, World</div>
</body>
</html>
████████████████████████████████████████████████████████████████████████
441.Reading and Parsing Data in the main.go
main.go:
package main
import (
"net/http"
//"os"
"time"
//"io"
"encoding/json"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
response, err := http.Get("http://localhost:5000/json")
if (err == nil && response.StatusCode == http.StatusOK) {
defer response.Body.Close()
data := []Product {}
err = json.NewDecoder(response.Body).Decode(&data)
if (err == nil) {
for _, p := range data {
Printfln("Name: %v, Price: $%.2f", p.Name, p.Price)
}
} else {
Printfln("Decode error: %v", err.Error())
}
} else {
Printfln("Error: %v, Status Code: %v", err.Error(), response.StatusCode)
}
}
====================================================================
Output: in Terminal
Name: Kayak,Price: $279.00
Name: Lifejacket,Price: $49.95
Name: Soccer Ball,Price: $19.50
Name: Corner Flags,Price: $34.95
Name: Stadium,Price: $79500.00
Name: Thinking Cap,Price: $16.00
Name: Unsteady Chair,Price: $75.00
Name: Bling-Bling King,Price: $1200.00
████████████████████████████████████████████████████████████████████████
442.Sending POST Requests
The Post and PostForm functions are used to send POST requests.
The PostForm function encodes a map of values as form data.
Sending a Form in the main.go
main.go:
package main
import (
"net/http"
"os"
"time"
"io"
//"encoding/json"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
formData := map[string][]string {
"name": { "Kayak "},
"category": { "Watersports"},
"price": { "279"},
}
response, err := http.PostForm("http://localhost:5000/echo", formData)
if (err == nil && response.StatusCode == http.StatusOK) {
io.Copy(os.Stdout, response.Body)
defer response.Body.Close()
} else {
Printfln("Error: %v, Status Code: %v", err.Error(), response.StatusCode)
}
}
====================================================================
Output:
Method: POST
Header: Accept-Encoding: [gzip]
Header: User-Agent: [Go-http-client/1.1]
Header: Content-Length: [42]
Header: Content-Type: [application/x-www-form-urlencoded]
----
category=Watersports&name=Kayak+&price=279
████████████████████████████████████████████████████████████████████████
443.Posting a Form Using a Reader
The Post function sends a POST request
to the server and creates the request body by reading content from a Reader.
Posting from a Reader in the main.go:
package main
import (
"net/http"
"os"
"time"
"io"
"encoding/json"
"strings"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
var builder strings.Builder
err := json.NewEncoder(&builder).Encode(Products[0])
if (err == nil) {
response, err := http.Post("http://localhost:5000/echo","application/json",strings.NewReader(builder.String()))
if (err == nil && response.StatusCode == http.StatusOK) {
io.Copy(os.Stdout, response.Body)
defer response.Body.Close()
} else {
Printfln("Error: %v", err.Error())
}
} else {
Printfln("Error: %v", err.Error())
}
}
====================================================================
Output:
Method: POST
Header: User-Agent: [Go-http-client/1.1]
Header: Content-Length: [54]
Header: Content-Type: [application/json]
Header: Accept-Encoding: [gzip]
----
{"Name":"Kayak","Category":"Watersports","Price":279}
████████████████████████████████████████████████████████████████████████
444.Understanting The Content-Length Header
This header is set automatically but is included in requests only when it is
possible to determine how much data will be included in the body in advance.
This is done by inspecting the Reader to determine the dynamic type.
When the data is stored in memory using the strings.
Reader, bytes.Reader, or bytes.Buffer type,
the built-in len function is used to determine the amount of data,
and the result is used to set the Content-Length header.
For all other types, the Content-Type head is not set,
and chunked encoding is used instead, which means that
the body is written in blocks of data whose size
is declared as part of the request body.
This approach allows requests to be sent
without needing to read all the data from the Reader just to work
out how many bytes there are.
Chunked encoding is described at
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding
████████████████████████████████████████████████████████████████████████
445.Configuring HTTP Client Requests
The Client struct is used when control is required over an HTTP request
and defines the fields and methods described:
The Client Fields and Methods
Name Description
----------------- -------------------------
Transport This field is used to select the transport that will be used to send the HTTP
request. The net/http package provides a default transport.
CheckRedirect This field is used to specify a custom policy for dealing with repeated
redirections, as described in the “Managing Redirections” section.
Jar This field returns a CookieJar, which is used to manage cookies, as
described in the “Working with Cookies” section.
Timeout This field is used to set a timeout for the request, specified as a time.Duration
Do(request) This method sends the specified Request, returning a Response and an
error that indicates problems sending the request.
CloseIdleConnections() This method closes any idle HTTP requests that are currently open and unused.
Get(url) This method is called by the Get function described
Head(url) This method is called by the Head function described
Post(url, contentType,reader) This method is called by the Post function described
PostForm(url, data) This method is called by the PostForm function described
████████████████████████████████████████████████████████████████████████
446.Useful Request Fields and Methods
Name Description
------ ------------------------------
Method This string field specifies the HTTP method that will be used for the request. The net/
http package defines constants for HTTP methods, such as MethodGet and MethodPost.
URL This URL field specifies the URL to which the request will be sent. The URL struct is
Header This field is used to specify the headers for the request. The headers are specified in a
map[string][]string, and the field will be nil when a Request value is created using the literal struct syntax.
ContentLength This field is used to set the Content-Length header using an int64 value.
TransferEncoding This field is used to set the Transfer-Encoding header using a slice of strings.
Body This ReadCloser field specifies the source for the request body. If you have a Reader
that doesn't define a Close method, then the io.NopCloser function can be used to
create a ReadCloser whose Close method does nothing.
████████████████████████████████████████████████████████████████████████
447.The Function for Parsing URL Values
Name Description
--------- ---------------------
Parse(string) This method parses a string into a URL. The results are the URL value and an error that
indicates problems parsing the string.
████████████████████████████████████████████████████████████████████████
448.Sending a Request in the main.go
main.go:
package main
import (
"net/http"
"os"
"time"
"io"
"encoding/json"
"strings"
"net/url"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
var builder strings.Builder
err := json.NewEncoder(&builder).Encode(Products[0])
if (err == nil) {
reqURL, err := url.Parse("http://localhost:5000/echo")
if (err == nil) {
req := http.Request {
Method: http.MethodPost,
URL: reqURL,
Header: map[string][]string {
"Content-Type": { "application.json" },
},
Body: io.NopCloser(strings.NewReader(builder.String())),
}
response, err := http.DefaultClient.Do(&req)
if (err == nil && response.StatusCode == http.StatusOK) {
io.Copy(os.Stdout, response.Body)
defer response.Body.Close()
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Parse Error: %v", err.Error())
}
} else {
Printfln("Encoder Error: %v", err.Error())
}
}
====================================================================
Output:
Method: POST
Header: User-Agent: [Go-http-client/1.1]
Header: Content-Type: [application.json]
Header: Accept-Encoding: [gzip]
----
{"Name":"Kayak","Category":"Watersports","Price":279}
████████████████████████████████████████████████████████████████████████
449.Using the Convenience Functions to Create a Request
The net/http Convenience Functions for Creating Requests
Name Description
------------------------- ----------------------------
NewRequest(method, url,reader) This function creates a new Reader, configured with the specified method,
URL, and body. The function also returns an error that indicates problems
creating the value, including parsing the URL, which is expressed as a string.
NewRequestWithContext(context, method, url, reader) This function creates a new Reader that will be sent in the specified context.
████████████████████████████████████████████████████████████████████████
450.Using the Convenience Function in the main.go
example:
main.go:
package main
import (
"encoding/json"
"io"
"net/http"
"os"
"strings"
"time"
//"net/url"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
var builder strings.Builder
err := json.NewEncoder(&builder).Encode(Products[0])
if err == nil {
req, err := http.NewRequest(http.MethodPost, "http://localhost:5000/echo",
io.NopCloser(strings.NewReader(builder.String())))
if err == nil {
req.Header["Content-Type"] = []string{"application/json"}
response, err := http.DefaultClient.Do(req)
if err == nil && response.StatusCode == http.StatusOK {
io.Copy(os.Stdout, response.Body)
defer response.Body.Close()
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Request Init Error: %v", err.Error())
}
} else {
Printfln("Encoder Error: %v", err.Error())
}
}
====================================================================
Output:
Method: POST
Header: User-Agent: [Go-http-client/1.1]
Header: Content-Type: [application/json]
Header: Accept-Encoding: [gzip]
----
{"Name":"Kayak","Category":"Watersports","Price":279}
████████████████████████████████████████████████████████████████████████
451.Working with Cookies
The Client keeps track of the cookies it receives from the server and automatically includes them in
subsequent requests.
example:
The Contents of the server_cookie.go:
package main
import (
"fmt"
"net/http"
"strconv"
)
func init() {
http.HandleFunc("/cookie",
func(writer http.ResponseWriter, request *http.Request) {
counterVal := 1
counterCookie, err := request.Cookie("counter")
if err == nil {
counterVal, _ = strconv.Atoi(counterCookie.Value)
counterVal++
}
http.SetCookie(writer, &http.Cookie{
Name: "counter", Value: strconv.Itoa(counterVal),
})
if len(request.Cookies()) > 0 {
for _, c := range request.Cookies() {
fmt.Fprintf(writer, "Cookie Name: %v, Value: %v\n",
c.Name, c.Value)
}
} else {
fmt.Fprintln(writer, "Request contains no cookies")
}
})
}
-----------------------------------
Changing URL in the main.go:
package main
import (
"io"
"net/http"
"os"
"time"
// "encoding/json"
// "strings"
//"net/url"
"net/http/cookiejar"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
jar, err := cookiejar.New(nil)
if err == nil {
http.DefaultClient.Jar = jar
}
for i := 0; i < 3; i++ {
req, err := http.NewRequest(http.MethodGet,
"http://localhost:5000/cookie", nil)
if err == nil {
response, err := http.DefaultClient.Do(req)
if err == nil && response.StatusCode == http.StatusOK {
io.Copy(os.Stdout, response.Body)
defer response.Body.Close()
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Request Init Error: %v", err.Error())
}
}
}
====================================================================
Output: in Terminal
Request contains no cookies
Cookie Name: counter, Value: 1
Cookie Name: counter, Value: 2
████████████████████████████████████████████████████████████████████████
452.The Methods Defined by the CookieJar Interface
Name Description
------------------------ ---------------------------------
SetCookies(url, cookies) This method stores a *Cookie slice for the specified URL.
Cookes(url) This method returns a *Cookie slice containing the cookies that
should be included in a request for the specified URL.
The net/http/cookiejar package contains an implementation of the CookieJar interface that stores
cookies in memory. Cookie jars are created with a constructor function
████████████████████████████████████████████████████████████████████████
453.The Cookie Jar Constructor Function in the net/http/cookiejar Package
Name Description
------------ -------------------------
New(options) This function creates a new CookieJar, configured with an Options struct, described
next. The function also returns an error that reports problems creating the jar.
The New function accepts a net/http/cookiejar/Options struct, which is used to configure the
cookie jar. There is only one Options field, PublicSuffixList, which is used to specify an implementation
of the interface with the same name, which provides support for preventing cookies from being set too
widely, which can cause privacy violations. The standard library doesn't contain an implementation of the
PublicSuffixList interface, but there is an implementation available at
https://pkg.go.dev/golang.org/x/net/publicsuffix
████████████████████████████████████████████████████████████████████████
454.Creating Separate Clients and Cookie Jars
A consequence of using the DefaultClient is that all requests share the same cookies,
which can be useful, especially since the cookie jar will ensure that each request
only includes the cookies that are required for each URL.
████████████████████████████████████████████████████████████████████████
455.Creating Separate Clients in the main.go File
example:
main.go:
package main
import (
"net/http"
"os"
"time"
"io"
//"encoding/json"
//"strings"
//"net/url"
"net/http/cookiejar"
"fmt"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
clients := make([]http.Client, 3)
for index, client := range clients {
jar, err := cookiejar.New(nil)
if (err == nil) {
client.Jar = jar
}
for i := 0; i < 3; i++ {
req, err := http.NewRequest(http.MethodGet,
"http://localhost:5000/cookie", nil)
if (err == nil) {
response, err := client.Do(req)
if (err == nil && response.StatusCode == http.StatusOK) {
fmt.Fprintf(os.Stdout, "Client %v: ", index)
io.Copy(os.Stdout, response.Body)
defer response.Body.Close()
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Request Init Error: %v", err.Error())
}
}
}
}
====================================================================
Output:
Client 0: Request contains no cookies
Client 0: Cookie Name: counter, Value: 1
Client 0: Cookie Name: counter, Value: 2
Client 1: Request contains no cookies
Client 1: Cookie Name: counter, Value: 1
Client 1: Cookie Name: counter, Value: 2
Client 2: Request contains no cookies
Client 2: Cookie Name: counter, Value: 1
Client 2: Cookie Name: counter, Value: 2
████████████████████████████████████████████████████████████████████████
456.Sharing a CookieJar in the main.go
example:
main.go:
package main
import (
"io"
"net/http"
"os"
"time"
//"encoding/json"
//"strings"
//"net/url"
"fmt"
"net/http/cookiejar"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
jar, err := cookiejar.New(nil)
clients := make([]http.Client, 3)
for index, client := range clients {
//jar, err := cookiejar.New(nil)
if err == nil {
client.Jar = jar
}
for i := 0; i < 3; i++ {
req, err := http.NewRequest(http.MethodGet,
"http://localhost:5000/cookie", nil)
if err == nil {
response, err := client.Do(req)
if err == nil && response.StatusCode == http.StatusOK {
fmt.Fprintf(os.Stdout, "Client %v: ", index)
io.Copy(os.Stdout, response.Body)
defer response.Body.Close()
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Request Init Error: %v", err.Error())
}
}
}
}
====================================================================
Output:
Client 0: Request contains no cookies
Client 0: Cookie Name: counter, Value: 1
Client 0: Cookie Name: counter, Value: 2
Client 1: Cookie Name: counter, Value: 3
Client 1: Cookie Name: counter, Value: 4
Client 1: Cookie Name: counter, Value: 5
Client 2: Cookie Name: counter, Value: 6
Client 2: Cookie Name: counter, Value: 7
Client 2: Cookie Name: counter, Value: 8
████████████████████████████████████████████████████████████████████████
457.Managing Redirections
By default, a Client will stop following redirections after ten requests,
but this can be changed by specifying a custom policy.
example:
The Contents of the server_redirects.go File in the httpclient Folder:
package main
import "net/http"
func init() {
http.HandleFunc("/redirect1",
func(writer http.ResponseWriter, request *http.Request) {
http.Redirect(writer, request, "/redirect2",
http.StatusTemporaryRedirect)
})
http.HandleFunc("/redirect2",
func(writer http.ResponseWriter, request *http.Request) {
http.Redirect(writer, request, "/redirect1",
http.StatusTemporaryRedirect)
})
}
====================================================================
Sending a Request in the main.go File in the httpclient Folder:
package main
import (
"net/http"
"os"
"io"
"time"
//"encoding/json"
//"strings"
//"net/url"
//"net/http/cookiejar"
//"fmt"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
req, err := http.NewRequest(http.MethodGet,
"http://localhost:5000/redirect1", nil)
if (err == nil) {
var response *http.Response
response, err = http.DefaultClient.Do(req)
if (err == nil) {
io.Copy(os.Stdout, response.Body)
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Error: %v", err.Error())
}
}
====================================================================
Output:
Request Error: Get "/redirect1": stopped after 10 redirects
████████████████████████████████████████████████████████████████████████
458.Defining a Custom Redirection Policy in the main.go
example:
main.go:
package main
import (
"net/http"
"os"
"io"
"time"
//"encoding/json"
//"strings"
"net/url"
//"net/http/cookiejar"
//"fmt"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
http.DefaultClient.CheckRedirect = func(req *http.Request,
previous []*http.Request) error {
if len(previous) == 3 {
url, _ := url.Parse("http://localhost:5000/html")
req.URL = url
}
return nil
}
req, err := http.NewRequest(http.MethodGet,
"http://localhost:5000/redirect1", nil)
if (err == nil) {
var response *http.Response
response, err = http.DefaultClient.Do(req)
if (err == nil) {
io.Copy(os.Stdout, response.Body)
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Error: %v", err.Error())
}
}
====================================================================
Output:
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
</head>
<body>
<h1>Hello, World</div>
</body>
</html>
████████████████████████████████████████████████████████████████████████
459.Creating Multipart Forms:
The mime/multipart package can be used to create a request body encoded as multipart/form-data, which
allows a form to safely contain binary data, such as the contents of a file.
████████████████████████████████████████████████████████████████████████
460.The Contents of the server_forms.go File in the httpclient Folder
example:
server_forms.go:
package main
import (
"net/http"
"fmt"
"io"
)
func init() {
http.HandleFunc("/form",
func (writer http.ResponseWriter, request *http.Request) {
err := request.ParseMultipartForm(10000000)
if (err == nil) {
for name, vals := range request.MultipartForm.Value {
fmt.Fprintf(writer, "Field %v: %v\n", name, vals)
}
for name, files := range request.MultipartForm.File {
for _, file := range files {
fmt.Fprintf(writer, "File %v: %v\n", name, file.Filename)
if f, err := file.Open(); err == nil {
defer f.Close()
io.Copy(writer, f)
}
}
}
} else {
fmt.Fprintf(writer, "Cannot parse form %v", err.Error())
}
})
}
====================================================================
Output:
<!DOCTYPE html>
<html>
<head>
<title>Pro Go</title>
<meta name="viewport" content="width=device-width" />
</head>
<body>
<h1>Hello, World</div>
</body>
</html>
████████████████████████████████████████████████████████████████████████
461.The multipart.Writer Constructor Function
Name Description
--------------- ----------------------------
NewWriter(writer) This function creates a new multipart.Writer that writes form data to the specified io.Writer.
████████████████████████████████████████████████████████████████████████
462.The multipart.Writer Methods
Name Description
------------------------ ---------------------------------------
CreateFormField(fieldname) This method creates a new form field with the specified name. The results
are an io.Writer that is used to write the field data and an error that
reports problems creating the field.
CreateFormFile(fieldname, filename) This method creates a new file field with the specified field name and file
name. The results are an io.Writer that is used to write the field data and
an error that reports problems creating the field.
FormDataContentType() This method returns a string that is used to set the Content-Type request
header and includes the string that denotes the boundaries between the parts of the form.
Close() This function finalizes the form and writes the terminating boundary that
denotes the end of the form data.
████████████████████████████████████████████████████████████████████████
463.Creating and Sending a Multipart Form in the main.go
example:
main.go:
package main
import (
"io"
"net/http"
"os"
"time"
//"encoding/json"
//"strings"
//"net/url"
//"net/http/cookiejar"
//"fmt"
"bytes"
"mime/multipart"
)
func main() {
go http.ListenAndServe(":5000", nil)
time.Sleep(time.Second)
var buffer bytes.Buffer
formWriter := multipart.NewWriter(&buffer)
fieldWriter, err := formWriter.CreateFormField("name")
if err == nil {
io.WriteString(fieldWriter, "Alice")
}
fieldWriter, err = formWriter.CreateFormField("city")
if err == nil {
io.WriteString(fieldWriter, "New York")
}
fileWriter, err := formWriter.CreateFormFile("codeFile", "printer.go")
if err == nil {
fileData, err := os.ReadFile("./printer.go")
if err == nil {
fileWriter.Write(fileData)
}
}
formWriter.Close()
req, err := http.NewRequest(http.MethodPost,
"http://localhost:5000/form", &buffer)
req.Header["Content-Type"] = []string{formWriter.FormDataContentType()}
if err == nil {
var response *http.Response
response, err = http.DefaultClient.Do(req)
if err == nil {
io.Copy(os.Stdout, response.Body)
} else {
Printfln("Request Error: %v", err.Error())
}
} else {
Printfln("Error: %v", err.Error())
}
}
====================================================================
Output:
Field name: [Alice]
Field city: [New York]
File codeFile: printer.go
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template+"\n", values...)
}
Caution Don't use the defer keyword on the call to the Close method; otherwise, the final boundary string
won't be added to the form until after the request will be sent, producing a form that not all servers will process.
It is important to call the Close method before sending the request.
████████████████████████████████████████████████████████████████████████
464.Working with Databases
There are drivers for a wide range of databases, and a list can be found at
https://github.com/golang/go/wiki/sqldrivers
Putting Working with Databases in Context
What is it?
The database/sql package provides features for working with SQL databases.
Why is it useful?
Relational databases remain the most effective way of storing large amounts of
structured data and are used in most large projects.
How is it used?
Driver packages provide support for specific databases, while the database/sql
package provides a set of types that allow databases to be used consistently.
Are there any pitfalls or limitations?
These features do not automatically populate struct fields from result rows.
Are there any alternatives?
There are third-party packages that build on these features to simplify or enhance their use.
Preparing for This Chapter:
1-Initializing the Module
go mod init data
2-Add a file named printer.go to the data folder
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
3-Add a file named main.go
package main
func main() {
Printfln("Hello, Data")
}
4-Compiling and Executing the Project
go run .
====================================================================
Output:
Hello, Data
Preparing the Database:
add a file named products.sql to the data folder:
DROP TABLE IF EXISTS Categories;
DROP TABLE IF EXISTS Products;
CREATE TABLE IF NOT EXISTS Categories (
Id INTEGER NOT NULL PRIMARY KEY,
Name TEXT
);
CREATE TABLE IF NOT EXISTS Products (
Id INTEGER NOT NULL PRIMARY KEY,
Name TEXT,
Category INTEGER,
Price decimal(8, 2),
CONSTRAINT CatRef FOREIGN KEY(Category) REFERENCES Categories (Id)
);
INSERT INTO
Categories (Id, Name)
VALUES
(1, "Watersports"),
(2, "Soccer");
INSERT INTO
Products (Id, Name, Category, Price)
VALUES
(1, "Kayak", 1, 279),
(2, "Lifejacket", 1, 48.95),
(3, "Soccer Ball", 2, 19.50),
(4, "Corner Flags", 2, 34.95);
Go to https://www.sqlite.org/download.html , look for the precompiled binaries section for your
operating system, and download the tools package.
Unpack the zip archive and copy the sqlite3 or sqlite3.exe file into the data folder. Run the command
Creating the Database command:
./sqlite3 products.db ".read products.sql"
████████████████████████████████████████████████████████████████████████
465.Creating the Database command:
./sqlite3 products.db ".read products.sql"
████████████████████████████████████████████████████████████████████████
466.Installing a Database Driver
Run the command:
go get modernc.org/sqlite
Most database servers are set up separately so that the database driver opens a connection to a separate
process. SQLite is an embedded database and is included in the driver package, which means no additional
configuration is required.
████████████████████████████████████████████████████████████████████████
467.Opening a Database
The standard library provides the database/sql package for working with databases.
The functions described here:
The database/sql Functions for Opening a Database
Name Description
---------------- ------------------------------
Drivers() This function returns a slice of strings, each of which contains the name of a database driver.
Open(driver,connectionStr) This function opens a database using the specified driver and connection string. The
results are a pointer to a DB struct, which is used to interact with the database and an
error that indicates problems opening the database.
████████████████████████████████████████████████████████████████████████
468.The Contents of the database.go
package main
// The blank identifier is used to import the database driver package, which loads the driver and allows it
// to register as a provider of the SQL API:
import (
"database/sql"
_ "modernc.org/sqlite"
)
func listDrivers() {
for _, driver := range sql.Drivers() {
Printfln("Driver: %v", driver)
}
}
func openDatabase() (db *sql.DB, err error) {
db, err = sql.Open("sqlite", "products.db")
if err == nil {
Printfln("Opened database")
}
return
}
====================================================================
Using the DB Struct in the main.go:
package main
func main() {
listDrivers()
db, err := openDatabase()
if (err == nil) {
db.Close()
} else {
panic(err)
}
}
====================================================================
in Terminal:
go mod tidy
Output in Terminal:
go: finding module for package modernc.org/sqlite
go: downloading modernc.org/sqlite v1.27.0
go: found modernc.org/sqlite in modernc.org/sqlite v1.27.0
go: downloading golang.org/x/sys v0.9.0
go: downloading modernc.org/ccgo/v3 v3.16.13
go: downloading modernc.org/libc v1.29.0
go: downloading modernc.org/mathutil v1.6.0
go: downloading github.com/mattn/go-sqlite3 v1.14.16
go: downloading github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26
go: downloading modernc.org/tcl v1.15.2
go: downloading github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec
go: downloading github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
go: downloading golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78
go: downloading modernc.org/cc/v3 v3.40.0
go: downloading modernc.org/opt v0.1.3
go: downloading github.com/dustin/go-humanize v1.0.1
go: downloading github.com/google/uuid v1.3.0
go: downloading github.com/mattn/go-isatty v0.0.16
go: downloading modernc.org/memory v1.7.2
go: downloading golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
go: downloading golang.org/x/mod v0.3.0
go: downloading lukechampine.com/uint128 v1.2.0
go: downloading modernc.org/strutil v1.1.3
go: downloading modernc.org/token v1.0.1
go: downloading modernc.org/httpfs v1.0.6
go: downloading modernc.org/z v1.7.3
go: downloading modernc.org/ccorpus v1.11.6
====================================================================
Output:
The main method calls the listDrivers function to print out the names of the loaded drivers and then
calls the openDatabase function to open the database. Nothing is done with the database yet, but Close
method is called.
████████████████████████████████████████████████████████████████████████
469.The DB Method for Closing the Database:
Name Description
------- ----------------------
Close() This function closes the database and prevents further operations from being performed.
████████████████████████████████████████████████████████████████████████
470.Executing Statements and Queries
The DB Methods for Executing SQL Statements
Name Description
-------------------- -----------------------------
Query(query,...args) This method executes the specified query, using the optional placeholder arguments.
The results are a Rows struct, which contains the query results, and an error that
indicates problems executing the query.
QueryRow(query, ..args) This method executes the specified query, using the optional placeholder arguments.
The result is a Row struct, which represents the first row from the query results. See
the “Executing Queries for Single Rows” section.
Exec(query,...args) This method executes statements or queries that do not return rows of data. The
method returns a Result, which describes the response from the database, and
an error that signals problems with execution. See the “Executing Other Queries” section.
████████████████████████████████████████████████████████████████████████
471.Using Contexts with Databases
the context package and the Context interface it defines, which is used
to manage requests as they are processed by a server. All the important methods defined in the
database/sql package also have versions that accept a Context argument, which is useful if you
want to take advantage of features like request handling timeouts.
████████████████████████████████████████████████████████████████████████
472.Querying for Multiple Rows
The Query method executes a query that retrieves one or more rows from the database. The Query method
returns a Rows struct, which contains the query results and an error that indicates problems. The row data is
accessed through the methods described in below
The Rows Struct Methods
Name Description
---------------- -----------------------------
Next() This method advances to the next result row. The result is a bool, which is true when
there is data to read and false when the end of the data has been reached, at which point
the Close method is automatically called.
NextResultSet() This method advances to the next result set when there are multiple result sets in the
same database response. The method returns true if there is another set of rows to process.
Scan(...targets) This method assigns the SQL values from the current row to the specified variables. The
values are assigned via pointers and the method returns an error that indicates when the
values cannot be scanned. See the “Understanding the Scan Method” section for details.
Close() This method prevents further enumeration of the results and is used when not all of
the data is required. There is no need to call this method if the Next method is used to
advance until it returns false.
████████████████████████████████████████████████████████████████████████
473.Querying the Database in the main.go
example:
package main
import "database/sql"
func queryDatabase(db *sql.DB) {
rows, err := db.Query("SELECT * from Products")
if err == nil {
for rows.Next() {
var id, category int
var name string
var price float64
rows.Scan(&id, &name, &category, &price)
Printfln("Row: %v %v %v %v", id, name, category, price)
}
} else {
Printfln("Error: %v", err)
}
}
func main() {
//listDrivers()
db, err := openDatabase()
if err == nil {
queryDatabase(db)
db.Close()
} else {
panic(err)
}
}
====================================================================
The queryDatabase function performs a simple SELECT query on the Products table with the Query
method, which produces a Rows result and an error. If the error is nil, a for loop is used to move through
the result rows by calling the Next method, which returns true if there is a row to process and returns false
when the end of the data has been reached.
████████████████████████████████████████████████████████████████████████
474.Using Reflection
Preparing for This Chapter
1-go mod init reflection
2-printer.go:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
3-types.go:
package main
type Product struct {
Name, Category string
Price float64
}
type Customer struct {
Name, City string
}
4-main.go:
package main
func printDetails(values ...Product) {
for _, elem := range values {
Printfln("Product: Name: %v, Category: %v, Price: %v",
elem.Name, elem.Category, elem.Price)
}
}
func main() {
product := Product {
Name: "Kayak", Category: "Watersports", Price: 279,
}
printDetails(product)
}
====================================================================
Output:
Product: Name: Kayak, Category: Watersports, Price: 279
████████████████████████████████████████████████████████████████████████
475.Understanding the Need for Reflection
The Go type system is rigorously enforced,
which means you can't use a value of one type when a different type is inspected.
example:
Mixing Types in the main.go
package main
func printDetails(values ...Product) {
for _, elem := range values {
Printfln("Product: Name: %v, Category: %v, Price: %v",
elem.Name, elem.Category, elem.Price)
}
}
func main() {
product := Product {
Name: "Kayak", Category: "Watersports", Price: 279,
}
customer := Customer { Name: "Alice", City: "New York" }
printDetails(product, customer)
}
====================================================================
Output:
# reflection
./main.go:13:24: cannot use customer (variable of type Customer) as Product value in argument to printDetails
interfaces, which allow common characteristics to be defined through
methods, which can be invoked regardless of the type that implements the interface.
████████████████████████████████████████████████████████████████████████
476.Using the Empty Interface in the main.go File in the reflection
example:
package main
func printDetails(values ...interface{}) {
for _, elem := range values {
switch val := elem.(type) {
case Product:
Printfln("Product: Name: %v, Category: %v, Price: %v",
val.Name, val.Category, val.Price)
case Customer:
Printfln("Customer: Name: %v, City: %v", val.Name, val.City)
}
}
}
func main() {
product := Product {
Name: "Kayak", Category: "Watersports", Price: 279,
}
customer := Customer { Name: "Alice", City: "New York" }
printDetails(product, customer)
}
====================================================================
Output:
Product: Name: Kayak, Category: Watersports, Price: 279
Customer: Name: Alice, City: New York
The limitation of this approach is that the printDetails function
can only process types that are known in advance.
Many projects will deal with a small enough set of types that this won't be an issue or will be able to
define interfaces with methods that provides access to common functionality. Reflection solves this issue for
those projects for which this isn't the case, either because there are a large number of types to deal with or
because interfaces and methods can't be written.
████████████████████████████████████████████████████████████████████████
477.Using Reflection
The reflect package provides the Go reflection features, and the key functions are called TypeOf and
ValueOf
████████████████████████████████████████████████████████████████████████
478.The Key Reflection Functions
Name Description
----------- -------------------------------
TypeOf(val) This function returns a value that implements the Type interface, which describes the
type of the specified value.
ValueOf(val) This function returns a Value struct, which allows the specified value to be inspected
and manipulated.
████████████████████████████████████████████████████████████████████████
479.Using Reflection in the main.go
example:
package main
import (
"reflect"
"strings"
"fmt"
)
func printDetails(values ...interface{}) {
for _, elem := range values {
fieldDetails := []string {}
elemType := reflect.TypeOf(elem)
elemValue := reflect.ValueOf(elem)
if elemType.Kind() == reflect.Struct {
for i := 0; i < elemType.NumField(); i++ {
fieldName := elemType.Field(i).Name
fieldVal := elemValue.Field(i)
fieldDetails = append(fieldDetails,
fmt.Sprintf("%v: %v", fieldName, fieldVal ))
}
Printfln("%v: %v", elemType.Name(), strings.Join(fieldDetails, ", "))
} else {
Printfln("%v: %v", elemType.Name(), elemValue)
}
}
}
type Payment struct {
Currency string
Amount float64
}
func main() {
product := Product {
Name: "Kayak", Category: "Watersports", Price: 279,
}
customer := Customer { Name: "Alice", City: "New York" }
payment := Payment { Currency: "USD", Amount: 100.50 }
printDetails(product, customer, payment, 10, true)
}
====================================================================
Code that uses reflection can be verbose, but the basic pattern becomes easy to follow once you become
familiar with the basics. The key point to remember is that there are two aspects of reflection that work
together: the reflected type and the reflected value.
The reflected type gives you access to details of a Go type without knowing in advance what it is. You can
explore the reflected type, exploring its details and characteristics through the methods defined by the Type
interface.
The reflected value lets you work with the specific value with which you have been provided. You can't
just read a struct field or call a method, for example, as you would in normal code when you don't know
what type you are dealing with.
The use of the reflected type and reflected value leads to the code verbosity. If you know you are dealing
with a Product struct, for example, you can just read the Name field and get a string result. If you don't know
what type is being used, then you must use the reflected type to establish whether you are dealing with a
struct and whether it has a Name field. Once you have determined there is such as field, you use the reflected
value to read that field and get its value.
Output:
Product: Name: Kayak, Category: Watersports, Price: 279
Customer: Name: Alice, City: New York
Payment: Currency: USD, Amount: 100.5
int: 10
bool: true
████████████████████████████████████████████████████████████████████████
480.Using the Basic Type Features
The Type interface provides basic details about a type through the methods described in below.
There are specialized methods for working with specific kinds of types, such as arrays, which are described in later
sections, but these are the methods that provide the essential details for all types.
████████████████████████████████████████████████████████████████████████
481.Basic Methods Defined by the Type Interface
Name Description
--------------- ------------------------------------
Name() This method returns the name of the type.
PkgPath() This method returns the package path for the type. The empty string is returned for
built-in types, such as int and bool.
Kind() This method returns the kind of type, using a value that matches one of the constant
values defined by the reflect package, as described in Table 27-5.
String() This method returns a string representation of the type name, including the package name.
Comparable() This method returns true if values of this type can be compared using the standard
comparison operator, as described in the “Comparing Values” section.
AssignableTo(type) This method returns true if values of this type can be assigned to variables or fields of
the specified reflected type.
████████████████████████████████████████████████████████████████████████
482.The Kind Constants
The reflect package defines a type named Kind, which is an alias for uint, and which is used for a
series of constants that describe different kinds of type.
Name Description
---------------------- -----------------------------------------
Bool This value denotes a bool.
Int, Int8, Int16, Int32, Int64 These values denote the different sizes of integer types.
Uint, Uint8, Uint16, Uint32, Uint64 These values denote the different sizes of unsigned integer types.
Float32, Float64 These values denote the different sizes of floating-point types.
String This value denotes a string.
Struct This value denotes a struct.
Array This value denotes an array.
Slice This value denotes a slice.
Map This value denotes a map.
Chan This value denotes a channel.
Func This value defines a function.
Interface This value denotes an interface.
Ptr This value denotes a pointer
Uintptr This value denotes an unsafe pointer, which is not described in this book.
████████████████████████████████████████████████████████████████████████
483.Printing Type Details in the main.go File in the reflection Folder
added a function that replaces empty package names so that built-in types are more obviously described.
example:
main.go:
package main
import (
"reflect"
// "strings"
// "fmt"
)
func getTypePath(t reflect.Type) (path string) {
path = t.PkgPath()
if path == "" {
path = "(built-in)"
}
return
}
func printDetails(values ...interface{}) {
for _, elem := range values {
elemType := reflect.TypeOf(elem)
Printfln("Name: %v, PkgPath: %v, Kind: %v",
elemType.Name(), getTypePath(elemType), elemType.Kind())
}
}
type Payment struct {
Currency string
Amount float64
}
func main() {
product := Product{
Name: "Kayak", Category: "Watersports", Price: 279,
}
customer := Customer{Name: "Alice", City: "New York"}
payment := Payment{Currency: "USD", Amount: 100.50}
printDetails(product, customer, payment, 10, true)
}
==============================
printer.go:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
==============================
types.go:
package main
type Product struct {
Name, Category string
Price float64
}
type Customer struct {
Name, City string
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Output:
Name: Product, PkgPath: main, Kind: struct
Name: Customer, PkgPath: main, Kind: struct
Name: Payment, PkgPath: main, Kind: struct
Name: int, PkgPath: (built-in), Kind: int
Name: bool, PkgPath: (built-in), Kind: bool
████████████████████████████████████████████████████████████████████████
484.Using the Basic Value Features
For each group of reflected type features, there are corresponding features for reflected values. The Value
struct defines the methods described in here, which provide access to basic reflection features,
including accessing the underlying value.
Basic Methods Defined by the Value Struct
Name Description
------- -----------------------------------------------------
Kind() This method returns the kind of the value's type.
Type() This method returns the Type for the Value.
IsNil() This method returns true if the value is nil. This method will panic if the underlying value
isn't a function, an interface, a pointer, a slice, or a channel.
IsZero() This method returns true if the underlying value is the zero value for its type.
Bool() This method returns the underlying bool value. The method panics if the underlying value's Kind is not Bool.
Bytes() This method returns the underlying []byte value. The method panics if the underlying
value is not a byte slice. I demonstrate how to determine the type of a slice in the
“Identifying Byte Slices” section.
Int() This method returns the underlying value as an int64. The method panics if the underlying
value's Kind is not Int, Int8, Int16, Int32, or Int64.
Uint() This method returns the underlying value as an uint64. The method panics if the underlying
value's Kind is not Uint, Uint8, Uint16, Uint32, or Uint64.
Float() This method returns the underlying value as an float64. The method panics if the
underlying value's Kind is not Float32, or Float64.
String() This method returns the underlying value as a string if the value's Kind is String. For other
Kind values, this method returns the string <T Value> where T is the underlying type, such
as <int Value>.
Elem() This method returns the Value to which a pointer refers. This method can also be used with
interfaces, as described in Chapter 29. This method panics if the underlying value's Kind is not Ptr.
IsValid() This method returns false if the Value is the zero value, created as Value{} rather than
obtained using ValueOf, for example. This method doesn't relate to reflected values that
are the zero value of their reflected type. If this method returns false, then all other Value
methods will panic.
████████████████████████████████████████████████████████████████████████
485.Using the Basic Value Methods in the main.go
example:
main.go:
package main
import (
"reflect"
// "strings"
// "fmt"
)
func printDetails(values ...interface{}) {
for _, elem := range values {
elemValue := reflect.ValueOf(elem)
switch elemValue.Kind() {
case reflect.Bool:
var val bool = elemValue.Bool()
Printfln("Bool: %v", val)
case reflect.Int:
var val int64 = elemValue.Int()
Printfln("Int: %v", val)
case reflect.Float32, reflect.Float64:
var val float64 = elemValue.Float()
Printfln("Float: %v", val)
case reflect.String:
var val string = elemValue.String()
Printfln("String: %v", val)
case reflect.Ptr:
var val reflect.Value = elemValue.Elem()
if val.Kind() == reflect.Int {
Printfln("Pointer to Int: %v", val.Int())
}
default:
Printfln("Other: %v", elemValue.String())
}
}
}
func main() {
product := Product{
Name: "Kayak", Category: "Watersports", Price: 279,
}
number := 100
printDetails(true, 10, 23.30, "Alice", &number, product)
}
================================================================
printer:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
================================================================
types.go:
package main
type Product struct {
Name, Category string
Price float64
}
type Customer struct {
Name, City string
}
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Output:
Bool: true
Int: 10
Float: 23.3
String: Alice
Pointer to Int: 100
Other: <main.Product Value>
████████████████████████████████████████████████████████████████████████
486.Comparing Types in the main.go
main.go:
package main
import (
"reflect"
// "strings"
// "fmt"
)
var intPtrType = reflect.TypeOf((*int)(nil))
func printDetails(values ...interface{}) {
for _, elem := range values {
elemValue := reflect.ValueOf(elem)
elemType := reflect.TypeOf(elem)
if elemType == intPtrType {
Printfln("Pointer to Int: %v", elemValue.Elem().Int())
} else {
switch elemValue.Kind() {
case reflect.Bool:
var val bool = elemValue.Bool()
Printfln("Bool: %v", val)
case reflect.Int:
var val int64 = elemValue.Int()
Printfln("Int: %v", val)
case reflect.Float32, reflect.Float64:
var val float64 = elemValue.Float()
Printfln("Float: %v", val)
case reflect.String:
var val string = elemValue.String()
Printfln("String: %v", val)
// case reflect.Ptr:
// var val reflect.Value = elemValue.Elem()
// if (val.Kind() == reflect.Int) {
// Printfln("Pointer to Int: %v", val.Int())
// }
default:
Printfln("Other: %v", elemValue.String())
}
}
}
}
func main() {
product := Product{
Name: "Kayak", Category: "Watersports", Price: 279,
}
number := 100
printDetails(true, 10, 23.30, "Alice", &number, product)
}
====================================================================
printer.go:
package main
import "fmt"
func Printfln(template string, values ...interface{}) {
fmt.Printf(template + "\n", values...)
}
====================================================================
types:
package main
type Product struct {
Name, Category string
Price float64
}
type Customer struct {
Name, City string
}
====================================================================
Output:
Bool: true
Int: 10
Float: 23.3
String: Alice
Pointer to Int: 100
Other: <main.Product Value>
████████████████████████████████████████████████████████████████████████
487.Identifying Byte Slices
████████████████████████████████████████████████████████████████████████
488.go tool dist list
Output:
aix/ppc64
android/386
android/amd64
android/arm
android/arm64
darwin/amd64
darwin/arm64
dragonfly/amd64
freebsd/386
freebsd/amd64
freebsd/arm
freebsd/arm64
freebsd/riscv64
illumos/amd64
ios/amd64
ios/arm64
js/wasm
linux/386
linux/amd64
linux/arm
linux/arm64
linux/loong64
linux/mips
linux/mips64
linux/mips64le
linux/mipsle
linux/ppc64
linux/ppc64le
linux/riscv64
linux/s390x
netbsd/386
netbsd/amd64
netbsd/arm
netbsd/arm64
openbsd/386
openbsd/amd64
openbsd/arm
openbsd/arm64
plan9/386
plan9/amd64
plan9/arm
solaris/amd64
wasip1/wasm
windows/386
windows/amd64
windows/arm
windows/arm64
████████████████████████████████████████████████████████████████████████
489.
████████████████████████████████████████████████████████████████████████
490.
████████████████████████████████████████████████████████████████████████
491.
████████████████████████████████████████████████████████████████████████
492.
████████████████████████████████████████████████████████████████████████
493.
████████████████████████████████████████████████████████████████████████
494.
████████████████████████████████████████████████████████████████████████
495.
████████████████████████████████████████████████████████████████████████
496.
████████████████████████████████████████████████████████████████████████
497.
████████████████████████████████████████████████████████████████████████
498.
████████████████████████████████████████████████████████████████████████
499.
████████████████████████████████████████████████████████████████████████
500.
████████████████████████████████████████████████████████████████████████
501.
████████████████████████████████████████████████████████████████████████
502.
████████████████████████████████████████████████████████████████████████
503.
████████████████████████████████████████████████████████████████████████
504.
████████████████████████████████████████████████████████████████████████
505.
████████████████████████████████████████████████████████████████████████
506.
████████████████████████████████████████████████████████████████████████
507.
████████████████████████████████████████████████████████████████████████
508.
████████████████████████████████████████████████████████████████████████
509.
████████████████████████████████████████████████████████████████████████
510.
████████████████████████████████████████████████████████████████████████
511.
████████████████████████████████████████████████████████████████████████
512.
████████████████████████████████████████████████████████████████████████
513.
████████████████████████████████████████████████████████████████████████
514.
████████████████████████████████████████████████████████████████████████
515.
████████████████████████████████████████████████████████████████████████
516.
████████████████████████████████████████████████████████████████████████
517.
████████████████████████████████████████████████████████████████████████
518.
████████████████████████████████████████████████████████████████████████
519.
████████████████████████████████████████████████████████████████████████
420.
████████████████████████████████████████████████████████████████████████
521.
████████████████████████████████████████████████████████████████████████
522.
████████████████████████████████████████████████████████████████████████
523.
████████████████████████████████████████████████████████████████████████
524.
████████████████████████████████████████████████████████████████████████
525.
████████████████████████████████████████████████████████████████████████
526.
████████████████████████████████████████████████████████████████████████
527.
████████████████████████████████████████████████████████████████████████
528.
████████████████████████████████████████████████████████████████████████
529.
████████████████████████████████████████████████████████████████████████
530.
████████████████████████████████████████████████████████████████████████
531.
████████████████████████████████████████████████████████████████████████
532.
████████████████████████████████████████████████████████████████████████
533.
████████████████████████████████████████████████████████████████████████
534.
████████████████████████████████████████████████████████████████████████
535.
████████████████████████████████████████████████████████████████████████
536.
████████████████████████████████████████████████████████████████████████
537.
████████████████████████████████████████████████████████████████████████
538.
████████████████████████████████████████████████████████████████████████
539.
████████████████████████████████████████████████████████████████████████
540.
████████████████████████████████████████████████████████████████████████
541.
████████████████████████████████████████████████████████████████████████
542.
████████████████████████████████████████████████████████████████████████
543.
████████████████████████████████████████████████████████████████████████
544.
████████████████████████████████████████████████████████████████████████
545.
████████████████████████████████████████████████████████████████████████
546.
████████████████████████████████████████████████████████████████████████
547.
████████████████████████████████████████████████████████████████████████
548.
████████████████████████████████████████████████████████████████████████
549.
████████████████████████████████████████████████████████████████████████
550.
████████████████████████████████████████████████████████████████████████
551.
████████████████████████████████████████████████████████████████████████
552.
████████████████████████████████████████████████████████████████████████
553.
████████████████████████████████████████████████████████████████████████
554.
████████████████████████████████████████████████████████████████████████
555.
████████████████████████████████████████████████████████████████████████
556.
████████████████████████████████████████████████████████████████████████
557.
████████████████████████████████████████████████████████████████████████
558.
████████████████████████████████████████████████████████████████████████
559.
████████████████████████████████████████████████████████████████████████
560.
████████████████████████████████████████████████████████████████████████
561.
████████████████████████████████████████████████████████████████████████
562.
████████████████████████████████████████████████████████████████████████
563.
████████████████████████████████████████████████████████████████████████
564.
████████████████████████████████████████████████████████████████████████
565.
████████████████████████████████████████████████████████████████████████
566.
████████████████████████████████████████████████████████████████████████
567.
████████████████████████████████████████████████████████████████████████
568.
████████████████████████████████████████████████████████████████████████
569.
████████████████████████████████████████████████████████████████████████
570.
████████████████████████████████████████████████████████████████████████
571.
████████████████████████████████████████████████████████████████████████
572.
████████████████████████████████████████████████████████████████████████
573.
████████████████████████████████████████████████████████████████████████
574.
████████████████████████████████████████████████████████████████████████
575.
████████████████████████████████████████████████████████████████████████
576.
████████████████████████████████████████████████████████████████████████
577.
████████████████████████████████████████████████████████████████████████
578.
████████████████████████████████████████████████████████████████████████
579.
████████████████████████████████████████████████████████████████████████
580.
████████████████████████████████████████████████████████████████████████
581.
████████████████████████████████████████████████████████████████████████
582.
████████████████████████████████████████████████████████████████████████
583.
████████████████████████████████████████████████████████████████████████
584.
████████████████████████████████████████████████████████████████████████
585.
████████████████████████████████████████████████████████████████████████
586.
████████████████████████████████████████████████████████████████████████
587.
████████████████████████████████████████████████████████████████████████
588.
████████████████████████████████████████████████████████████████████████
589.
████████████████████████████████████████████████████████████████████████
590.
████████████████████████████████████████████████████████████████████████
591.
████████████████████████████████████████████████████████████████████████
592.
████████████████████████████████████████████████████████████████████████
593.
████████████████████████████████████████████████████████████████████████
594.
████████████████████████████████████████████████████████████████████████
595.
████████████████████████████████████████████████████████████████████████
596.
████████████████████████████████████████████████████████████████████████
597.
████████████████████████████████████████████████████████████████████████
598.
████████████████████████████████████████████████████████████████████████
599.
████████████████████████████████████████████████████████████████████████
600.
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
████████████████████████████████████████████████████████████████████████
`,
}
type FunctionsDefinitions ¶ added in v1.0.32
type QuestionsSampleStruct ¶ added in v1.0.28
type SingleDefinitions ¶
type WordsInGolangNumbered ¶ added in v1.0.32
Source Files ¶
- Answers.go
- AnswersSample.go
- CreatingHTTPClients.go
- CreatingHTTPServers.go
- Every words in Golang numbered.go
- Every words in Golang.go
- FunctionsDefinition.go
- MapUsages.go
- Printer.go
- Questions.go
- QuestionsSample.go
- ReadingandWriting.go
- RegEx.go
- SingleDefinitions.go
- SingleDefinitionsExamples.go
- Time.go
- UsingHTML&TextTemplates.go
- UsingReflection.go
- WorkingwithDatabases.go
- WorkingwithFiles.go
- WorkingwithJSONData.go
- helpMessage.go
Click to show internal directories.
Click to hide internal directories.