GoLang: Hex Output
In last few blogs I have written about reading commandline
parameters reading files
and using the os
package.
In this blog, I will combine some these items to read a file byte
by byte
using the bufio
package func
‘s.
Some of the things I am still learning while I try out different things is that how to organise the perfect folder structure to ensure what I learn is manageable to refer back when and if I revisit.
Still refining, but am getting better by the day.
Challenges
- Defining the flag to get the input from the command line using the
-f
flag. - If the input is not provided the format expected should be printed.
- For a file present, read byte by byte and output
HEX:Byte
representation.
Defining flag
var fileName string
func init() {
flag.StringVar(&fileName, "f", "", "File to read")
flag.Parse()
}
Why the flag.Parse()
is defined here in Init()
? Before I get there let me add some details about init
and Parse
.
What is Init()?
Each source file can define its own niladic (operator or function having no arguments) init
function (one or more) to set up whatever state is required.
The function init
is called after all the variable declarations in the package have evaluated their initializers, and those are evaluated only after all the imported packages have been initialized.
Parse
Also Parse() must be called after all flags are defined and before flags are accessed by the program.
Since it is to set state and I will be calling the main processing logic from a different source file it is best to initialise the state in init()
else the flag might not be set properly.
Hex:Byte
The function below are quite self-explanatory
OpenAndDump
This function is to open a file passed to it as an argument and uses os.Open
to open the file and then a buffered reader is created to read the file byte by byte (using the method dump
) and fmt.Fprintf
is used to push the content to Stdout
.
//dump dumps the content from a reader
func dump(fr *bufio.Reader) {
fmt.Println("\n -------- STARTOFFILE -------")
for {
b, err := fr.ReadByte()
if err == io.EOF {
break
}
fmt.Fprintf(os.Stdout, " %X:%c", b, b)
}
fmt.Println("\n ------ ENDOFFILE ------")
}
// OpenAndDump a file and dumps the content
func OpenAndDump(f string) {
fh, err := os.Open(f)
if err == nil {
dump(bufio.NewReader(fh))
os.Exit(0)
}
defer fh.Close()
fmt.Fprintf(os.Stderr, "ERR: (%s) %s", f, err.Error())
return
}
Entry function: Cmd()
It does the following
- Checks for the number of flags (NFlag) passed, if 0 then prints the default usage and exits with error.
- If argument
-f
is set then invoke theOpenAndDump
method defined.
const (
// InvalidArgument for the program
InvalidArgument = "Please provide a valid file\n"
)
// Cmd Runs the command
func Cmd() {
// https://golang.org/pkg/flag/#NArg
// fmt.Fprintf(os.Stdout, "DBG: %d %s\n", flag.NArg(), fileName)
if flag.NFlag() == 0 {
fmt.Fprintf(os.Stderr, "%s", InvalidArgument)
flag.PrintDefaults()
os.Exit(1)
}
//OpenAndDump(flag.Arg(i))
OpenAndDump(fileName)
}
Sample Output
./main -f filer.txt
File: filer.txt
I am a sample file.
I have a new line.
I even have an int 10.
Output
-------- STARTOFFILE -------
49:I 20: 61:a 6D:m 20: 61:a 20: 73:s 61:a 6D:m 70:p 6C:l 65:e 20: 66:f 69:i 6C:l 65:e 2E:. A:
49:I 20: 68:h 61:a 76:v 65:e 20: 61:a 20: 6E:n 65:e 77:w 20: 6C:l 69:i 6E:n 65:e 2E:. A:
49:I 20: 65:e 76:v 65:e 6E:n 20: 68:h 61:a 76:v 65:e 20: 61:a 6E:n 20: 69:i 6E:n 74:t 20: 31:1 30:0 2E:.
------ ENDOFFILE ------