Foro de elhacker.net

Programación => .NET (C#, VB.NET, ASP) => Mensaje iniciado por: okik en 7 Diciembre 2016, 20:41 pm



Título: Obtener el índice de un elemento obtenido con Split
Publicado por: okik en 7 Diciembre 2016, 20:41 pm
Quiero obtener el índice de un elemento obtenido con Split utilizando IndexOf pero no lo consigo.

Si hago esto:

Código
  1. Dim P As String() = {"[Primero]", "a", "b", "c", "[Segundo]"}
  2.        MessageBox.Show(CStr(P.ToList.IndexOf("[Segundo]")))

Me devuelve el índice correctamente.


Si hago esto otro:
Código
  1. Dim P As New List(Of String)
  2.        P.AddRange({"[Primero]", "a", "b", "c", "[Segundo]"})
  3.        MessageBox.Show(CStr(P.IndexOf("[Segundo]")))

También me devuelve el índice correctamente

Sin embargo con un array obtenido con Split no funciona, me devuelve -1.


Código
  1.  
  2.  
  3. Dim Texto As String =
  4.               "[Primero]" & Environment.NewLine &
  5.               "a" & Environment.NewLine &
  6.               "b" & Environment.NewLine &
  7.               "c" & Environment.NewLine &
  8.               "[Segundo]"
  9.  
  10.        Dim P As New List(Of String)
  11.        P.AddRange((Texto.ToString.Trim.Split(CChar(Environment.NewLine))))
  12.  
  13.        MessageBox.Show(CStr(P.IndexOf("[Segundo]")))
  14.        MessageBox.Show(CStr(P(4)))


No acabo de entender porqué si al llamarlo con P(4), si que me devuelve el valor. O sea que el elemento está en la lista P, sin embargo no me devuelve el índice.

Gracias



Conseguí hacerlo:

Código
  1.        Dim Texto As String =
  2.               "[Primero]" & Environment.NewLine &
  3.               "a" & Environment.NewLine &
  4.               "b" & Environment.NewLine &
  5.               "c" & Environment.NewLine &
  6.               "[Segundo]"
  7.  
  8.        Dim spl() As String = Texto.Split(CChar(Environment.NewLine))
  9.        Dim Lista As New List(Of String)
  10.        For Each Item As String In spl
  11.                Lista.Add(Item.Trim)
  12.        Next
  13.  
  14.        MessageBox.Show(Lista.IndexOf("[Segundo]").ToString)

Pero sigo si entender porque no puedo obtenerlo directamente como puse antes o usando directamente el array String:

Código
  1. Dim spl() As String = Texto.Split(CChar(Environment.NewLine))
  2.        MessageBox.Show(spl.ToList.IndexOf("[Segundo]").ToString)

Parece que Split devuelve los valores con espacios, de ahí que haya añadido Trim, para eliminar los caracteres de espacio.

Si lo pusiera así:

Código
  1.  
  2. ...
  3. For Each Item As String In spl
  4.            Lista.Add(Item)
  5.        Next
  6.        MessageBox.Show(Lista.IndexOf("[Segundo]").ToString)
  7.  
me devolvería -1 nuevamente.



Encontré otra solución para quien le sirva. Consiste en reemplazar el espacio de cada elemento, sin necesidad de crear otro array

Código
  1.         Dim Texto As String =
  2.             "[Primero]" & Environment.NewLine &
  3.             "a" & Environment.NewLine &
  4.             "b" & Environment.NewLine &
  5.             "c" & Environment.NewLine &
  6.             "[Segundo]"
  7.  
  8.        Dim spl() As String = Texto.Split(CChar(Environment.NewLine))
  9.        For Index As Integer = 0 To spl.Count - 1
  10.            spl(Index) = spl(Index).Trim
  11.        Next
  12.        MessageBox.Show(spl.ToList.IndexOf("[Segundo]").ToString)


Lo suyo sería poder hacerlo todo en la misma línea, en esta:
Código
  1. Dim spl() As String = Texto.Split(CChar(Environment.NewLine))



Vale, pues este sería otro modo más reducido. Usando  Regex
Código
  1.        Dim Texto As String =
  2.             "[Primero]" & Environment.NewLine &
  3.             "a" & Environment.NewLine &
  4.             "b" & Environment.NewLine &
  5.             "c" & Environment.NewLine &
  6.             "[Segundo]"
  7.        Dim spl() As String = System.Text.RegularExpressions.Regex.Split(Texto, Environment.NewLine)
  8.        MessageBox.Show(spl.ToList.IndexOf("[Segundo]").ToString)






Título: Re: Obtener el índice de un elemento obtenido con Split
Publicado por: Eleкtro en 9 Diciembre 2016, 19:41 pm
Sin embargo con un array obtenido con Split no funciona, me devuelve -1.
Código
  1.        Dim Texto As String =
  2.               "[Primero]" & Environment.NewLine &
  3.               "a" & Environment.NewLine &
  4.               "b" & Environment.NewLine &
  5.               "c" & Environment.NewLine &
  6.               "[Segundo]"
  7.  
  8.        Dim P As New List(Of String)
  9.        P.AddRange((Texto.ToString.Trim.Split(CChar(Environment.NewLine))))
  10.  
  11.        MessageBox.Show(CStr(P.IndexOf("[Segundo]")))
  12.        MessageBox.Show(CStr(P(4)))

No acabo de entender porqué si al llamarlo con P(4), si que me devuelve el valor.

No te funciona por que el texto que buscas y el que crees que devuelve el índice 4 no es "[Segundo]", sino LINEA VACÍA + "[Segundo]", es decir, lo que haces de forma incorrecta es partir la cadena de texto, puesto que estás añadiendo lineas en blanco, no tiene más misterio ...todas esas soluciones que encontraste usando RegEx y etc. son innecesarias, puesto que el único fallo que tuviste fue no darte cuenta de cual era el problema real.

La solución es muy sencilla, puedes utilizar el overload de la función String.Split() en donde puedes especificar el parámetro StringSplitOptions para eliminar lineas en blanco sobrantes:

Código
  1. P.AddRange((Texto.Split({ControlChars.NewLine}, StringSplitOptions.RemoveEmptyEntries)))

Te dejo el ejemplo completo (usando la sintaxis compatible con VB 14):
Código
  1. Dim str As String =
  2. "[Primero]
  3. a
  4. b
  5. c
  6. [Segundo]"
  7.  
  8. Dim strList As New List(Of String)
  9. strList.AddRange((str.Split({ControlChars.CrLf}, StringSplitOptions.RemoveEmptyEntries)))
  10.  
  11. MessageBox.Show(CStr(strList.IndexOf("[Segundo]")))
  12. MessageBox.Show(String.Format("""{0}""", strList(4)))

Saludos!


Título: Re: Obtener el índice de un elemento obtenido con Split
Publicado por: okik en 10 Diciembre 2016, 19:16 pm
Genial @Elektro, pues no tenía ni idea de StringSplitOptions.RemoveEmptyEntries y mira que estuve mirando a ver si había algo pero nada.

Pues ya me lo apunto y le quito el regex al programa que estaba haciendo..



Ya no hace falta que añada el list

Código
  1.        Dim Texto As String =
  2.           "[Primero]" & Environment.NewLine &
  3.           "a" & Environment.NewLine &
  4.           "b" & Environment.NewLine &
  5.           "c" & Environment.NewLine &
  6.           "[Segundo]"
  7.        Dim spl() As String = Texto.Split({Environment.NewLine}, StringSplitOptions.RemoveEmptyEntries)
  8.        MessageBox.Show(spl.ToList.IndexOf("[Segundo]").ToString)


No encontraba la forma porque la clave está en ponerlo entre corchetes {Environment.NewLine}, entonces la función Split si que ya te ofrece la opción de añadir StringSplitOptions


Título: Re: Obtener el índice de un elemento obtenido con Split
Publicado por: Eleкtro en 10 Diciembre 2016, 21:09 pm
la clave está en ponerlo entre corchetes {Environment.NewLine}, entonces la función Split si que ya te ofrece la opción de añadir StringSplitOptions

Sé que este tema ya ha quedado solucionado pero me gustaría comentarte algo que podría serte de utilidad en el futuro.

Efectivamente ese overload solo acepta un {Array} de caracteres y/o de strings, no acepta un string individual, yo por eso utilizo las siguientes extensiones (ATENCIÓN, PROPAGANDA GRATUITA :xD) que puedes encontrar en mi set de librerías ElektroKit para acomodar diferentes situaciones de uso en la vida real:

Código
  1. ''' <summary>
  2. ''' Contains custom extension methods to use with the <see cref="System.String"/> type.
  3. ''' </summary>
  4. <ImmutableObject(True)>
  5. <HideModuleName>
  6. Public Module StringExtensions
  7.  
  8.        ''' <summary>
  9.        ''' Splits a string into substrings that are based on the specified string.
  10.        ''' </summary>
  11.        <DebuggerStepThrough>
  12.        <Extension>
  13.        <EditorBrowsable(EditorBrowsableState.Always)>
  14.        Public Function Split(ByVal sender As String,
  15.                              ByVal separator As String,
  16.                              ByVal options As StringSplitOptions) As String()
  17.  
  18.            Return sender.Split({separator}, options)
  19.  
  20.        End Function
  21.  
  22.        ''' <summary>
  23.        ''' Splits a string into a maximum number of substrings based on the specified string.
  24.        ''' </summary>
  25.        <DebuggerStepThrough>
  26.        <Extension>
  27.        <EditorBrowsable(EditorBrowsableState.Always)>
  28.        Public Function Split(ByVal sender As String,
  29.                              ByVal separator As String,
  30.                              ByVal count As Integer,
  31.                              ByVal options As StringSplitOptions) As String()
  32.  
  33.            Return sender.Split({separator}, count, options)
  34.  
  35.        End Function
  36.  
  37. End Module

Lo puedes copiar y pegar y listo (o puedes ignorar lo que te digo xD), siempre viene bien tener utensilios así a mano. En el código he eliminado toda la documentación adicional (que es mucha) de los parámetros y etc. para que no se haga tediosa la lectura.

Como puedes ver, esas funciones no hacen nada más lejos que llamar a las funciones originales de .NET Framework pero pasando el string como array (así que no hay que preocuparse por un error humano por mi parte).

Ejemplo de uso:
Código
  1. Dim str As String = "Hello" & Environment.NewLine & "World"
  2. Dim arr As String() = str.Split(ControlChars.NewLine, StringSplitOptions.RemoveEmptyEntries)
  3.  
  4. For Each item As String In arr
  5.    Console.WriteLine(item)
  6. Next

y:

Código
  1. Dim str As String = "Hello" & Environment.NewLine & Environment.NewLine & "World"
  2. Dim arr As String() = str.Split(ControlChars.NewLine, 3, StringSplitOptions.None)
  3.  
  4. For Each item As String In arr
  5.    Console.WriteLine(item)
  6. Next

¡Un saludo!