What is the most portable/cross-platform way to represent a newline in go/golang?
Asked Answered
B

4

48

Currently, to represent a newline in go programs, I use \n. For example:

package main

import "fmt"


func main() {
    fmt.Printf("%d is %s \n", 'U', string(85))
}

... will yield 85 is U followed by a newline.

However, this doesn't seem all that cross-platform. Looking at other languages, PHP represents this with a global constant ( PHP_EOL ). Is \n the right way to represent newlines in a cross-platform specific manner in go / golang?

Babette answered 24/1, 2013 at 4:15 Comment(0)
A
28

I got curious about this so decided to see what exactly is done by fmt.Println. http://golang.org/src/pkg/fmt/print.go

If you scroll to the very bottom, you'll see an if addnewline where \n is always used. I can't hardly speak for if this is the most "cross-platform" way of doing it, and go was originally tied to linux in the early days, but that's where it is for the std lib.

I was originally going to suggest just using fmt.Fprintln and this might still be valid as if the current functionality isn't appropriate, a bug could be filed and then the code would simply need to be compiled with the latest Go toolchain.

Ablepsia answered 24/1, 2013 at 4:50 Comment(4)
It is OK to have "\n" to be printed directly to some output stream because of newline translation which is enabled when this output stream is non-binary. This is a property of C runtime library on which Go is based: it automatically converts line endings to the ones used by the platform, so it is safe to use just "\n" in the program code. This works only for non-binary streams though, and it won't be easy just to get platform line separator (e.g. as a string) using only C runtime.Axiom
@VladimirMatveev, are you sure Go is based on C runtime? It seems its standard compilers do not depend on C runtime and produce statically-linked binaries which do not depend on anything either.Prominence
@kostix, it seems you're right. Looking through internals of Go I/O machinery suggests that they are using POSIX open() syscall on Unix systems and CreateFile on Windows systems to open files. Buffer supplied to os.File#Write seems to be sent unchanged directly to Windows WriteFile API function. Windows API functions do not do line ending conversions, so the output will contain only LF in case when there were only LF in the source.Axiom
@VladimirMatveev, by the way I recently implemented parsing of a simple text file, basically using bufio.ReadString(), and thought I would like to have also a bit more higher-level function for a task like this, which would read up to either \r\n or \n (single \r seems to not be relevant anymore) and return the text not including the EOL sequence. This is how Tcl's gets works for instance. But this is really a topic to discuss on golang-nuts...Prominence
L
22

Having the OS determine what the newline character is happens in many contexts to be wrong. What you really want to know is what the "record" separator is and Go assumes that you as the programmer should know that.

Even if the binary runs on Windows, it may be consuming a file from a Unix OS.

Line endings are determined by what the source of the file or document said was a line ending, not the OS the binary is running in.

Langur answered 24/1, 2013 at 21:18 Comment(0)
Y
19

You can always use an OS specific file to declare certain constants. Just like _test.go files are only used when doing go test, the _[os].go are only included when building to that target platform.

Basically you'll need to add the following files:

 - main.go
 - main_darwin.go     // Mac OSX
 - main_windows.go    // Windows
 - main_linux.go      // Linux

You can declare a LineBreak constant in each of the main_[os].go files and have your logic in main.go.

The contents of you files would look something like this:

main_darwin.go

package somepkg

const LineBreak = "\n"

main_linux.go

package somepkg

const LineBreak = "\n"

main_windows.go

package somepkg

const LineBreak = "\r\n"

and simply in your main.go file, write the code and refer to LineBreak

main.go

package main

import "fmt"


func main() {
    fmt.Printf("%d is %s %s", 'U', string(85), LineBreak)
}
Yarmouth answered 22/4, 2018 at 7:5 Comment(1)
Splitting between Windows and non-Windows should be enough. Note that there are more GOOS values beyond linux, darwin and windows.Kissinger
M
-2

You can use os.PathSeparator

func main() {
    var PS = fmt.Sprintf("%v", os.PathSeparator)
    var LineBreak = "\n"
    if PS != "/" {
        LineBreak = "\r\n"
    }
    fmt.Printf("Line Break %v", LineBreak)
}

https://play.golang.com/p/UTnBbTJyL9c

Mn answered 2/2, 2023 at 10:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.