Alters

Curiosidades del split en Java

Buenas a todos!

Estuve el otro día peleando con el método split de Java, y me topé con una situación que no me había dado problemas... hasta que me los dio :P

Así pues, me decidí a darle una vuelta a todo, y encontré una solución que me gustaría compartir con vosotros.




El problema en si, viene a ser que cuando hacemos un split de un String en Java, nuestra "aguja" se pierde... es decir


  • Aguja: "palabra"
  • Pajar: "Tengo una palabra, dos palabras, tres palabras

Al hacer nuestro split, obtenemos como retorno el objeto String[] = {"Tengo una ", ", dos ", "s, tres ", "s"};

Pero... ¿y si quiero recuperar mi aguja inicial?

Pensaréis que es una tontería, ya que la aguja es "palabra", y basta con ir intercalando la aguja entre las piezas del retorno... ¡pues resulta que hay una pequeña excepción!

Y es que, resulta que Java puede trabajar con expresiones regulares (regexp), lo cual hace que nuestra aguja pueda tener valores muy diversos.

En mi caso, estaba intentando jugar con los printf de C, así que pensé en detectar el siguiente regexp:

"%'\\S+:\\S+'"

Esto atiende a todas las posibles formas de  %'(cualquier texto):(cualquier texto)'; es decir, por ejemplo:

%'i:int1'

Para este caso, necesitaba partir (split) las líneas de texto, y necesitaba recuperar los datos que se habían ido por el camino, para poder procesarlos.

Existe una alternativa, pero solo funciona con regexp sencillos: colocar un "?<=" antes del regexp para que el texto eliminado se quede al final de cada pieza (y por algún motivo, no funcionaba con mi regexp).

Así que, me puse a ello, y cree un split que mantiene los textos eliminados.


Get Raw
0001import java.util.ArrayList;
0002 
0003/**
0004 * SplittedString class
0005 * 
0006 * Class used to have an alternative type of split in which the return contains
0007 * all the pieces, including the parts that consists on the given regexp.
0008 * 
0009 * @author DoHITB
0010 *
0011 */
0012public class SplittedString{
0013 
0014 /**
0015  * Split method.
0016  * 
0017  * Main method. It does the split of the given string.
0018  * @param text: text to be splitted
0019  * @param raw: the regexp to be splitted
0020  * @return an array containing all pieces.
0021  */
0022 public static final String[] split(String text, String raw){
0023  //return var
0024  ArrayList ret = new ArrayList();
0025 
0026  //it indicates if there's need to call treatpieced
0027  boolean pieced = false;
0028 
0029  //make a "normal" split
0030  String pieces[] = text.split(raw);
0031 
0032  if(pieces.length == 1) 
0033   /*
0034    * there's a line that contains text with no regex
0035    *  -- or --
0036    * there's a line that contains only a regex
0037    */
0038   if(pieces[0].equals(text))
0039    //there is no regex
0040    ret.add(text);
0041   else
0042    //temp <> pieces, so there's a regex.
0043    pieced = true;
0044  else if(pieces.length > 1)
0045   /*
0046    * there's a line that contains regex + text + regex
0047    *  -- or --
0048    * there's a line that contains text + regex + text
0049    */
0050   pieced = true;
0051  else 
0052   /*
0053    * there's a line that only contains regex
0054    */
0055   ret.add(text);
0056 
0057  if(pieced)
0058   ret = treatPieced(pieces, text);
0060  return (String[]) ret.toArray(new String[ret.size()]);
0061 }
0062 
0063 
0064 /**
0065  * TreatPieced method
0066  * 
0067  * It searches the begining and end of each "normal" split pieces and then
0068  * retrieves the missing pieces from the original text.
0069  * 
0070  * @param pieces: array given by "normal" split
0071  * @param text: the original String to be splitted
0072  * @return: an ArrayList containing all pieces
0073  */
0074 private static final ArrayList treatPieced(String[] pieces, 
0075   String text){
0076  //return var
0077  ArrayList ret = new ArrayList();
0078 
0079  //actual position from text
0080  int l = -1;
0081 
0082  //where the text chonk starts
0083  int start = 0;
0084 
0085  //where the text chonk ends
0086  int end = 1;
0087 
0088  //indicator for a special operation on first iteration
0089  boolean first = true;
0090 
0091  //piece length. Declared outside for to avoid create it each time
0092  int pl = 0;
0093 
0094  //the middle pieces that "split" eliminates
0095  ArrayList middle = new ArrayList();
0096 
0097  //for each piece
0098  for(int i = 0;i < pieces.length;i++){
0099   if(pieces[i].isEmpty() && i == 0){
0100    //this means it start with regex, then text
0101    first = false;
0102    continue;
0103   }
0104 
0105   pl = pieces[i].length();
0106 
0107   //this will put "l" on the start of "pieces[i]"
0108   while(!text.substring(++l, (l + pl)).equals(pieces[i]));
0109 
0110   /*
0111    * on first part, if l == 0 means that temp start with text
0112    * if l > 0, it starts with a variable
0113    */
0114   if(first){
0115    /*
0116     * if end > start --> we're starting to search. Store the value
0117     * if end < start --> we already have a start. Store the value
0118     */
0119    if(end > start){
0120     start = l + pl;
0121     l = start;
0122    }else 
0123     end = l;
0124 
0125    //Once we have done a pair, store the middle value.
0126    if(end > start){
0127     middle.add(text.substring(start, end));
0128     start = end + pl;
0129    }
0130   }else{
0131    //On the first iteration, make special treatment
0132    if(i == 1){
0133     start = 0;
0134     end = l;
0135     middle.add(text.substring(start, end));
0136     start = end + pl;
0137    }else{
0138     /*
0139      * if end > start --> we're starting to search. Store it
0140      * if end < start --> we already have a start. Store it
0141      */
0142     if(end > start){
0143      start = l + pl;
0144      l = start;
0145     }else 
0146      end = l;
0147 
0148     //Once we have done a pair, store the middle value.
0149     if(end > start){
0150      middle.add(text.substring(start, end));
0151      start = end + pl;
0152     }
0153    }
0154   }
0155  }
0156 
0157  //if we have an unfinished matching, it an end-var.
0158  if(start < text.length())
0159   middle.add(text.substring(start));
0160 
0161  //At this point, we have a shuffle of pieces, middle, pieces, ...
0162  Object[] midA = middle.toArray();
0163 
0164  /*
0165   * first = true: text + var + text + var + ...
0166   * first = false: var + text + var + text + ...
0167   */
0168  if(first){
0169   //merge pieces and ids.
0170   for(int i = 0;i < pieces.length;i++){
0171    ret.add(pieces[i]);
0172 
0173    if(i < midA.length)
0174     ret.add((String) midA[i]);
0175   }
0176  }else{
0177   //merge pieces and ids.
0178   for(int i = 0;i < midA.length;i++){
0179    ret.add((String) midA[i]);
0180 
0181    if(i + 1 < pieces.length)
0182     ret.add(pieces[i + 1]);
0183   }
0184  }
0185 
0186  return ret;
0187 }
0188}

Con esto, podemos obtener un String[] que contenga todos los fragmentos de nuestro split, incluyendo los fragmentos que un split "normal" eliminaría.

Espero que en algún momento os sea útil, o bien que pueda serviros para alguna idea similar... o simplemente como inspiración para algo!!

Nos vemos!


No hay comentarios:

Publicar un comentario