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

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?

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.

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)
}

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.

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

Related

How can I run a bat file with spaces in the filepath?

I want to run a bat file used to compile sass to css from within a Kotlin program, on a Windows machine. I had everything working using Runtime.exec until I switched to a Windows account that had a space in the username. From what I read, I read that using ProcessBuilder would make this easier. It seems that even with ProcessBuilder I still can't get it to work, no matter what I try.
Here is my code so far
val commands = mutableListOf(
"cmd",
"/c",
"C:\\Users\\John Doe\\VCS\\test\\tools\\sass\\windows\\dart-sass\\sass.bat",
"--no-source-map",
"C:\\Users\\John Doe\\VCS\\test\\src\\main\\sass\\global.scss",
"global.css"
)
val processBuilder = ProcessBuilder(commands)
val process = processBuilder.start()
...
The error I get is 'C:\Users\John' is not recognized as an internal or external command, operable program or batch file. It doesn't help if I surround the strings that have spaces with \".
If I remember correctly, all windows files and folders that have a space in the name have a matching short name in the old 8.3 format replacing additional space and other characters with a tilde (~) and a number.
So whatever is returning you the path for the .bat and .sscs files could return the full filename in that format?
Doesn't solve the problem but avoids it instead, I admit.
Also means you won't get busted when someone puts a space in the filename (OK, unlikely, but still something better to deal with from the start).
Consider something along the lines of the top 2 answers on this superuser thread
This is actually a Windows cmd issue. The question here shows that in cmd, in addition to quoting the file paths, you also have to quote the entire part of the command line text after the /c switch.
I don't know if this is the best way to do it in ProcessBuilder, but I was able to get it to work with the following code.
"cmd.exe",
"/c",
"\"\"C:/Users/John Doe/VCS/test/tools/sass/windows/dart-sass/sass.bat\" "
+ "--no-source-map "
+ "\"C:/Users/John Doe/VCS/test/src/main/sass/global.scss\" "
+ "\"global.css\"\""

Kotlin prints non-English characters as question marks

