如何将数据从自定义格式转换为CSV?

如何将数据从自定义格式转换为CSV?,csv,etl,Csv,Etl,我有一个文件,文件内容如下,我在这里只输出了两条记录,但单个文件中大约有1000条记录: Record type : GR address : 62.5.196 ID : 1926089329 time : Sun Aug 10 09:53:47 2014 Time zone : + 16200 seconds address [1] : 61.5.196

我有一个文件,文件内容如下,我在这里只输出了两条记录,但单个文件中大约有1000条记录:

           Record type : GR
            address : 62.5.196
             ID : 1926089329
     time : Sun Aug 10 09:53:47 2014
               Time zone : + 16200 seconds
         address [1] : 61.5.196
            PN ID : 412 1
          ---------- Container #1 (start) -------
          inID : 101
          ---------- Container #1 (end) -------
          timerecorded: Sun Aug 10 09:51:47 2014
          Uplink data volume : 502838
          Downlink data volume : 3133869
          Change condition : Record closed

--------------------------------------------------------------------
    Record type : GR
            address : 61.5.196
             ID : 1926089327
     time : Sun Aug 10 09:53:47 2014
               Time zone : + 16200 seconds
         address [1] : 61.5.196
            PN ID : 412 1
          ---------- Container #1 (start) -------
          intID : 100
          ---------- Container #1 (end) -------
          timerecorded: Sun Aug 10 09:55:47 2014
          Uplink data volume : 502838
          Downlink data volume : 3133869
          Change condition : Record closed
--------------------------------------------------------------------
    Record type : GR
            address : 63.5.196
             ID : 1926089328
     time : Sun Aug 10 09:53:47 2014
              Time zone : + 16200 seconds
         address [1] : 61.5.196
            PN ID : 412 1
          ---------- Container #1 (start) -------
          intID : 100
          ---------- Container #1 (end) -------
          timerecorded: Sun Aug 10 09:55:47 2014
          Uplink data volume : 502838
          Downlink data volume : 3133869
          Change condition : Record closed
我的目标是将其转换为CSV或txt文件,如下面所示

Record type| address |ID | time | Time zone| address [1] | PN ID 
GR |61.5.196 |1926089329 |Sun Aug 10 09:53:47 2014 |+ 16200 seconds |61.5.196 |412 1
任何关于如何开始这项工作的指南都会很好,我提供的示例我认为会给出清晰的想法,但用文字来说,我希望阅读每条记录的标题一次,并将其数据放在out out标题下


感谢您的时间和帮助或建议

您正在做的是创建提取/转换脚本(ETL的ET部分)。我不知道你打算使用哪种语言,但基本上任何语言都可以使用。就个人而言,除非这是一个庞大的文件,否则我建议使用Python,因为它很容易摸索,也很容易编写

