2008-11-25 20 views
5

Próbuję użyć C# do parsowania CSV. Użyłem wyrażeń regularnych, aby znaleźć "," i przeczytać ciąg, jeśli liczba moich nagłówków była równa liczbie meczy.Przetwarzanie CSV

Teraz to nie będzie działać, jeśli mogę mieć wartość jak:

"a",""b","x","y"","c" 

potem moje wyjście jest:

'a' 
'"b' 
'x' 
'y"' 
'c' 

ale czego chcę jest: tam

'a' 
'"b","x","y"' 
'c' 

Czy jakiegokolwiek regex lub jakiejkolwiek innej logiki, której mogę użyć w tym celu?

+0

@Matt: Nie każdy jest rodowitym Osoba mówiąca po angielsku. Nie wiem, co spowodowało mania redagowania. Przywróciłem go do wersji odzwierciedlającej pierwotną intencję, ponieważ znaczenie pytania zaczęło się pogarszać. – Tomalak

+0

@xyz: Przepraszamy za anarchiczne zmiany, które zostały wprowadzone do Twojego pytania bez powodu. Mam nadzieję, że teraz dobiegło końca. – Tomalak

+1

Twój CSV jest nieprawidłowy, powinien być "a", "" "b" "," "x" "," "y" "", "c" – dalle

Odpowiedz

1

Aby mieć plik CSV podzielny na części, wszelkie cudzysłowy wewnątrz wartości muszą zostać w jakiś sposób odpowiednio zmienione. Dwa standardowe sposoby, aby to zrobić, to przedstawienie podwójnego cudzysłowu albo jako dwóch podwójnych cudzysłowów, albo podwójnego cudzysłowu z ukośnika odwrotnego. To jest jeden z dwóch poniższych form:

""

\”

W drugiej postaci początkowy ciąg będzie wyglądać następująco:

"a", "\" b \ ", \" x \ ", \" y \ "", "c"

Jeśli twój łańcuch wejściowy nie jest sformatowany w oparciu o tak rygorystyczny format jak ten, wtedy masz bardzo małe szanse na pomyślne przeanalizowanie go w zautomatyzowanym środowisku.

+0

Nie, nie sądzę, że to prawda. w swoim przykładzie, o ile założysz, że CSV jest ważny tak długo, jak to możliwe (i nie tylko poddawaj się w części "" ", b"), to możesz nadal to analizować. – nickf

+0

Jest szansa, że ​​możesz osiągnąć właściwy rezultat - to tylko więcej pracy. Mam kod, który z powodzeniem to robi (choć nie używa regex). – Murph

0

Cóż, nie jestem kretynem regex, ale jestem pewien, że mają na to odpowiedź.

Proceduralnie przechodzi przez list po literze. Ustaw zmienną, powiedzmy dontMatch, na FALSE.

Za każdym razem, gdy napotkasz cytat, przełącz dontMatch.

za każdym razem, gdy wpadniesz na przecinek, sprawdź dontMatch. Jeśli jest to PRAWDA, zignoruj ​​przecinek. Jeśli FALSE, podziel na przecinek.

Działa to w podanym przykładzie, ale logika, której używasz w cudzysłowach, jest z gruntu błędna - musisz uciec przed nimi lub użyć innego ogranicznika (na przykład pojedyncze cudzysłowy), aby ustawić ważne cytaty poza drobnymi cytatami.

Przykładowo

"a", ""b", ""c", "d"", "e""

przyniesie złe wyniki.

Można to naprawić za pomocą innej łaty. Zamiast po prostu zachować prawdziwy fałsz, musisz dopasować wycenę.

Aby dopasować oferty, musisz wiedzieć, co było ostatnio widziane, co dostaje się na dość głęboki obszar analizowania. Zapewne w tym momencie będziesz chciał się upewnić, że twój język jest dobrze zaprojektowany, a jeśli tak, to możesz użyć narzędzia kompilatora, aby utworzyć parser.

-Adam

1

Jeśli wszystkie wartości są gwarantowana być w cudzysłowie, szukać wartości, a nie przecinkami:

("".*?""|"[^"]*") 

ta wykorzystuje fakt, że „najwcześniej najdłuższy mecz wins "- najpierw szuka wartości podwójnie cytowanych i z niższym priorytetem dla normalnych kwotowanych wartości.

Jeśli nie chcesz, by otaczająca cytat być częścią spotkania użytku:

