Using Str
The Str
class is an addition to, or replacement for, java.lang.String, with functionality inspired
by Ruby. It is evolving quickly, and should be stable at the 4.0 release of ijdk.
In the following examples, the first section is an implementation with the JDK (as of Java 1.8 or so), and the second is the equivalent with ijdk.
of
The factory method of
returns an instance of Str
. This functionality is not yet optimized for
memory usage by pooling strings, which may occur in a future release.
String x = "abc";
// x : abc
Str y = Str.of("abc");
// y : abc
join
A common way to create a string is by joining a collection with a separator.
String[] ary = new String[] { "abc", "def", "ghi" };
StringBuilder sb = new StringBuilder();
boolean isFirst = true;
for (String str : ary) {
if (isFirst) {
isFirst = false;
}
else {
sb.append(", ");
}
sb.append(str);
}
String x = sb.toString();
// x : abc, def, ghi
Str y = Str.join(ary, ", ");
// y : abc, def, ghi
repeat
To repeat a string:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 3; ++i) {
sb.append("ho");
}
String x = sb.toString();
// x : hohoho
Str y = new Str("ho", 3);
// y : hohoho
And a character:
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 8; ++i) {
sb.append('m');
}
String x = sb.toString();
// x : mmmmmmmm
Str y = new Str('m', 8);
// y : mmmmmmmm
split
To split a string into a list, using a literal. (String.split(String arg)
treats the argument as a
regular expression.)
int max = 3;
Pattern pat = Pattern.compile(".", Pattern.LITERAL);
String[] ary = pat.split("abc.def.ghi.jkl.mno", max);
List<String> x = new ArrayList<>(Arrays.asList(ary));
// x : [abc, def, ghi.jkl.mno]
List<String> y = new Str("abc.def.ghi.jkl.mno").split(".", max);
// y : [abc, def, ghi.jkl.mno]
toList
A common behavior is to convert a set of “words”, separated by commas and whitespace, into a list.
String s = "first, second \nthird";
List<String> x = new ArrayList<String>();
StringTokenizer st = new StringTokenizer(s, " \t\n\r\f,");
while (st.hasMoreTokens()) {
String tk = st.nextToken();
x.add(tk);
}
// x : [first, second, third]
List<String> y = new Str(s).toList();
// y : [first, second, third]
pad
The pad methods align a string to the left or right, adding extra characters to fit.
Padding to the right:
StringBuilder sb = new StringBuilder("abc");
for (int idx = 3; idx < 8; ++idx) {
sb.append('*');
}
String x = sb.toString();
// x : abc*****
String y = new Str("abc").pad('*', 8).toString();
// y : abc*****
Padding to the left:
StringBuilder sb = new StringBuilder("abc");
for (int idx = 3; idx < 8; ++idx) {
sb.insert(0, '*');
}
String x = sb.toString();
// x : *****abc
String y = new Str("abc").padLeft('*', 8).toString();
// y : *****abc
repeat
An existing Str
can be repeated to create a new one.
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 3; ++i) {
sb.append("ho");
}
String x = sb.toString();
// x : hohoho
Str y = new Str("ho").repeat(3);
// y : hohoho
left, right
The left and right methods return a substring of the given number of characters, or a shorter string if of insufficient length.
String s = "abcdef";
int n = 4;
String x = s.substring(0, n);
// x : abcd
Str y = new Str(s).left(n);
// y : abcd
String s = "abcdef";
int n = 4;
String x = s.substring(s.length() - n, s.length());
// x : cdef
Str y = new Str(s).right(n);
// y : cdef
charAt
The method charAt
handles negative values, returning a character from the end of the string. This
method returns null when the index is not valid, not the (annoying)
ArrayIndexOutOfBoundsException
.
The method get
is the same as charAt
, matching the method name and behavior of Array
.
String s = "abcdef";
int n = 2;
Character x = n < s.length() ? s.charAt(n) : null;
// x : c
Character y = new Str(s).charAt(n);
// or y = new Str(s).get(n);
// y : c
String s = "abcdef";
int n = -3;
Character x = Math.abs(n) < s.length() ? s.charAt(s.length() + n) : null;
// x : d
Character y = new Str(s).charAt(n);
// or y = new Str(s).get(n);
// y : d
substring, get
The substring
method, with an equivalent get
method taking two arguments, returns an Str
, with
the indices being positive or negative, as with charAt
.
String s = "abcdef";
int i = 2;
int j = 5;
String x = s.substring(i, j + 1);
// x : cdef
String y = new Str(s).substring(i, j);
// or get(i, j)
// y : cdef
String s = "abcdef";
int i = 1;
int j = -3;
String x = s.substring(i, s.length() + j + 1);
// x : bcd
String y = new Str(s).substring(i, j);
// or get(i, j)
// y : bcd
substringBefore, substringAfter
These methods return an Str
instance for the section of the string before, or after, a matching
character.
String s = "abcdef";
int idx = s.indexOf('d');
String x = s.substring(0, idx);
// x : abc
String y = new Str(s).substringBefore('d');
// y : abc
String s = "abcdef";
int idx = s.indexOf('d');
String x = s.substring(s.length() - idx + 1, s.length());
// x : ef
String y = new Str(s).substringAfter('d');
// y : ef
startsWith
The startsWith
methods take a character or a string, and handle issues with the string length
being insufficent.
A character:
String s = "abcdef";
boolean x = s.length() >= 1 && s.charAt(0) == 'a';
// x : true
boolean y = new Str(s).startsWith('a');
// y : true
A string:
String s = "abcdef";
boolean x = s.length() >= 3 && s.substring(0, 3).equals("abc");
// x : true
boolean y = new Str(s).startsWith("abc");
// y : true
The methods also take the ignorecase option:
String s = "AbCdEf";
boolean x = s.length() >= 3 && s.substring(0, 3).equalsIgnoreCase("abc");
// x : true
boolean y = new Str(s).startsWith("abc", EnumSet.of(Str.Option.IGNORE_CASE));
// y : true
endsWith
As with startsWith
, the endsWith
methods check the end of the string.
String s = "abcdef";
boolean x = s.length() >= 1 && s.charAt(s.length() - 1) == 'a';
// x : false
boolean y = new Str(s).endsWith('a');
// y : false
String s = "abcdef";
boolean x = s.length() >= 3 && s.substring(3, s.length()).equals("def");
// x : true
boolean y = new Str(s).endsWith("def");
// y : true
chomp
The chomp
method removed one occurance of a new-line character, of Unix and DOS varieties.
String s = "abcdef\n";
String x = s.length() >= 1 && "\r\n".indexOf(s.charAt(s.length() - 1)) >= 0 ? s.substring(0, s.length() - 1) : s;
// x : abcdef
Str y = new Str(s).chomp();
// y : abcdef
chompAll
This method removes all of the new-line characters from the end of the string.
String s = "abcdef\n";
String x = s;
while (x.length() >= 1 && "\r\n".indexOf(x.charAt(x.length() - 1)) >= 0) {
x = x.substring(0, x.length() - 1);
}
// x : abcdef
Str y = new Str(s).chompAll();
// y : abcdef
contains
To check whether an Str
instance contains a substring. No, not much difference here.
String s = "abcdef";
boolean x = s.contains(String.valueOf('d'));
// x : true
boolean y = new Str(s).contains('d');
// y : true
indexOf
Not a major difference here either, meaning that Str
is consistent with String
when possible.
String s = "abcdef";
int x = s.indexOf('d');
// x : 3
int y = new Str(s).indexOf('d');
// y : 3
eq, eqi
These methods are the eq-uivalent (clever) of equals
and equalsIgnoreCase
.
String s = "abcdef";
String t = "abcdef";
boolean x = s.equals(t);
// x : true
boolean y = new Str(s).eq(t);
// y : true
String s = "abcdef";
String t = "abcdef";
boolean x = s.equalsIgnoreCase(t);
// x : true
boolean y = new Str(s).eq(t);
// y : true
snip
To cut a string down to a length, then append a hyphen:
String s = "abcdef";
String x = s.substring(0, 3) + '-';
// x : abc-
Str y = new Str(s).snip(4);
// y : abc-
isEmpty
To the isEmpty
method, Str
adds handling of ignoring whitespace.
String s = "abcdef";
boolean x = s.trim().isEmpty();
// x : false
boolean y = new Str(s).isEmpty(Str.Option.IGNORE_WHITESPACE);
// y : false
quote
To add quotation marks:
String s = "abcdef";
String x = '"' + s + '"';
// x : "abcdef"
Str y = new Str(s).quote();
// y : "abcdef"
unquote
And to remove quotation marks, if they exist:
String s = "\"abcdef\"";
String x = s.charAt(0) == '"' && s.charAt(s.length() - 1) == '"' ? s.substring(1, s.length() - 1) : s;
// x : abcdef
Str y = new Str(s).unquote();
// y : abcdef
compareTo, alphanumeric
In comparison to the String#compareTo
method, Str
adds support for comparing alphanumerically,
such that xyz3 precedes xyz12
String s = "abc12";
String t = "abc3";
int x = Integer.valueOf(s.substring(3, s.length())).compareTo(Integer.valueOf(t.substring(3, t.length())));
// x : 1
int y = new Str(s).compareTo(new Str(t), EnumSet.of(Str.Option.ALPHANUMERIC));
// y : 1
compareTo, ignorecase
And Str
can also ignore case for comparisons:
String s = "Abc";
String t = "def";
int x = s.toUpperCase().compareTo(t.toUpperCase());
// x : -3
int y = new Str(s).compareTo(new Str(t), EnumSet.of(Str.Option.IGNORE_CASE));
// y : -3
lt, lte, gt, gte
Because x > y
is unequivocally (clever, again) clearer than x.compareTo(y) > 0
, Str
offers the
same methods as Obj
, for less than, less than or equal to, greater than, and greater than or equal
to.
String s = "Abc";
String t = "def";
boolean x = s.compareTo(t) < 0;
// x : true
boolean y = new Str(s).lt(new Str(t));
// y : true
String s = "Abc";
String t = "def";
boolean x = s.compareTo(t) <= 0;
// x : true
boolean y = new Str(s).lte(new Str(t));
// y : true
String s = "Abc";
String t = "def";
boolean x = s.compareTo(t) > 0;
// x : false
boolean y = new Str(s).gt(new Str(t));
// y : false
String s = "Abc";
String t = "def";
boolean x = s.compareTo(t) >= 0;
// x : false
boolean y = new Str(s).gte(new Str(t));
// y : false
firstChar
As with Array
, Str
has the first
and last
methods, returning a character or string of a
given length.
String s = "abcdef";
char x = s.charAt(0);
// x : a
char y = new Str(s).first();
// y : a
String s = "abcdef";
String x = s.substring(0, 3);
// x : abc
Str y = new Str(s).first(3);
// y : abc
String s = "abcdef";
char x = s.charAt(s.length() - 1);
// x : f
char y = new Str(s).last();
// y : f
String s = "abcdef";
String x = s.substring(s.length() - 3, s.length());
// x : def
Str y = new Str(s).last(3);
// y : def
replaceAll
In comparison to String#replaceAll
, which operates on a regular expression, Str#replaceAll
handles the argument as a literal.
String s = "ab.ac.ad";
String t = ".a";
String u = ".x";
String x = "";
int prev = 0;
int idx = s.indexOf(t, prev);
while (idx >= 0) {
x += s.substring(prev, idx);
x += u;
prev = idx + t.length();
idx = s.indexOf(t, prev);
}
x += s.substring(prev, s.length());
// x : ab.xc.xd
Str y = new Str(s).replaceAll(t, u);
// y : ab.xc.xd
replaceAll, ignore case
And for more excitement, an option can ignore case for the replacements. Voila:
String s = "ab.Ac.ad";
String t = ".a";
String u = ".x";
String x = "";
String su = s.toUpperCase();
String tu = t.toUpperCase();
int prev = 0;
int idx = su.indexOf(tu, prev);
while (idx >= 0) {
x += s.substring(prev, idx);
x += u;
prev = idx + t.length();
idx = su.indexOf(tu, prev);
}
x += s.substring(prev, s.length());
// x : ab.xc.xd
Str y = new Str(s).replaceAll(t, u, EnumSet.of(Str.Option.IGNORE_CASE));
// y : ab.xc.xd
trimLeft, trimRight
Finally, these methods remove whitespace at the left or right of the string.
String s = " abc def ";
int idx = 0;
while (idx < s.length() && Character.isWhitespace(s.charAt(idx))) {
++idx;
}
String x = idx < s.length() ? s.substring(idx, s.length()) : "";
// x : "abc def "
Str y = new Str(s).trimLeft();
// y : "abc def "
String s = " abc def ";
int idx = s.length() - 1;
while (idx >= 0 && Character.isWhitespace(s.charAt(idx))) {
--idx;
}
String x = idx >= 0 ? s.substring(0, idx + 1) : "";
// x : " abc def"
Str y = new Str(s).trimRight();
// y : " abc def"
Plans
For consistency, Str
will most likely be refined to support the Array
functionality, as an
Array<Character>
.