I am trying to print Hebrew characters from a Kotlin program (running on the console).
All the Hebrew characters are being output as question marks.
I created the following simple test.kts script file for testing:
println("שלום מקוטלין")
// Try to print a simple non-Hebrew character too
println("\u0394") // Greek Delta
The file is properly saved in UTF-8 format.
It prints:
???? ???????
?
I tried running it in Command Prompt, PowerShell (both in its native window and in Windows Terminal), and Git Bash, all of which give the same result. I also tried redirecting the output to a file to rule out display issues in the shells.
To make sure the problem isn't the console itself, I also made simple test.bat, test.ps1, and test.sh files with the following content:
echo "שלום מקוטלין"
All three shells correctly displayed the Hebrew text here, indicating that the problem is in Kotlin's output, not in the shell display. (Though PowerShell requires the file to be saved "UTF-8 with BOM" to display properly, this can't be the issue with Kotlin since Kotlin won't even run a script that is saved with a BOM.)
As far as I can tell, Kotlin should support UTF-8 output by default with no configuration needed.
How can I get the proper output?
Updates:
If I write the output to a file using java.io.File("out.txt").writeText("שלום מקוטלין"), it works properly.
Also, if I open a new PrintStream using val out = java.io.PrintStream(System.out, true, "UTF-8") and then write to it using out.println("שלום מקוטלין"), that works properly too.
Only writing to the console with println is broken.
System info:
Windows 10 2004 (Build 19041.450)
Kotlin 1.4.0 (downloaded from GitHub Releases)
Tested with JAVA_HOME pointing to both JRE 1.8.0_261 (Oracle) and 11.0.2 (Oracle OpenJDK).
(Update at bottom)
Partial answer, but was able to get some Hebrew characters in the console in both Kotlin and Java. Was verry painful. Included some commented out stuff to show you some other things I may have tried if you run into any other hurdles.
Saved Tester.kt as UTF-8 with Notepad.
fun main(args : Array<String>) {
System.setProperty("file.encoding", "UTF8")
//val charset = Charsets.UTF_8
//val byteArray = "שלום מקוטלין".toByteArray(charset)
//System.out.printf("%c",byteArray.toString(charset))
//System.out.println(Charset.defaultCharset())
System.out.println("ל")
}
kotlinc.bat .\Tester.kt -include-runtime -d Tester.jar
Now, this leads to another mess, which I discovered by trying to copy and paste Hebrew characters to Powershell/Cmd. When copying, the ? marks showed right off the bat. Dug around a little bit, seems Powershell ISE is better suited for this (reference below). Without any plugins, copy and pasted successfully. Then had to run this:
PS> [Console]::OutputEncoding = [System.Text.Encoding]::UTF8
Because on my system, running the following showed:
PS> [Console]::OutputEncoding
IsSingleByte : True
BodyName : iso-8859-1
EncodingName : Western European (Windows)
HeaderName : Windows-1252
WebName : Windows-1252
WindowsCodePage : 1252
IsBrowserDisplay : True
IsBrowserSave : True
IsMailNewsDisplay : True
IsMailNewsSave : True
EncoderFallback : System.Text.InternalEncoderBestFitFallback
DecoderFallback : System.Text.InternalDecoderBestFitFallback
IsReadOnly : True
CodePage : 1252
Then,
java -jar -D"file.encoding=UTF-8" tester.jar
and voila, a single Lamedh
ל
Also, the Java route, which may or may not bring more insights:
Tester.java saved as UTF-8 with Notepad, imports redundant, yes, but shows some standout imports
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import static java.nio.charset.StandardCharsets.*;
import java.nio.*;
public class Tester{
public static void main(String[] args){
String str1 = "שלום מקוטלין";
byte[] ptext = str1.getBytes(UTF_8);
String value = new String(ptext, UTF_8);
ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode("ש");
System.out.println(Charset.defaultCharset());
System.out.println("שלום מקוטלין");
System.out.println(value);
System.out.print(byteBuffer.getChar());
System.out.printf("Value: %s",value);
}
}
javac would give:
javac .\Tester.java
.\Tester.java:8: error: unmappable character (0x9D) for encoding windows-1252
System.out.println("╫⌐╫£╫ò╫? ╫₧╫º╫ò╫ÿ╫£╫Ö╫ƒ");
So
javac -encoding UTF-8 .\Tester.java
and voila again, PS ISE only:
PS> java -D"file.encoding=UFT-8" Tester
UTF-8
שלום מקוטלין
שלום מקוטלין
힩Value: שלום מקוטלין
I think this shows there are several hurdles, but it can work with Kotlin, and with println after making sure the file is correct, running the file the right way, and the output is correct. Hebrew may be particularly difficult due to the right-to-left nature, other characters like Greek were easier I think.
No matter what, I feel your pain, good luck. From what I read, there may be other bottlenecks like sending Hebrew over a network. This opened my eyes to several things, will continue to learn about this myself.
(Update)
Using the second link in the reference actually provided before, you can make two small changes and get Hebrew in Powershell (not just ISE)!!
PS> $OutputEncoding = [console]::InputEncoding = [console]::OutputEncoding = New-Object System.Text.UTF8Encoding
Then,
Font: Courier New
References:
https://markw.dev/unicode_powershell/
Displaying Unicode in Powershell
https://community.idera.com/database-tools/powershell/ask_the_experts/f/learn_powershell_from_don_jones-24/11793/add-hebrew-to-powershell
https://docs.oracle.com/javase/7/docs/api/java/nio/charset/Charset.html
I want to display Greek unicode characters but i get "?" instead on ouput
Encode String to UTF-8

Do Perl 6 programs have to compile to read embedded docs?

