2010-12-09 9 views
5

Czytam wklejenie danych pochodzących z programu Excel przy użyciuPodział użyciu separatora wyjątkiem sytuacji, gdy separator jest uciekł

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

ale niestety, Excel przechodzi tekst komórek zamiast wartości komórki. Gdy komórki są za pomocą specjalnego formatowania (takich jak separator tysięcy), dane w schowku za serię komórek w kolumnach, które wygląda następująco:

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

jest przechowywany jako to:

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

kiedy co naprawdę chcę to:

1234123.00, 2345.00, 342.00, 12345.00 

ja uprzednio za pomocą funkcji clipData.Split(new string[] { "," }, StringSllitOptions.None)) włączyć moich danych schowka CSV do szeregu komórek, ale to się nie powiedzie w kura zawiera sformatowany tekst zawierający przecinki.


Pytam, czy ktoś może wymyślić sposób, aby podzielić ten ciąg do zbioru komórek, ignorując przecinki uciekł ciągu \" bitów, ponieważ jest to w jaki sposób Excel wybiera się uciec komórki zawierające przecinki.

W skrócie, w jaki sposób można włączyć pojedynczy łańcuch zawierający ten:

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

na tablicę ciągów zawierających ten:

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

Bez rujnuje moją zdolność do analizowania proste rozdzielany przecinkami ciąg .

***** edit ***

Kontynuacja pytanie (w postaci DFA) tutaj: Split a string based on each time a Deterministic Finite Automata reaches a final state?

+0

Czy rzeczywiście widzisz \, po którym lub "właśnie używasz \", aby wskazać podwójny cytat zamiast denotacja początku ciągu znaków – juharr

+0

Nie możesz pobrać danych w innym formacie? Użyj IDataObject.GetFormats, aby pobrać listę dostępnych formatów i sprawdzić, czy możesz znaleźć taką, która działa lepiej – erikkallen

+0

Dobra, wezmę udział czas poświęcony na ocenę i testowanie kilku z tych rozwiązań i skontaktowanie się z wami – Alain

Odpowiedz

3

pierwsze mam do czynienia z danymi programu Excel przed i co zazwyczaj zobaczyć jest oddzielone przecinkami wartości, a jeżeli wartość jest uważane za ciągiem go będzie zawierał podwójne cudzysłowy (i może zawierać przecinki i podwójne cudzysłowy). Jeśli jest uważany za numeryczny, nie ma podwójnych cudzysłowów. Dodatkowo, jeśli dane zawierają podwójny cudzysłów, który zostanie ograniczony przez podwójny cudzysłów, taki jak "". Więc zakładając, że wszystko tu jest jak mam do czynienia z tym w ubiegłym

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;", "\""); 
} 

Oczywiście zakłada to dane w najbliższych jest ważna, więc jeśli masz coś takiego "fo,o"b,ar","bar""foo" to nie będzie działać. Dodatkowo, jeśli twoje dane zawierają &quot;, to zostanie zmienione na "które może, ale nie musi być pożądane."

+0

To dostaje gorzej, jeśli masz do czynienia z zrzutem CSV z programu Excel, ponieważ wiersze są rozdzielane przez znaki nowej linii, ale komórka może zawierać znak nowej linii i musisz sprawdzić, czy znak nowej linii jest "cytowany", aby określić, czy jest on częścią danych, czy też początkiem nowy wiersz. – juharr

+0

Jest to najlepsze podejście, ponieważ w przypadku dzielenia łańcuchów znaków przecinkami, o ile nie są one otoczone cudzysłowami, w rzeczywistości wymagana jest iteracyjna implementacja deterministycznych automatów skończonych odpowiadających temu problemowi. Podczas gdy wyrażenia regularne mogą sprawdzać, czy łańcuch spełnia DFA, nie jestem świadomy, że jest on w stanie podzielić łańcuchy na podstawie każdego wystąpienia stanu końcowego, który jest spełniony. Stąd ręczna ocena DFA przez iterację. Twoje zdrowie. – Alain

+0

Kolejne pytanie tutaj: http://stackoverflow.com/questions/4462168/split-a-string-based-on-each-time-a-deterministic-finite-automata-esaches-afinal – Alain

0

ze swojego przykład wejściowym, możemy zobaczyć, że istnieją „niechcianych” sekwencje trzech znaków:

\" 
\", 
,\" 

Tak, dodać wszystkie te sekwencje do tablicy wejściowej dla metody Split:

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

To da ci tablicę zawierającą kilka pustych elementów. Jeśli to jest problem, użyj StringSplitOptions.RemoveEmptyEntries zamiast StringSplitOptions.None:

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

To również podzieliłoby się na tysiąc separatorów w obrębie liczby. –

+0

@Tim: odpowiedź została poprawiona. –

+0

Jestem prawie pewien, że istnieje potencjał, aby dane wejściowe miały postać '123, 456, 789', ponieważ program Excel umieszcza podwójne cudzysłowy wokół danych, które są traktowane jako ciąg (w tym przypadku, gdy dane zawierają przecinek). W takim przypadku twoje rozwiązanie nie zadziałałoby. – juharr

1

Istnieje wiele sposobów, aby to zrobić. Jeden nieeleganckie sposób, że będzie działać to:

  1. Konwersja \ „\” na karcie lub innego ogranicznika (zakładam opuściłeś kilka \”w swoim przykładzie, ponieważ w przeciwnym razie ciąg nie jest zgodny
  2. Strip wszystkie pozostałe przecinki
  3. Usuń wszystkie pozostałe \ "
  4. Konwersja separatora (np.karta) z powrotem do przecinkiem

Teraz masz to, czego chciał w pierwszej kolejności

+0

Dlaczego warto zastąpić \ ", \" czymś innym, gdy można po prostu podzielić na to. Z doświadczenia wiem również, że Excel nie zawsze umieszcza podwójne dane wokół danych, więc istnieje potencjał typu "\" 1,234 \ ", 123, \" 2,345 \ ". – juharr

+0

Dzięki za ten pomysł. Dzieliłem się na '" 'ale nie chciałem się dzielić' \ ", więc zastąpiłem wszystkie' \ "' czymś szalonym, którego nigdy nie było, a potem podzieliłem je na '' 'i zastąpiłem szalone rzeczy z '\" 'po podziale.Było świetnie! – Johannes

0

Można spróbować użyć trochę 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; 

Ewentualnie, jeśli nie podoba tej sugestii, spróbuj wdrożyć podobny wzór z RegEx.

1

Zgadzam się z Kyle'em, że twój ciąg prawdopodobnie nie jest spójny.

Zamiast pierwszego etapu Kyle'a można użyć

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

chociaż teraz masz dwa problemy :) – Nat

+0

@Nat to nieodpowiedni żart i niepoprawny żart.Nawet ludzie mogą nie być świadomi, że powtarzacie żart, że jeśli rozważ rozwiązanie problemu za pomocą wyrażenia regularnego, wtedy masz dwa problemy, tzn. nie mówisz, że jest jakiś problem z jego rozwiązaniem.Więc twój żart jest tu nieodpowiedni, ponieważ mógłby zostać źle zinterpretowany przez tych, którzy go nie znają. Po drugie, gdy masz już rozwiązanie z regex, nie masz już dwóch problemów, jeśli działa, masz 0 problemów, więc twój żart jest również błędny w tym kontekście lub w jakimkolwiek kontekście, w którym masz rozwiązanie .. – barlop

Powiązane problemy