4

Do you know a java method to expand the chars of a text into spaces?

My text:

1. <tab>  firstpoint  <tab> page  1
10. <tab> secondpoint <tab> page 10

If I directly replace the tab by, let say, 4 spaces, I will have

1.    firstpoint    page1
10.    secondpoint    page2

Instead of it, I need a method to replace each tab by the number of spaces it really corresponds to (as the command :retab of vim does). Any solution?

Olivier Faucheux
  • 2,520
  • 3
  • 29
  • 37

5 Answers5

5

I think there might be an issue with the retab() function above ignoring an initial tab. I made my own; hereby placed into public domain.

public static String expandTabs(String s, int tabSize) {
    if (s == null) return null;
    StringBuilder buf = new StringBuilder();
    int col = 0;
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        switch (c) {
            case '\n' :
                col = 0;
                buf.append(c);
                break;
            case '\t' :
                buf.append(spaces(tabSize - col % tabSize));
                col += tabSize - col % tabSize;
                break;
            default :
                col++;
                buf.append(c);
                break;
        }
    }
    return buf.toString();
}

public static String spaces(int n) {
    StringBuilder buf = new StringBuilder();
    for (int sp = 0; sp < n; sp++) buf.append(" ");
    return buf.toString();
}
Eldar Abusalimov
  • 24,387
  • 4
  • 67
  • 71
Terence Parr
  • 5,912
  • 26
  • 32
  • 1
    I think it's missing `col += tabSize - col % tabSize;` in the `'\t'` case. – Terence Parr Apr 15 '16 at 19:36
  • Great answer - a few suggestions: * Split if statements into two lines so you can set break points on the body * Take advantage of StringBuffers capacity constructor and give it help to pre allocate storage. – LanDenLabs Aug 12 '18 at 20:30
4

Although there's probably a library out there that does this, the manual solution is quite simple:

  1. Create an output buffer (StringBuilder).
  2. Iterate through the string's characters
  3. For each character check if it's a tab or not.
    a. If it's not a tab, add it to the output buffer.
    b. If it's a tab, add a space first, then as many spaces as it takes for the length of the output buffer to be divisible by 4. (Your tab length.)
  4. Return the output buffer.
biziclop
  • 48,926
  • 12
  • 77
  • 104
2

I didn't find any implementation on the web, so I have written it myself, on the idea of biziclop:

/**
 * Replace the tabulator characters of a String by the corresponding number of spaces.
 * Example:<pre>
 *   1.&lt;tab&gt;firstpoint&lt;tab&gt;page  1
 *   10.&lt;tab&gt;secondpoint&lt;tab&gt;page 10 </pre>
 * will become<pre>
 *   1.      firstpoint      page  1
 *   10.     secondpoint     page 10</pre>
 *
 * @param text     the text
 * @param tabstop  the espacement between the tab stops, typically 4 or 8
 * @return The text, with no &lt;tab&gt; character anymore
 */
public static String retab(final String text, final int tabstop)
{
    final char[] input = text.toCharArray();
    final StringBuilder sb = new StringBuilder();

    int linepos = 0;
    for (int i = 0; i<input.length; i++)
    {
        // treat the character
        final char ch = input[i];
        if (ch == '\t')
        {
            // expand the tab
            do
            {
                sb.append(' ');
                linepos++;
            } while (linepos % tabstop != 0);
        }
        else
        {
            sb.append(ch);
            linepos++;
        }

        // end of line. Reset the lineposition to zero.
        if (ch == '\n' || ch == '\r' || (ch|1) == '\u2029' || ch == '\u0085')
            linepos = 0;

    }

    return sb.toString();
}
Olivier Faucheux
  • 2,520
  • 3
  • 29
  • 37
1

I rewrote The ANTLR Guy's answer using Java 8 Streams, also using Alexis C.'s Streams-based code to repeat a string:

static String repeatString(String s, int count) {
   return Stream.generate(() -> s).limit(count).collect(Collectors.joining());
}

static String expandTabs(String s, int tabSize) {
   int[] col = new int[1];
   return s.chars().mapToObj(c -> {
      switch (c) {
         case '\t':
            int expandBy = tabSize - col[0] % tabSize;
            col[0] += expandBy;
            return repeatString(" ", expandBy);
         case '\n':
            col[0] = 0;
            break;
         default:
            col[0]++;
      }
      return String.valueOf((char) c);
   }).collect(Collectors.joining());
}
Community
  • 1
  • 1
Simon Kissane
  • 4,373
  • 3
  • 34
  • 59
1

A variation on Eldar (Abusalimov, Terence Parr) post.

Changes:
1. Split if-statements onto two lines so you can set break points on body
2. Use StringBuffer's capacity constructor to help it pre-allocate storage.

public static String expandTabs(String str, int tabSize) {
    if (str == null)
        return null;
    StringBuilder buf = new StringBuilder(str.length()+tabSize);
    int col = 0;
    for (int i = 0; i < str.length(); i++) {
        char c = str.charAt(i);
        switch (c) {
            case '\n' :
                col = 0;
                buf.append(c);
                break;
            case '\t' :
                buf.append(spaces(tabSize - col % tabSize));
                col += tabSize - col % tabSize;
                break;
            default :
                col++;
                buf.append(c);
                break;
        }
    }
    return buf.toString();
}

public static StringBuilder spaces(int n) {
    StringBuilder buf = new StringBuilder(n);
    for (int sp = 0; sp < n; sp++)
        buf.append(" ");
    return buf;
}
LanDenLabs
  • 1,566
  • 16
  • 10