views:

1223

answers:

10

How can I trim characters in Java?
e.g.

String j = “\joe\jill\”.Trim(new char[] {“\”});

j should be

"joe\jill"

String j = “jack\joe\jill\”.Trim("jack");

j should be

"\joe\jill\"

etc

A: 

EDIT: Amended by answer to replace just the first and last '\' character.

System.err.println("\\joe\\jill\\".replaceAll("^\\\\|\\\\$", ""));
Adamski
this will remove any \\ in the middle
Ahmed Kotb
I've amended this so it now works.
Adamski
A: 

I don't think there is any built in function to trim based on a passed in string. Here is a small example of how to do this. This is not likely the most efficient solution, but it is probably fast enough for most situations, evaluate and adapt to your needs. I recommend testing performance and optimizing as needed for any code snippet that will be used regularly. Below, I've included some timing information as an example.

public String trim( String stringToTrim, String stringToRemove )
{
    String answer = stringToTrim;

    while( answer.startsWith( stringToRemove ) )
    {
        answer = answer.substring( stringToRemove.length() );
    }

    while( answer.endsWith( stringToRemove ) )
    {
        answer = answer.substring( 0, answer.length() - stringToRemove.length() );
    }

    return answer;
}

This answer assumes that the characters to be trimmed are a string. For example, passing in "abc" will trim out "abc" but not "bbc" or "cba", etc.

Some performance times for running each of the following 10 million times.

" mile ".trim(); runs in 248 ms included as a reference implementation for performance comparisons.

trim( "smiles", "s" ); runs in 547 ms - approximately 2 times as long as java's String.trim() method.

"smiles".replaceAll("s$|^s",""); runs in 12,306 ms - approximately 48 times as long as java's String.trim() method.

And using a compiled regex pattern Pattern pattern = Pattern.compile("s$|^s"); pattern.matcher("smiles").replaceAll(""); runs in 7,804 ms - approximately 31 times as long as java's String.trim() method.

Alex B
"answer.length - trimChar.length - 1" actually
Brett Widmeier
Not really optimized. I wouldn't use this.
Pindatjuh
why not use regex?
Paulo Guedes
@BrettWidmeier I think I've got this write. `trim( "smiles", "les" )` yields `smi` and `trim( "smiles", "s" )` yields `mile`.
Alex B
I can't imagine a more *inefficient* way to solve this problem.
Software Monkey
@SoftwareMonkey I agree that it is not the most efficient solution, but context is helpful. On my machine, 10 million runs of `trim( "smiles", "s" );` take 547ms and 10 million runs of `" mile ".trim()` take 248 ms. My solution is 1/2 as fast as `String.trim()` and still 10 Million runs complete in about half a second.
Alex B
@SoftwareMonkey The regex numbers are that `"smiles".replaceAll("s$|^s","")` run 10 million times takes 12,306 ms (~48 times slower than `String.trim()` and ~24 times slower than my implementation)
Alex B
@Alex: I will take back my down-vote if you add a note that it should be optimized for the common-case of trimming a single character; I will up-vote it if you amend the code to so optimize (given that what is being asked for a is a re-usable library method).
Software Monkey
(and by "a single character" I mean any number of a single character from either end... IOW, when trimChar is length 1). Also trimChar is a bad name for that param... should likely be trimString or trimValue.
Software Monkey
@SoftwareMonkey: I appreciate the feedback. I'll add a comment about optimizing for the problem domain, but I didn't see any context for how this would be used in the original question or how many characters would be stripped (one example had a single character, and one example had a string). Good call on the name of trimChar.
Alex B
Your comparison to regex is not really fair or realistic; since the form used will compile the regex every time. If one were doing this frequently, one would create the regex and reuse the matcher, as the JavaDoc suggests: `An invocation of this method of the form str.replaceAll(regex, repl) yields exactly the same result as the expression Pattern.compile(regex).matcher(str).replaceAll(repl)`.
Software Monkey
@SoftwareMonkey: Great point! I took the regex example from @PauloGuedes code. I added numbers for the compiled regex version to my timing section.
Alex B
A: 

it appears that there is no ready to use java api that makes that but you can write a method to do that for you. this link might be usefull

Ahmed Kotb
Regex is the ready-to-use api :-)
Paulo Guedes
sure it is :Di meant a trim function that takes string as the question says
Ahmed Kotb
+4  A: 

This does what you want:

public static void main (String[] args) {
    String a = "\\joe\\jill\\";
    String b = a.replaceAll("\\\\$", "").replaceAll("^\\\\", "");
    System.out.println(b);
}

The $ is used to remove the sequence in the end of string. The ^ is used to remove in the beggining.

As an alternative, you can use the syntax:

String b = a.replaceAll("\\\\$|^\\\\", "");

The | means "or".

In case you want to trim other chars, just adapt the regex:

String b = a.replaceAll("y$|^x", ""); // will remove all the y from the end and x from the beggining
Paulo Guedes
A: 

You could use removeStart and removeEnd from Apache Commons Lang StringUtils

Valentin Rocher
A: 

Hand made for the first option:

public class Rep {
    public static void main( String [] args ) {
       System.out.println( trimChar( '\\' , "\\\\\\joe\\jill\\\\\\\\" )  ) ;
       System.out.println( trimChar( '\\' , "joe\\jill" )  ) ;
    }
    private static String trimChar( char toTrim, String inString ) { 
        int from = 0;
        int to = inString.length();

        for( int i = 0 ; i < inString.length() ; i++ ) {
            if( inString.charAt( i ) != toTrim) {
                from = i;
                break;
            }
        }
        for( int i = inString.length()-1 ; i >= 0 ; i-- ){ 
            if( inString.charAt( i ) != toTrim ){
                to = i;
                break;
            }
        }
        return inString.substring( from , to );
    }
}