首先,您需要彻底理解格式

  • 记录是如何分开的
  • 田地是如何分开的
  • 是否有任何字段是可选的
  • 如果是,可选字段是重要的,还是需要丢弃它们
  • 不幸的是,这都是脑力劳动:没有神奇的代码解决方案可以让这变得更容易。然后,一旦你弄明白了格式,你就要开始写代码了。这本质上是一系列数据转换:

  • 读取文件
  • 将其拆分为记录
  • 对于每个记录,将字段转换为适当的数据结构
  • 将数据结构序列化到CSV中

  • 如果你的文件比内存大,这会变得更复杂;例如,您可能希望在每次检测到记录分隔符时按顺序读取文件并创建一个记录对象,而不是先读取然后拆分。如果您的文件更大,您可能希望使用具有更好的多线程功能的语言来并行处理转换;但是这些都比现在听起来你需要去的要先进

    这是一个简单的PHP脚本,它将读取包含数据的文本文件,并编写一个包含结果的csv文件。如果您在安装了命令行PHP的系统上,只需将其保存到某个目录中的文件中,将数据文件复制到旁边的文件中,并将其重命名为“your_data_file.txt”,然后在该目录的命令行上调用“PHP which you_named_the_script.PHP”

    <?php
    $text = file_get_contents("your_data_file.txt");
    
    $matches;
    preg_match_all("/Record type[\s\v]*:[\s\v]*(.+?)address[\s\v]*:[\s\v]*(.+?)ID[\s\v]*:[\s\v]*(.+?)time[\s\v]*:[\s\v]*(.+?)Time zone[\s\v]*:[\s\v]*(.+?)address \[1\][\s\v]*:[\s\v]*(.+?)PN ID[\s\v]*:[\s\v]*(.+?)/su", $text, $matches, PREG_SET_ORDER);
    
    $csv_file = fopen("your_csv_file.csv", "w");
    if($csv_file) {
        if(fputcsv($csv_file, array("Record type","address","ID","time","Time zone","address [1]","PN ID"), "|") === FALSE) {
            echo "could not write headers to csv file\n";
        }
        foreach($matches as $match) {
            $clean_values = array();
            for($i=1;$i<8;$i++) {
                $clean_values[] = trim($match[$i]);
            }
            if(fputcsv($csv_file, $clean_values, "|") === FALSE) {
                echo "could not write data to csv file\n";
            }
        }
        fclose($csv_file);
    } else {
        die("could not open csv file\n");
    }
    

    请避免使用“每种可以使用的语言”标记。仅标记实际使用的语言。因为没有使用任何语言,但这只是一种方法的建议,它很可能会被关闭。@user2864740-我不认为关于算法的问题是离题的,这就是问题所在;他并不是在询问关于使用哪种工具的建议,而是询问如何开始,这基本上意味着他希望得到关于要使用的算法的建议。@syrion然后不要投票关闭它。我发现这样的问题对于这个网站来说太广泛了。谢谢@janwschaefer我一直在寻找如何开始这个问题,但是你提供了解决方案,我感谢您的时间和解决方案:很抱歉打扰您,但它对示例文件有效,但当我对原始文件进行更改并运行包含33个字段的原始文件时,它不起作用,它会使用标题写入文件,但不会写入内容,原始文件的唯一区别是(a:它有33列b:在这33个字段之间有另一行,如“------------Container#1(end)----”)我所做的更改是($I=1;$I)的,它使用正则表达式(preg#u match_all(…)从数据文件解析数据。该表达式当前是静态的,它假设正好遇到问题中显示的数据格式。为了使其处理更多字段,必须增强正则表达式。这可以通过“愚蠢”来实现只需添加缺少的字段。或者您可以尝试使其更智能,以便它可以处理任意数量的字段。您能否发布数据字段的完整示例(可能有三个条目,带有所有分隔符和格式)?谢谢我更新了问题并按照说明添加了字段…谢谢您的时间…我无法在此处添加,因为字符有限制容器#1是否仅包含文件中所有条目的一个字段intID?换句话说,字段的数量和顺序是否始终相同?
    
    <?php
    
    $text = file_get_contents("your_data_file.txt");
    
    // this will match whole lines
    // only if they either start with an alpha-num character
    // or are completely made of dashes (record separator)
    // it also extracts the values of data lines one by one
    $regExp = '/(^\s*[a-zA-Z0-9][^:]*:(.*)$|^-+$)/m';
    
    $matches;
    preg_match_all($regExp, $text, $matches, PREG_SET_ORDER);
    
    $csv_file = fopen("your_csv_file.csv", "w");
    if($csv_file) {
    
        // in case the number or order of fields changes, adapt this array as well
        $column_headers = array(
            "Record type",
            "address",
            "ID",
            "time",
            "Time zone",
            "address [1]",
            "PN ID",
            "inID",
            "timerecorded",
            "Uplink data volume",
            "Downlink data volume",
            "Change condition"
        );
    
        if(fputcsv($csv_file, $column_headers, "|") === FALSE) {
            echo "could not write headers to csv file\n";
        }
    
        $clean_values = array();
        foreach($matches as $match) {
    
            // first entry will contain the whole line
            // remove surrounding whitespace
            $whole_line = trim($match[0]);
    
            if(strpos($whole_line, '-') !== 0) {
                // this match starts with something else than -
                // so it must be a data field, store the extracted value
                $clean_values[] = trim($match[2]);
            } else {
                // this match is a record separator, write csv line and reset buffer
                if(fputcsv($csv_file, $clean_values, "|") === FALSE) {
                    echo "could not write data to csv file\n";
                }
                $clean_values = array();
            }
        }
        if(!empty($clean_values)) {
            // there was no record separator at the end of the file
            // write the last entry that is still in the buffer
            if(fputcsv($csv_file, $clean_values, "|") === FALSE) {
                echo "could not write data to csv file\n";
            }
        }
    
        fclose($csv_file);
    
    } else {
        die("could not open csv file\n");
    }