2010-07-19 14 views
5

मैं PHP का उपयोग कर सीएसवी डेटा के एक सेट को पार्स करने का प्रयास कर रहा हूं, लेकिन एक बड़ी समस्या है। खेतों में से एक एक लंबा वर्णन क्षेत्र है, जिसमें संलग्नक के भीतर लाइनब्रेक होते हैं।आप एक्सेल सीएसवी डेटा को कैसे पार्स कर सकते हैं जिसमें डेटा में लाइनब्रैक शामिल हैं?

मेरा प्राथमिक मुद्दा कोड का एक टुकड़ा लिख ​​रहा है जो डेटा लाइन को लाइन से विभाजित कर सकता है, लेकिन यह भी पहचानता है कि डेटा के भीतर लाइनब्रेक का उपयोग नहीं किया जाना चाहिए। इस क्षेत्र के भीतर लाइनब्रेक ठीक से बच नहीं पाए जाते हैं, जिससे उन्हें वैध लाइनब्रेक्स से अलग करना मुश्किल हो जाता है।

मैंने नियमित अभिव्यक्ति के साथ आने की कोशिश की है जो इसे ठीक से संभाल सकता है, लेकिन अब तक कोई भाग्य नहीं था। कोई विचार?

सीएसवी प्रारूप:

"####","text data here", "text data \n with linebreaks \n here"\n 
"####","more text data", "more data \n with \n linebreaks \n here"\n 

उत्तर

9

aleske के अनुसार, पीएचपी के fgetcsv समारोह के लिए दस्तावेज में एक टिप्पणीकार:

पीएचपी का CSV से निपटने सामान अमानक है और RFC4180 के साथ विपरीत है, इस प्रकार fgetcsv() फाइलों के साथ ठीक तरह से निपट नहीं सकते [जिसमें लाइन ब्रेक होते हैं] ...

और उन्होंने निम्नलिखित फू की पेशकश की nction इस सीमा के आस-पाने के लिए:

function csvstring_to_array(&$string, $CSV_SEPARATOR = ';', $CSV_ENCLOSURE = '"', $CSV_LINEBREAK = "\n") { 
    $o = array(); 

    $cnt = strlen($string); 
    $esc = false; 
    $escesc = false; 
    $num = 0; 
    $i = 0; 
    while ($i < $cnt) { 
$s = $string[$i]; 

if ($s == $CSV_LINEBREAK) { 
    if ($esc) { 
    $o[$num] .= $s; 
    } else { 
    $i++; 
    break; 
    } 
} elseif ($s == $CSV_SEPARATOR) { 
    if ($esc) { 
    $o[$num] .= $s; 
    } else { 
    $num++; 
    $esc = false; 
    $escesc = false; 
    } 
} elseif ($s == $CSV_ENCLOSURE) { 
    if ($escesc) { 
    $o[$num] .= $CSV_ENCLOSURE; 
    $escesc = false; 
    } 

    if ($esc) { 
    $esc = false; 
    $escesc = true; 
    } else { 
    $esc = true; 
    $escesc = false; 
    } 
} else { 
    if ($escesc) { 
    $o[$num] .= $CSV_ENCLOSURE; 
    $escesc = false; 
    } 

    $o[$num] .= $s; 
} 

$i++; 
    } 

// $string = substr($string, $i); 

    return $o; 
} 

है जैसे कि यह चाल करना होगा लग रहा है यही कारण है कि।

+2

यह उत्कृष्ट लाइब्रेरी भी काम करती है: http://code.google.com/p/parsecsv-for-php/ – danieltalsky

-1

आप एक सीएसवी पार्स करने के लिए fgetcsv या strgetcsv का उपयोग कर सकते हैं। PHP दस्तावेज के अंदर उदाहरण देखें।

+2

जब मैंने पिछले कुछ वर्षों पहले उनका उपयोग करने की कोशिश की, तो getcsv फ़ंक्शंस में से कोई भी न्यूलाइन स्वीकार नहीं करेगा उद्धृत क्षेत्र वे इसे रिकॉर्ड का अंत मानेंगे। – Charles

0

मैं अपनी आवश्यकताओं के लिए काम करने के लिए कुछ विशेष झंडे के साथ एक नियमित अभिव्यक्ति को संशोधित करने में सक्षम होने के समाप्त हो गया।

1) 'एस' झंडा डॉट, जो आम तौर पर नहीं है के तहत नई-पंक्तियों को पकड़ने के लिए संपादक बताता है:

preg_match_all('/"\d+",".*",".*"\n/sU', $csv_data, $matches); 

यह कुछ कारणों के लिए काम करने के लिए लगता है: मैं निम्नलिखित समारोह कॉल के लिए इस्तेमाल किया मामला। इसका दुर्भाग्यपूर्ण साइड इफेक्ट यह है कि वैध न्यूलाइन अक्षरों को भी डॉट द्वारा पकड़ा जाता है, जो सैद्धांतिक रूप से पूरे सीएसवी से एक परिणाम में मिल सकता है, इसलिए

2) मैंने यू ध्वज जोड़ा। यह डॉट को डिफॉल्ट रूप से अप्रासंगिक होने के लिए कहता है, और इस तरह, यह वर्तमान में केवल एक पंक्ति को एक टुकड़ा से मेल खाता है।

2