Prints

joe\jil

joe\jil

OscarRyz
umh .. a comment please on the downvote?
OscarRyz
A: 

I would actually write my own little function that does the trick by using plain old char access:

public static String trimBackslash( String str )
{
    int len, left, right;
    return str == null || ( len = str.length() ) == 0 
                           || ( ( left = str.charAt( 0 ) == '\\' ? 1 : 0 ) |
           ( right = len > left && str.charAt( len - 1 ) == '\\' ? 1 : 0 ) ) == 0
        ? str : str.substring( left, len - right );
}

This behaves similar to what String.trim() does, only that it works with '\' instead of space.

Here is one alternative that works and actually uses trim(). ;) Althogh it's not very efficient it will probably beat all regexp based approaches performance wise.

String j = “\joe\jill\”;
j = j.replace( '\\', '\f' ).trim().replace( '\f', '\\' );
x4u
+9  A: 

Apache Commons has a great StringUtils class. In StringUtils there is a strip(String, String) method that will do what you want.

I highly recommend using Apache Commons anyway, especially the Collections and Lang libraries.

Colin Gislason
+1  A: 

For now, I'd second Colins' Apache commons-lang answer, but once Google's guava-libraries is released, the CharMatcher class will do what you want quite nicely:

String j = CharMatcher.is('\\').trimFrom("\\joe\\jill\\"); 
// j is now joe\jill

CharMatcher has a very simple + powerful set of APIs as well as some predefined constants which make manipulation very easy:

CharMatcher.is(':').countIn("a:b:c"); // returns 2
CharMatcher.isNot(':').countIn("a:b:c"); // returns 3
CharMatcher.inRange('a', 'b').countIn("a:b:c"); // returns 2
CharMatcher.DIGIT.retainFrom("a12b34"); // returns "1234"
CharMatcher.ASCII.negate().removeFrom("a®¶b"); // returns "ab";

etc. Very nice stuff.

Cowan
A: 

Here's how I would do it. Note that the corner case of passing an empty string to trim is handled (other answers would go into an infinite loop). And it optimizes the single character case since there is a useful alternate logic which is more efficient than creating multiple substrings for each character removed:

Note that this will be inefficient in the case where there a many repetitions of the trim string at either or both of the beginning and end when the trim string is not a single character - this is likely to be a very rare use case; if it is a common case for you, you might want to consider an alternative implementation.

public String stripString(String src, String trm) {
    String                              ret;

    if(trm.length()==0) {                                                       // catch corner case; all strings start and end with ""
        ret=src;
        }
    else if(trm.length()==1) {                                                  // optimize for common use - trimming a single character
        int  str=0,end=(src.length()-1);
        char chr=trm.charAt(0);
        while(str<=end && src.charAt(str)==chr) { str++; }
        while(str<=end && src.charAt(end)==chr) { end--; }
        if(str<=end) { ret=src.substring(str,(end+1)); }
        else         { ret="";                         }
        }
    else {                                                                      // handle repeated removal of a specific character sequence
        int trmlen=trm.length();
        ret=src;
        while(ret.startsWith(trm)) { ret=ret.substring(trmlen);                }
        while(ret.endsWith  (trm)) { ret=ret.substring(0,ret.length()-trmlen); }
        }

    return ret;
    }

EDIT

I got to thinking more about how to do this both flexibly and efficiently, and here's what I came up with on a subsequent pass on the problem. It's about as efficient as it reasonably can be now, I think. (Admittedly, I may be working this too hard now):

/** Trim all occurrences of the string <code>rmvval</code> from the left and right of <code>src</code>.  Note that <code>rmvval</code> constitutes an entire string which must match using <code>String.startsWith</code> and <code>String.endsWith</code>. */
static public String trim(String src, String rmvval) {
    return trim(src,rmvval,rmvval,true);
    }

/** Trim all occurrences of the string <code>lftval</code> from the left and <code>rgtval</code> from the right of <code>src</code>.  Note that the values to remove constitute strings which must match using <code>String.startsWith</code> and <code>String.endsWith</code>. */
static public String trim(String src, String lftval, String rgtval, boolean igncas) {
    int                                 str=0,end=src.length();

    if(lftval.length()==1) {                                                    // optimize for common use - trimming a single character from left
        char chr=lftval.charAt(0);
        while(str<end && src.charAt(str)==chr) { str++; }
        }
    else if(lftval.length()>1) {                                                // handle repeated removal of a specific character sequence from left
        int vallen=lftval.length(),newstr;
        while((newstr=(str+vallen))<=end && src.regionMatches(igncas,str,lftval,0,vallen)) { str=newstr; }
        }

    if(rgtval.length()==1) {                                                    // optimize for common use - trimming a single character from right
        char chr=rgtval.charAt(0);
        while(str<end && src.charAt(end-1)==chr) { end--; }
        }
    else if(rgtval.length()>1) {                                                // handle repeated removal of a specific character sequence from right
        int vallen=rgtval.length(),newend;
        while(str<=(newend=(end-vallen)) && src.regionMatches(igncas,newend,rgtval,0,vallen)) { end=newend; }
        }

    if(str!=0 || end!=src.length()) {
        if(str<=end) { src=src.substring(str,end); }                            // str is inclusive, end is exclusive
        else         { src="";                     }
        }

    return src;
    }
Software Monkey