Skip to content

Commit

Permalink
fix #1. camel case from/to underscore.
Browse files Browse the repository at this point in the history
  • Loading branch information
huandu committed Jan 6, 2015
1 parent 9b5ed4b commit 55ec751
Show file tree
Hide file tree
Showing 5 changed files with 210 additions and 3 deletions.
17 changes: 14 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -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.
141 changes: 141 additions & 0 deletions convert.go
Original file line number Diff line number Diff line change
@@ -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()
}
30 changes: 30 additions & 0 deletions convert_test.go
Original file line number Diff line number Diff line change
@@ -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",
})
}

7 changes: 7 additions & 0 deletions doc.go
Original file line number Diff line number Diff line change
@@ -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

18 changes: 18 additions & 0 deletions test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
}

0 comments on commit 55ec751

Please sign in to comment.