Perl 6 Plain-Old-Documentation (perhaps Fancy-New-Documentation) has some features that allow it to construct documentation for things it sees, and the documentation shows up in the $=pod variable at runtime.
However, I was surprised when I couldn't read the docs when I'd made an error in the program text. Here I've left out a statement separator between two statements:
use v6;
BEGIN { put "BEGIN" }
INIT { put "INIT" }
CHECK { put "CHECK" }
"foo" "bar";
DOC INIT { put "DOC INIT" }
DOC BEGIN { put "DOC BEGIN" }
DOC CHECK { put "DOC CHECK" }
=begin pod
=head1 This is a title
This is a bit of pod
=end pod
When I run it with the --doc switch, the program syntax matters (and BEGIN runs):
$ perl6 --doc doc.p6
BEGIN
===SORRY!=== Error while compiling ...
Two terms in a row
------> "foo"⏏ "bar";
expecting any of:
infix
infix stopper
statement end
statement modifier
statement modifier loop
When I fix it, I get some warnings (so, perl6 is compiling) and the compilation-time phasers run:
BEGIN
DOC BEGIN
DOC CHECK
CHECK
WARNINGS for /Users/brian/Desktop/doc.p6:
Useless use of constant string "bar" in sink context (line 9)
Useless use of constant string "foo" in sink context (line 9)
INIT
DOC INIT
This is a title
This is a bit of pod
We already know this is a bit dangerous in Perl 5. A perl -c and a BEGIN block can run code. See How to check if a Perl script doesn't have any compilation errors?. I don't think this is any more dangerous than something we already know, but now it's happening at a time when I'm not explicitly asking something to compile program statements.
I haven't delved into the details of Perl 6 pod and why this might be necessary outside of declarator blocks and .WHY (a cool feature), but it seems like this can lead to trouble. Is there perhaps an external program that might extract the Pod? Or a way to do without the declarators unless the program will run?
Yes, the whole file has to be parsed, which in turn requires running BEGIN and use statements and such.
The Perl 6 language is designed for one-pass parsing from top to bottom, so that at any given point the parser understands what it is parsing based on what it has parsed so far.
Consider code like the following:
say "
=begin pod
Not POD, just a string!
";
If you'd just grep the file for POD statements without parsing all of it, it would misinterpret this piece of code.
I.e. you can't parse only the POD parts without parsing the normal Perl 6 code parts, because without parsing it all from top to bottom you can't know which is which.
PS: In theory, the Perl 6 designers could have accommodated POD-only parsing, by making it illegal for normal Perl 6 code to contain lines that look like they start a POD block. In this scenario, the above code snippet would be a syntax error when the whole file is parsed, because starting a line inside a string literal with =begin pod would be disallowed, so the --pod switch could rely on all lines that begin with =begin foo actually starting a POD block.
Such a restriction probably wouldn't be a major burden for normal Perl 6 code (after all, who needs to write =begin pod at the start of a line in a multi-line string literal), but note that one of the reasons for the one-pass top-to-bottom parsing architecture is to facilitate language extensibility via slangs.
E.g. CPAN modules could add support for users writing a single subroutine (or other lexical scope) in another language or DSL. (Implementing such modules isn't actually possible yet without hacking into Rakudo internals via NQP, but once the macro/slang design is complete, it will be).
The burden for disallowing lines that look like they start a POD block, would then be passed on to all those slang parsers.
You could always submit a feature request for Larry and the other Perl 6 designers to consider this, though.

Issue with PerlObjCBridge and the OS X notification center

I am trying to connect a pre-existing perl script to the Mac OS 10.8/10.9 notification center.
The following code is what I have tried, and for some reason I can not get it to work:
#!/usr/bin/perl
use strict;
use warnings;
use Foundation;
my $notification = NSUserNotification->alloc()->init();
my $string = NSString->stringWithCString_("Test");
$notification->setTitle_($string);
$notification->setInformativeText_($string);
# the following line seems to be the issue. According to PerlObjCBridge->setTracing(1)
# the value returned from this is \0 and not an object, which is what it should be.
my $center = NSUserNotificationCenter->defaultUserNotificationCenter();
# this should be the line to display the notification, but it doesn't work because
# $center is \0 instead of an object
$center->deliverNotification_($notification);
Any assistance as to why NSUserNotificationCenter->defaultUserNotificationCenter() isn't returning an object?
Also, I realize that I could just call
system(osascript -e 'display notification "test" with title "test"');
But, well, I despise AppleScript. And my initial impression would be that there is more overhead calling the applescript engine then objc, which could be completely unfounded.
Thanks!
Here's how I solved this problem.
Find your "bundle directory" when running a perl script.
#!/usr/bin/perl
use Foundation;
my $bundle = NSBundle->mainBundle();
if ($bundle) {
print $bundle->bundlePath()->cString() . "\n";
print $bundle->bundleIdentifier()->cString() . "\n";
} else {
print "Bundle is not an object.";
}
Find an application to hijack the Info.plist (or Create your own Bundle)
If hijacking a bundle, create a soft link to the Contents directory of the bundle inside the bundle directory output from the script.
If creating your own Bundle, I don't have instructions. But I would presume creating a Contents directory and an Info.plist file with the required elements would work just as well.
I hijacked Microsoft Outlook's bundle, and it showed a notification as if it were from Outlook.

Best practice using NSLocalizedString

I'm (like all others) using NSLocalizedStringto localize my app.
Unfortunately, there are several "drawbacks" (not necessarily the fault of NSLocalizedString itself), including
No autocompletition for strings in Xcode. This makes working not only error-prone but also tiresome.
You might end up redefining a string simply because you didn't know an equivalent string already existed (i.e. "Please enter password" vs. "Enter password first")
Similarily to the autocompletion-issue, you need to "remember"/copypaste the comment strings, or else genstring will end up with multiple comments for one string
If you want to use genstring after you've already localized some strings, you have to be careful to not lose your old localizations.
Same strings are scattered througout your whole project. For example, you used NSLocalizedString(#"Abort", #"Cancel action") everywhere, and then Code Review asks you to rename the string to NSLocalizedString(#"Cancel", #"Cancel action") to make the code more consistent.
What I do (and after some searches on SO I figured many people do this) is to have a seperate strings.h file where I #define all the localize-code. For example
// In strings.h
#define NSLS_COMMON_CANCEL NSLocalizedString(#"Cancel", nil)
// Somewhere else
NSLog(#"%#", NSLS_COMMON_CANCEL);
This essentially provides code-completion, a single place to change variable names (so no need for genstring anymore), and an unique keyword to auto-refactor. However, this comes at the cost of ending up with a whole bunch of #define statements that are not inherently structured (i.e. like LocString.Common.Cancel or something like that).
So, while this works somewhat fine, I was wondering how you guys do it in your projects. Are there other approaches to simplify the use of NSLocalizedString? Is there maybe even a framework that encapsulates it?
NSLocalizedString has a few limitations, but it is so central to Cocoa that it's unreasonable to write custom code to handle localization, meaning you will have to use it. That said, a little tooling can help, here is how I proceed:
Updating the strings file
genstrings overwrites your string files, discarding all your previous translations.
I wrote update_strings.py to parse the old strings file, run genstrings and fill in the blanks so that you don't have to manually restore your existing translations.
The script tries to match the existing string files as closely as possible to avoid having too big a diff when updating them.
Naming your strings
If you use NSLocalizedString as advertised:
NSLocalizedString(#"Cancel or continue?", #"Cancel notice message when a download takes too long to proceed");
You may end up defining the same string in another part of your code, which may conflict as the same english term may have different meaning in different contexts (OK and Cancel come to mind).
That is why I always use a meaningless all-caps string with a module-specific prefix, and a very precise description:
NSLocalizedString(#"DOWNLOAD_CANCEL_OR_CONTINUE", #"Cancel notice window title when a download takes too long to proceed");
Using the same string in different places
If you use the same string multiple times, you can either use a macro as you did, or cache it as an instance variable in your view controller or your data source.
This way you won't have to repeat the description which may get stale and get inconsistent among instances of the same localization, which is always confusing.
As instance variables are symbols, you will be able to use auto-completion on these most common translations, and use "manual" strings for the specific ones, which would only occur once anyway.
I hope you'll be more productive with Cocoa localization with these tips!
As for autocompletition for strings in Xcode, you could try https://github.com/questbeat/Lin.
Agree with ndfred, but I would like to add this:
Second parameter can be use as ... default value!!
(NSLocalizedStringWithDefaultValue does not work properly with genstring, that's why I proposed this solution)
Here is my Custom implementation that use NSLocalizedString that use comment as default value:
1 . In your pre compiled header (.pch file) , redefine the 'NSLocalizedString' macro:
// cutom NSLocalizedString that use macro comment as default value
#import "LocalizationHandlerUtil.h"
#undef NSLocalizedString
#define NSLocalizedString(key,_comment) [[LocalizationHandlerUtil singleton] localizedString:key comment:_comment]
2. create a class to implement the localization handler
#import "LocalizationHandlerUtil.h"
#implementation LocalizationHandlerUtil
static LocalizationHandlerUtil * singleton = nil;
+ (LocalizationHandlerUtil *)singleton
{
return singleton;
}
__attribute__((constructor))
static void staticInit_singleton()
{
singleton = [[LocalizationHandlerUtil alloc] init];
}
- (NSString *)localizedString:(NSString *)key comment:(NSString *)comment
{
// default localized string loading
NSString * localizedString = [[NSBundle mainBundle] localizedStringForKey:key value:key table:nil];
// if (value == key) and comment is not nil -> returns comment
if([localizedString isEqualToString:key] && comment !=nil)
return comment;
return localizedString;
}
#end
3. Use it!
Make sure you add a Run script in your App Build Phases so you Localizable.strings file will be updated at each build, i.e., new localized string will be added in your Localized.strings file:
My build phase Script is a shell script:
Shell: /bin/sh
Shell script content: find . -name \*.m | xargs genstrings -o MyClassesFolder
So when you add this new line in your code:
self.title = NSLocalizedString(#"view_settings_title", #"Settings");
Then perform a build, your ./Localizable.scripts file will contain this new line:
/* Settings */
"view_settings_title" = "view_settings_title";
And since key == value for 'view_settings_title', the custom LocalizedStringHandler will returns the comment, i.e. 'Settings"
Voilà :-)
In Swift I'm using the following, e.g. for button "Yes" in this case:
NSLocalizedString("btn_yes", value: "Yes", comment: "Yes button")
Note usage of the value: for the default text value. The first parameter serves as the translation ID. The advantage of using the value: parameter is that the default text can be changed later but the translation ID remains the same. The Localizable.strings file will contain "btn_yes" = "Yes";
If the value: parameter was not used then the first parameter would be used for both: for the translation ID and also for the default text value. The Localizable.strings file would contain "Yes" = "Yes";. This kind of managing localization files seems to be strange. Especially if the translated text is long then the ID is long as well. Whenever any character of the default text value is changed, then the translation ID gets changed as well. This leads to issues when external translation systems are used. Changing of the translation ID is understood as adding new translation text, which may not be always desired.
I wrote a script to help maintaining Localizable.strings in multiple languages. While it doesn't help in autocompletion it helps to merge .strings files using command:
merge_strings.rb ja.lproj/Localizable.strings en.lproj/Localizable.strings
For more info see:
https://github.com/hiroshi/merge_strings
Some of you find it useful I hope.
If anyone looking for a Swift solution. You may want to check out my solution I put together here: SwiftyLocalization
With few steps to setup, you will have a very flexible localization in Google Spreadsheet (comment, custom color, highlight, font, multiple sheets, and more).
In short, steps are: Google Spreadsheet --> CSV files --> Localizable.strings
Moreover, it also generates Localizables.swift, a struct that acts like interfaces to a key retrieval & decoding for you (You have to manually specify a way to decode String from key though).
Why is this great?
You no longer need have a key as a plain string all over the places.
Wrong keys are detected at compile time.
Xcode can do autocomplete.
While there're tools that can autocomplete your localizable key. Reference to a real variable will ensure that it's always a valid key, else it won't compile.
// It's defined as computed static var, so it's up-to-date every time you call.
// You can also have your custom retrieval method there.
button.setTitle(Localizables.login.button_title_login, forState: .Normal)
The project uses Google App Script to convert Sheets --> CSV , and Python script to convert CSV files --> Localizable.strings You can have a quick look at this example sheet to know what's possible.
with iOS 7 & Xcode 5, you should avoid using the 'Localization.strings' method, and use the new 'base localisation' method. There are some tutorials around if you google for 'base localization'
Apple doc : Base localization
#define PBLocalizedString(key, val) \
[[NSBundle mainBundle] localizedStringForKey:(key) value:(val) table:nil]
Myself, I'm often carried away with coding, forgetting to put the entries into .strings files. Thus I have helper scripts to find what do I owe to put back into .strings files and translate.
As I use my own macro over NSLocalizedString, please review and update the script before using as I assumed for simplicity that nil is used as a second param to NSLocalizedString. The part you'd want to change is
NSLocalizedString\(#(".*?")\s*,\s*nil\)
Just replace it with something that matches your macro and NSLocalizedString usage.
Here comes the script, you only need Part 3 indeed. The rest is to see easier where it all comes from:
// Part 1. Get keys from one of the Localizable.strings
perl -ne 'print "$1\n" if /^\s*(".+")\s*=/' myapp/fr.lproj/Localizable.strings
// Part 2. Get keys from the source code
grep -n -h -Eo -r 'NSLocalizedString\(#(".*?")\s*,\s*nil\)' ./ | perl -ne 'print "$1\n" if /NSLocalizedString\(#(".+")\s*,\s*nil\)/'
// Part 3. Get Part 1 and 2 together.
comm -2 -3 <(grep -n -h -Eo -r 'NSLocalizedString\(#(".*?")\s*,\s*nil\)' ./ | perl -ne 'print "$1\n" if /NSLocalizedString\(#(".+")\s*,\s*nil\)/' | sort | uniq) <(perl -ne 'print "$1\n" if /^\s*(".+")\s*=/' myapp/fr.lproj/Localizable.strings | sort) | uniq >> fr-localization-delta.txt
The output file contains keys that were found in the code, but not in the Localizable.strings file. Here is a sample:
"MPH"
"Map Direction"
"Max duration of a detailed recording, hours"
"Moving ..."
"My Track"
"New Trip"
Certainly can be polished more, but thought I'd share.