From 55ec75123694d4480e6c06b00f09d84ff3637aaf Mon Sep 17 00:00:00 2001 From: Huan Du Date: Tue, 6 Jan 2015 16:11:27 +0800 Subject: [PATCH] fix #1. camel case from/to underscore. --- README.md | 17 ++++-- convert.go | 141 ++++++++++++++++++++++++++++++++++++++++++++++++ convert_test.go | 30 +++++++++++ doc.go | 7 +++ test.go | 18 +++++++ 5 files changed, 210 insertions(+), 3 deletions(-) create mode 100644 convert.go create mode 100644 convert_test.go create mode 100644 doc.go create mode 100644 test.go diff --git a/README.md b/README.md index 7136efe..fe58bf7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,15 @@ -xstrings -======== +# xstrings # -More string algorithm for Go. +Go [strings](http://golang.org/pkg/strings) package only has a few string algorithms. There are lots of other useful algorithms missing. + +`xstrings` is to fill the gap. It will include as many useful string algorithm as possible. Please [create new issue](https://github.com/huandu/xstrings/issues) if you find one string algorithm is useful but not in either `strings` and `xstrings`. + +## Install ## + +Use `go get` to install this library. + + go get github.com/huandu/xstrings + +## Document ## + +See [GoDoc](https://godoc.org/github.com/huandu/xstrings) for details. diff --git a/convert.go b/convert.go new file mode 100644 index 0000000..e12cf5a --- /dev/null +++ b/convert.go @@ -0,0 +1,141 @@ +// Copyright 2015 Huan Du. All rights reserved. +// Licensed under the MIT license that can be found in the LICENSE file. + +package xstrings + +import ( + "bytes" + "unicode" + "unicode/utf8" +) + +// ToCamelCase can convert all lower case characters behind underscores +// to upper case character. +// Underscore character will be removed in result except following cases. +// * More than 1 underscore, e.g. "a__b" => "A_B" +// * At the beginning of string, e.g. "_a" => "_A" +// * At the end of string, e.g. "ab_" => "Ab_" +func ToCamelCase(str string) string { + if len(str) == 0 { + return "" + } + + buf := &bytes.Buffer{} + var r0, r1 rune + var size int + + // leading '_' will appear in output. + for len(str) > 0 { + r0, size = utf8.DecodeRuneInString(str) + str = str[size:] + + if r0 != '_' { + break + } + + buf.WriteRune(r0) + } + + if len(str) == 0 { + return buf.String() + } + + buf.WriteRune(unicode.ToUpper(r0)) + r0, size = utf8.DecodeRuneInString(str) + str = str[size:] + + for len(str) > 0 { + r1 = r0 + r0, size = utf8.DecodeRuneInString(str) + str = str[size:] + + if r1 == '_' && r0 != '_' { + r0 = unicode.ToUpper(r0) + } else { + buf.WriteRune(r1) + } + } + + buf.WriteRune(r0) + return buf.String() +} + +// ToUnderscore can convert all upper case characters in a string to +// underscore format. +// +// Some samples. +// "FirstName" => "first_name" +// "HTTPServer" => "http_server" +// "NoHTTPS" => "no_https" +func ToUnderscore(str string) string { + if len(str) == 0 { + return "" + } + + buf := &bytes.Buffer{} + var r0, r1 rune + var size int + + for len(str) > 0 { + r0, size = utf8.DecodeRuneInString(str) + str = str[size:] + + switch { + case r0 == utf8.RuneError: + buf.WriteByte(byte(str[0])) + + case unicode.IsUpper(r0): + if buf.Len() > 0 { + buf.WriteRune('_') + } + + buf.WriteRune(unicode.ToLower(r0)) + + if len(str) == 0 { + break + } + + r0, size = utf8.DecodeRuneInString(str) + str = str[size:] + + if !unicode.IsUpper(r0) { + buf.WriteRune(r0) + break + } + + // find next non-upper-case character and insert `_` properly. + // it's designed to convert `HTTPServer` to `http_server`. + // if there are more than 2 adjacent upper case characters in a word, + // treat them as an abbreviation plus a normal word. + for len(str) > 0 { + r1 = r0 + r0, size = utf8.DecodeRuneInString(str) + str = str[size:] + + if r0 == utf8.RuneError { + buf.WriteRune(unicode.ToLower(r1)) + buf.WriteByte(byte(str[0])) + break + } + + if !unicode.IsUpper(r0) { + buf.WriteRune('_') + buf.WriteRune(unicode.ToLower(r1)) + buf.WriteRune(r0) + break + } + + buf.WriteRune(unicode.ToLower(r1)) + } + + if len(str) == 0 { + buf.WriteRune(unicode.ToLower(r0)) + } + + default: + buf.WriteRune(r0) + } + } + + return buf.String() +} diff --git a/convert_test.go b/convert_test.go new file mode 100644 index 0000000..b6e89cd --- /dev/null +++ b/convert_test.go @@ -0,0 +1,30 @@ +// Copyright 2015 Huan Du. All rights reserved. +// Licensed under the MIT license that can be found in the LICENSE file. + +package xstrings + +import ( + "testing" +) + +func TestToUnderscore(t *testing.T) { + runTestCases(t, ToUnderscore, map[string]string{ + "HTTPServer": "http_server", + "_camelCase": "_camel_case", + "NoHTTPS": "no_https", + "Wi_thF": "wi_th_f", + "_AnotherTES_TCaseP": "__another_te_s__t_case_p", + "ALL": "all", + }) +} + +func TestToCamelCase(t *testing.T) { + runTestCases(t, ToCamelCase, map[string]string{ + "http_server": "HttpServer", + "_camel_case": "_CamelCase", + "no_https": "NoHttps", + "_complex__case_": "_Complex_Case_", + "all": "All", + }) +} + diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..f026d24 --- /dev/null +++ b/doc.go @@ -0,0 +1,7 @@ +// Copyright 2015 Huan Du. All rights reserved. +// Licensed under the MIT license that can be found in the LICENSE file. + +// Package xstrings is to provide string algorithms which are useful but not included in `strings` package. +// All algorithms working directly on string can be added to this package. +package xstrings + diff --git a/test.go b/test.go new file mode 100644 index 0000000..2ac9df0 --- /dev/null +++ b/test.go @@ -0,0 +1,18 @@ +// Copyright 2015 Huan Du. All rights reserved. +// Licensed under the MIT license that can be found in the LICENSE file. + +package xstrings + +import ( + "testing" +) + +func runTestCases(t *testing.T, converter func(string)string, cases map[string]string) { + for k, v := range cases { + s := converter(k) + + if s != v { + t.Fatalf("case fails. [expected:%v] [actual:%v]", v, s) + } + } +} \ No newline at end of file