2010-12-09 23 views
5

estoy leyendo los datos del portapapeles procedentes de Excel usandode Split usando delimitador delimitador excepto cuando se escapó

var stream = (System.IO.Stream) (Forms.Clipboard.GetDataObject()).GetData(Forms.DataFormats.CommaSeparatedValue);,

pero, por desgracia, Excel está pasando texto de la celda en lugar de los valores de celda. Cuando las células están utilizando un formato especial (tal como separador de miles), los datos del portapapeles para una serie de celdas en las columnas que tiene este aspecto:

1,234,123.00 2,345.00 342.00  12,345.00 

se almacena como esto:

\" 1,234,123.00 \",\" 2,345.00 \", 342.00 ,\" 12,345.00 \" 

cuando lo que realmente quiero es éste:

1234123.00, 2345.00, 342.00, 12345.00 

que había estado previamente usando la función clipData.Split(new string[] { "," }, StringSllitOptions.None)) convertir mis datos del portapapeles CSV en una serie de células, pero esto no funciona w Cuando hay texto con formato de escape que contiene comas.


lo que pido si alguien puede pensar en una manera de dividir esta cadena en un conjunto de células, haciendo caso omiso de las comas escaparon dentro de los bits de \", ya que esta es la forma en Excel es la elección para escapar de las células que contienen comas.

En pocas palabras, ¿cómo puedo convertir una sola cadena que contiene lo siguiente:

\" 1,234,123.00 \",\" 2,345.00 \", 342.00 ,\" 12,345.00 \" 

en una matriz de cadenas que contiene lo siguiente:

{ "1,234,123.00", "2,345.00", "342.00", "12,345.00" } 

sin arruinar mi capacidad para analizar una sencilla cadena separada por comas .

***** *** editar

Seguimiento pregunta (formulada como DFA) aquí: Split a string based on each time a Deterministic Finite Automata reaches a final state?

+0

¿De verdad está viendo un \ seguido de un" o simplemente está usando \ "para indicar una comilla doble en lugar de una denotación del comienzo de una cadena – juharr

+0

¿No puede obtener los datos en otro formato? Use IDataObject.GetFormats para recuperar una lista de formatos disponibles y vea si puede encontrar uno que funcione mejor. – erikkallen

+0

Bien, voy a tomar mi tiempo evaluando y probando varias de estas soluciones y volviendo a ustedes. – Alain

Respuesta

3

En primer lugar he tratado con datos de Excel antes y lo que normalmente se ve es valores separados por comas y si se considera que el valor es una cadena que tendrá comillas dobles a su alrededor (y puede contener comas y comillas dobles). Si se considera que es numérico, entonces no hay comillas dobles. Además, si los datos contienen una comilla doble que se delimitará con una comilla doble como "". Así que asumiendo que todos aquí es como me he ocupado de esto en el pasado

public static IEnumerable<string> SplitExcelRow(this string value) 
{ 
    value = value.Replace("\"\"", "&quot;"); 
    bool quoted = false; 
    int currStartIndex = 0; 
    for (int i = 0; i < value.Length; i++) 
    { 
     char currChar = value[i]; 
     if (currChar == '"') 
     { 
      quoted = !quoted;  
     } 
     else if (currChar == ',') 
     { 
      if (!quoted) 
      { 
       yield return value.Substring(currStartIndex, i - currStartIndex) 
        .Trim() 
        .Replace("\"","") 
        .Replace("&quot;","\""); 
       currStartIndex = i + 1; 
      } 
     } 
    } 
    yield return value.Substring(currStartIndex, value.Length - currStartIndex) 
     .Trim() 
     .Replace("\"", "") 
     .Replace("&quot;", "\""); 
} 

Por supuesto, esto supone que los datos que entra es válida por lo que si usted tiene algo así como "fo,o"b,ar","bar""foo" esto no funcionará. Además, si sus datos contienen &quot;, se convertirá en un "que puede o no ser deseable.

+0

Se pone peor si se trata de un volcado de csv de Excel porque las filas están delimitadas por líneas nuevas, pero una celda puede contener una nueva línea y debe ver si la línea nueva está "cotizada" para determinar si es parte de los datos o el comienzo de una nueva fila – juharr

+0