"(".*?"|[^"]*)" 

i przejść do wartości w meczu grupy 1.

Jak powiedziałem: Warunkiem tego do pracy jest dobrze uformowany wkład z gwarantowanymi kwotowaniami lub podwójnymi kwotowaniami wokół każdej wartości. Również puste wartości muszą być podane! Ciekawym efektem ubocznym jest to, że nie zwraca uwagi na znak separatora. Przecinki, tabele, średniki, spacje, to ty. Wszystko zadziała.

+0

Dziękuję ... za odpowiedź informacyjną, wydaje mi się, że to działa. –

12

CSV, jeśli chodzi o takie rzeczy jak wieloliniowy, cytowany, różne ograniczniki * itd. - może stać się trudniejszy, niż mogłoby się wydawać ... może rozważyć wstępnie zwiniętą odpowiedź? Używam this i działa bardzo dobrze.

* = pamiętać, że niektóre lokalizacje użyć [TAB] jako C w formacie CSV ...

+0

Moje ustawienia narodowe używają średników dla "C". Nie zaczynaj mnie od Excela i _comma_ plików rozdzielonych, które nie są poprawnie przetwarzane, ponieważ przecinek jest rzeczywistym przecinkiem;) – VVS

+0

+1 Dzięki za sugestię czytnika CSV Lumenworks Marc , ładnie działa. BTW jest dostępny na NuGet: Install-Package LumenWorksCsvReader –

+0

@ Jonathan ah, świetnie - to świetnie, nie wiedziałem, że –

1

Tam jest często cytowany mówiąc: „Wiem, użyję wyrażeń regularnych”

niektórych ludzi, w konfrontacji z problemu, myślę Teraz mają dwa problemy: . (Jamie Zawinski)

Biorąc pod uwagę, że nie ma oficjalny standard dla plików CSV (zamiast istnieje duża liczba nieznacznie niekompatybilnych stylów), trzeba się upewnić, że to, co wdrożyć garnitury pliki będziesz otrzymywać. Nie ma sensu wdrażać niczego bardziej wyszukanego niż to, czego potrzebujesz - i jestem prawie pewien, że nie potrzebujesz regularnych wyrażeń.

Oto mój stab w prosty sposób wyodrębnić warunki - w zasadzie, to pętle przez linię szuka przecinkami, śledzenie, czy bieżący indeks jest w smyczkowy czy nie:

public IEnumerable<string> SplitCSV(string line) 
    { 
     int index = 0; 
     int start = 0; 
     bool inString = false; 

     foreach (char c in line) 
     { 
      switch (c) 
      { 
       case '"': 
        inString = !inString; 
        break; 

       case ',': 
        if (!inString) 
        { 
         yield return line.Substring(start, index - start); 
         start = index + 1; 
        } 
        break; 
      } 
      index++; 
     } 

     if (start < index) 
      yield return line.Substring(start, index - start); 
    } 

standardowym zastrzeżeniem - nietestowany kod, mogą występować błędy "off-by-one".

Ograniczenia

  • cudzysłowy wokół wartości nie są automatycznie usuwane.
    Aby to zrobić, dodaj czek tuż przed końcem oświadczenia yield return.

  • Pojedyncze cudzysłowy nie są obsługiwane w ten sam sposób jak cudzysłów
    można dodać osobny logiczna inSingleQuotedString, zmiana nazwy istniejącego logiczną do inDoubleQuotedString i leczenia zarówno w ten sam sposób. (Nie można dokonać istniejące logiczna zrobić podwójną pracę, bo trzeba ciąg do końca z tym samym środki, które się zaczęło.)

  • Biała spacja nie jest automatycznie usuwany
    Niektóre narzędzia wprowadzać spacji wokół przecinków w pliku CSV pliki do "ładnego" pliku; wtedy trudno jest odróżnić celowe białe znaki od formatowania białych znaków.

+0

Nie zapominaj, że multi-line jest również opcją dla cytowanych csv, i musisz przetestować to z wygasłymi cytatami takimi jak "niektóre" "dane" itd. –

+0

Są też ludzie, którzy za każdym razem wykonują własną procedurę obsługi ciągów, ponieważ słyszeli, że wyrazy regularne po prostu jej nie wycinają. Jeśli znasz * dane, z którymi masz do czynienia * wyraŜenia są w porządku. @xyz nie pytał, jak napisać pełnoprawny parser CSV o gotowych do użycia. – Tomalak