समस्या यह है कि "\ n" एस्केप स्ट्रिंग एक ही नए लाइन वर्ण का मूल्यांकन नहीं करती है जो कि Excel अपनी पंक्ति डिलीमीटर के लिए उपयोग करता है। ASCII वर्ण जो एक्सेल का उपयोग करता है ASCII 13. है। निम्न कोड कुशलतापूर्वक एक .csv फ़ाइल को पार्स करेगा जो $ file_get_contents() विधि के माध्यम से पारित होता है।

<?php 

//variable to store filename of file 
$filename = $_SERVER['DOCUMENT_ROOT'] . "/site/docs/boothmap.csv"; 

//read file in as string 
$file = file_get_contents($filename); 

//convert csv to array 
//first to single dimensional array 
$array1D = explode(chr(13),$file); 

//create new array to hold 2d array 
$array2D = array(); 

//iterate through 1 dimensional array and explode each value to the new array 
foreach($array1D as &$row) 
{ 
array_push($array2D, explode(',',$row)); 
} 

//pop off empty last row of array2D 
array_pop($array2D); 

//iterate through $array2D building table of data 
//start table with column headers 
echo "<table border=\"1\">\n<tr>\n<th>Company</th>\n<th>Booth #</th>\n<th>Location</th>\n</tr>\n"; 

foreach ($array2D as &$row) 
{ 
    echo "<tr>\n"; 
    foreach($row as &$subrow) 
    { 
     echo "<td>" . $subrow . "</td>\n"; 
    } 
    echo "</tr>\n"; 
} 

//close table 
echo "</table>"; 
+0

यह मेरे लिए काम करता था लेकिन मुझे chr (10) – MadTurki

+0

के लिए chr (13) बदलना पड़ा, यदि आपके किसी एक स्ट्रिंग में कॉमा है तो यह काम नहीं करेगा। –

3

मैंने पाया कि आप CSV को यूनिक्स प्रारूप में कनवर्ट करने के बाद सामान्य CSV पार्सर का उपयोग कर सकते हैं।

यहां एक ऐसा फ़ंक्शन है जिसने मेरे लिए चाल बनाई है।

function dos2unix($s) { 
    $s = str_replace("\r\n", "\n", $s); 
    $s = str_replace("\r", "\n", $s); 
    $s = preg_replace("/\n{2,}/", "\n\n", $s); 
    return $s; 
} 

और एक पार्स समारोह

function csvstring_to_array($string, $separatorChar = ',', $enclosureChar = '"', $newlineChar = PHP_EOL) { 
    // @author: Klemen Nagode 
    $string = dos2unix($string); 
    $array = array(); 
    $size = strlen($string); 
    $columnIndex = 0; 
    $rowIndex = 0; 
    $fieldValue=""; 
    $isEnclosured = false; 
    for($i=0; $i<$size;$i++) { 

     $char = $string{$i}; 
     $addChar = ""; 

     if($isEnclosured) { 
      if($char==$enclosureChar) { 

       if($i+1<$size && $string{$i+1}==$enclosureChar){ 
        // escaped char 
        $addChar=$char; 
        $i++; // dont check next char 
       }else{ 
        $isEnclosured = false; 
       } 
      }else { 
       $addChar=$char; 
      } 
     }else { 
      if($char==$enclosureChar) { 
       $isEnclosured = true; 
      }else { 

       if($char==$separatorChar) { 

        $array[$rowIndex][$columnIndex] = $fieldValue; 
        $fieldValue=""; 

        $columnIndex++; 
       }elseif($char==$newlineChar) { 
        echo $char; 
        $array[$rowIndex][$columnIndex] = $fieldValue; 
        $fieldValue=""; 
        $columnIndex=0; 
        $rowIndex++; 
       }else { 
        $addChar=$char; 
       } 
      } 
     } 
     if($addChar!=""){ 
      $fieldValue.=$addChar; 

     } 
    } 

    if($fieldValue) { // save last field 
     $array[$rowIndex][$columnIndex] = $fieldValue; 
    } 
    return $array; 
} 
0

यह एक पुराने धागा है, लेकिन मैं इस समस्या हुई है और मैं एक regex के साथ इसे हल तो आप सिर्फ इतना है कि के लिए एक पुस्तकालय से बच सकते हैं। यहां कोड PHP में है लेकिन इसे अन्य भाषा में अनुकूलित किया जा सकता है।

$parsedCSV = preg_replace('/(,|\n|^)"(?:([^\n"]*)\n([^\n"]*))*"/', '$1"$2 $3"', $parsedCSV);

यह कुशल नहीं हो सकता है अगर सामग्री बहुत बड़ी है, लेकिन यह कई मामलों के लिए मदद कर सकते हैं और विचार पुन: उपयोग किया जा सकता है, हो सकता है (लेकिन आप नहीं करनी छोटे टुकड़ों के लिए ऐसा करने से अनुकूलित फिक्स-साइज्ड बफर के साथ कटौती को संभालें)। यह समाधान मानता है कि एक लाइनब्रेक युक्त फ़ील्ड डबल कोट्स से घिरे हुए हैं, जो कम से कम मैंने जो देखा है, उसके लिए एक मान्य धारणा प्रतीत होती है। इसके अलावा, डबल कोट्स को , का पालन करना चाहिए या एक नई लाइन (या पहली पंक्ति) की शुरुआत में रखा जाना चाहिए।

उदाहरण:

field1,"field2-part1\nfield2-part2",field3

यहाँ \ N एक खाली स्थान के द्वारा बदल दिया जाता है, तो परिणाम होगा:

field1,"field2-part1 field2-part2",field3

regex के साथ-साथ कई लाइनब्रेक संभाल चाहिए।

संबंधित मुद्दे