Este es el mejor enfoque dado aquí, porque cuando se trata de dividir cadenas por comas, a menos que estén rodeadas de comillas, lo que realmente se requiere es una implementación iterativa de los autómatas finitos determinísticos correspondientes a este problema. Si bien las expresiones regulares pueden validar si una cadena satisface un DFA, no soy consciente de que pueda dividir cadenas en función de cada instancia del estado final que se satisfaga. Por lo tanto, una evaluación manual del DFA por iteración. Aclamaciones. – Alain

+0

Pregunta de seguimiento aquí: http://stackoverflow.com/questions/4462168/split-a-string-based-on-each-time-a-deterministic-finite-automata-reaches-a-final – Alain

0

Desde su entrada de ejemplo, podemos ver que hay tres secuencias de "no deseados" personajes:

\" 
\", 
,\" 

Así, se suman todas estas secuencias a la matriz de entrada para el método Split:

string[] result = clipData.Split(new[] { @",\""", @"\"",", @"\""" }, 
    StringSplitOptions.None); 

Esto le dará una matriz que contiene algunos elementos vacíos. Si esto es un problema, utilice StringSplitOptions.RemoveEmptyEntries en lugar de StringSplitOptions.None:

string[] result = clipData.Split(new[] { @",\""", @"\"",", @"\""" }, 
    StringSplitOptions.RemoveEmptyEntries); 
+0

Eso luego se dividiría en el separador de miles dentro del número también. –

+0

@Tim: la respuesta está corregida. –

+0

Estoy bastante seguro de que existe la posibilidad de que la entrada sea '123, 456, 789' ya que Excel solo coloca las comillas dobles alrededor de datos que se consideran una cadena (en este caso cuando los datos contienen una coma). En ese caso, su solución no funcionaría. – juharr

1

Hay muchas maneras de hacer esto. Una forma poco elegante que funcionaría es:

  1. Convert \ "\" a la pestaña o algún otro delimitador (supongo que lo dejó fuera unos pocos \" en su ejemplo, porque de lo contrario la cadena no es consistente
  2. de Gaza todas las comas restantes
  3. Pele todos los restantes \ "
  4. Convierta su delimitador (por ejemplo,pestaña) de nuevo en una coma

Ahora usted tiene lo que quería en primer lugar

+0

¿Por qué sustituir \ ", \" con otra cosa cuando se puede dividir en eso? También sé por experiencia que Excel no siempre pone comillas dobles en torno a los datos, por lo que existe la posibilidad de algo como '\" 1,234 \ ", 123, \" 2,345 \ "'. – juharr

+0

Gracias por esta idea. Me estaba dividiendo en '" 'pero no quería dividirme en' \ "' así que reemplacé todo '\" 'con algo loco que nunca estaría allí, y luego lo dividí por' "' y reemplacé lo loco con '\" 'después de la división. ¡Funcionó muy bien! – Johannes

0

Usted podría tratar de usar un poco de LINQ:

string excelData = "\\\" 1,234,123.00 \\\",\\\" 2,345.00 \\\", 342.00 ,\\\" 12,345.00 \\\""; 

IEnumerable<string> cells = from x in excelData.Split(new string[] { "\\\"" }, StringSplitOptions.RemoveEmptyEntries) 
          let y = x.Trim(',').Trim() 
          where !string.IsNullOrWhiteSpace(y) 
          select y; 

Alternativamente, si no te gusta esta sugerencia, intenta implementar un patrón similar con RegEx.

1

Estoy de acuerdo con Kyle respecto a que su cadena probablemente no sea consistente.

En lugar de primer paso de Kyle podría utilizar

string[] vals = Regex.Split(value, @"\s*\"",\s*"); 
+2

aunque ahora tienes dos problemas :) – Nat

+0

@Nat es una broma inapropiada y una broma incorrecta también. En primer lugar la gente puede no ser consciente de que estás repitiendo una broma que si Considera resolver algo con una expresión regular, entonces tienes dos problemas, es decir, no estás diciendo que haya algún problema con su solución.Entonces su broma es inapropiada aquí ya que podría ser malinterpretada por aquellos que no están familiarizados con ella. En segundo lugar, una vez que tienes una solución de la expresión regular, ya no tienes dos problemas, si funciona, tienes 0 problemas, por lo que tu broma también es incorrecta en este contexto o en cualquier contexto donde tengas la solución. – barlop

Cuestiones relacionadas