+0

Co ze zmienną "inString"? Brakuje jakiejś części logiki. – saku

0

parser Lumenworks CSV (open source, bezpłatny, ale wymaga logowania codeproject) jest zdecydowanie jednym z najlepszych I” ve używane. Pozwoli to zaoszczędzić na konieczności napisania regex i jest intuicyjny w użyciu.

3

Chciałbym użyć FileHelpers, gdybym był tobą. Wyrażenia regularne są w porządku, ale trudne do odczytania, szczególnie jeśli wrócisz po chwili do szybkiej poprawki.

Właśnie przez wzgląd wykonywania zdanie, szybkie & brudny roboczą procedurę C#:

public static List<string> SplitCSV(string line) 
{ 
    if (string.IsNullOrEmpty(line)) 
     throw new ArgumentException(); 

    List<string> result = new List<string>(); 

    bool inQuote = false; 
    StringBuilder val = new StringBuilder(); 

    // parse line 
    foreach (var t in line.Split(',')) 
    { 
     int count = t.Count(c => c == '"'); 

     if (count > 2 && !inQuote) 
     { 
      inQuote = true; 
      val.Append(t); 
      val.Append(','); 
      continue; 
     } 

     if (count > 2 && inQuote) 
     { 
      inQuote = false; 
      val.Append(t); 
      result.Add(val.ToString()); 
      continue; 
     } 

     if (count == 2 && !inQuote) 
     { 
      result.Add(t); 
      continue; 
     } 

     if (count == 2 && inQuote) 
     { 
      val.Append(t); 
      val.Append(','); 
      continue; 
     } 
    } 

    // remove quotation 
    for (int i = 0; i < result.Count; i++) 
    { 
     string t = result[i]; 
     result[i] = t.Substring(1, t.Length - 2); 
    } 

    return result; 
} 
+2

Naprawdę nie lubiłem FileHelpers. Zbyt duża ręczna konfiguracja. –

0

właśnie spróbować wyrażenia regularnego w moje code..its działać dobrze dla formatowany tekst z cytatem .. .

ale zastanawiałem się, czy możemy analizować poniżej wartości przez Regex ..

 
"First_Bat7679",""NAME","ENAME","FILE"","","","From: "DDD,_Ala%as"@sib.com" 

szukam za wynik: jest

 
'First_Bat7679' 
'"NAME","ENAME","FILE"' 
'' 
'' 
'From: "DDD,_Ala%as"@sib.com' 

Niż

+0

Nie, to niemożliwe w moim rozwiązaniu, ponieważ moje wyrażenie regularne zależy od cudzysłowów, które ograniczają wartości. Powinieneś pomyśleć o użyciu jednego z innych rozwiązań (np. Użyj parsera). – Tomalak

+0

Jeśli możesz coś z tym zrobić, zmień format CSV na mniej niejednoznaczny. Nie używaj przecinków ani cudzysłowów, gdy oba mogą wystąpić w obrębie wartości lub przynajmniej konsekwentnie unikaj przecinków i cudzysłowów w obrębie wartości. – Tomalak

+0

Jeśli użyjesz ogranicznika w swoim DSV, który nie pojawi się w twoich wartościach (lub pojawi się bardzo rzadko, i po prostu uciekniesz, jeśli tak się stanie), nie musisz się nawet martwić o cytaty. W moim odczuciu dwukropki są dość powszechne jako ogranicznik. –

1

Wypróbuj CsvHelper (biblioteka, którą utrzymuję) lub FastCsvReader. Oba działają dobrze. CsvHelper również pisze. Jak wszyscy mówili, nie tarzajcie się. : P

1

obsługuje pola wielowierszowe.

Można analizować pliki takie jak:

a,"line 1 
line 2 
line 3" 
b,"line 1 
line 2 
line 3" 

Oto deklaracja typu danych:

[DelimitedRecord(",")] 
public class MyRecord 
{ 
public string field1; 
[FieldQuoted('"', QuoteMode.OptionalForRead, MultilineMode.AllowForRead)] 
public string field2; 
} 

Oto Wykorzystanie:

static void Main() 
{ 
FileHelperEngine engine = new FileHelperEngine(typeof(MyRecord)); 
MyRecord[] res = engine.ReadFile("file.csv");  
}