使用pentaho解析文本文件
我有大量的文本文件需要加载到数据库中。它们的结构不是更常见的csv格式,而是如下所示:使用pentaho解析文本文件,pentaho,kettle,Pentaho,Kettle,我有大量的文本文件需要加载到数据库中。它们的结构不是更常见的csv格式,而是如下所示: TY - JOUR T1 - On the Structure and Life-History of Entyloma ranunculi (Bonorden) JF - Philosophical Transactions of the Royal Society of London. B (1887-1895) VL - 178 SP - 173 EP - 185 PY - 1887/01
TY - JOUR
T1 - On the Structure and Life-History of Entyloma ranunculi (Bonorden)
JF - Philosophical Transactions of the Royal Society of London. B (1887-1895)
VL - 178
SP - 173
EP - 185
PY - 1887/01/01/
UR - http://dx.doi.org/10.1098/rstb.1887.0008
M3 - doi:10.1098/rstb.1887.0008
AU - Ward, H.
ER -
如果每行是不同的字段,则字段名由前导字符表示
我想做的是将每一行加载到记录中相应的字段中。我想通过pentaho做到这一点,任何人都知道如何做到这一点。文本输入步骤是为csv输入设置的。您需要使用行反规范化步骤来反规范化行 步骤:
来自TY、T1、JF等。它们可以是您选择的任何名称 4.3。对于每个新字段,指定值字段名称,即分配给拆分后的第二个字段的字段。在字段拆分器中的示例中,我分配了两个字段-fld_hdr和fld_content。“我的值”字段包含fld_内容fld 4.4。指定字段类型以及每行上的剩余列(可选)
我创建了一个示例,但看不到将ktr文件上载到何处。您需要使用行反规范化步骤来反规范化行 步骤:
来自TY、T1、JF等。它们可以是您选择的任何名称 4.3。对于每个新字段,指定值字段名称,即分配给拆分后的第二个字段的字段。在字段拆分器中的示例中,我分配了两个字段-fld_hdr和fld_content。“我的值”字段包含fld_内容fld 4.4。指定字段类型以及每行上的剩余列(可选)
我创建了一个示例,但不知道在哪里上传ktr文件。该文本示例看起来非常熟悉 匆匆忙忙地去检查一些东西 如果这个文本示例是我认为的“皇家科学学会期刊描述文件”,那么您将无法编写pentaho脚本来解析这些内容 我去过那里,尝试过,非常痛苦 为什么? 嗯,有很多事情 首先,文件格式未经过严格检查,因此会发现一些文件中没有2个字符标识,后跟2个空格、破折号1空格和数据格式行 您还会发现一些文件中有未解析的LATEX命令和/或未处理的变量替换 简言之,这些文件(至少是我上次用它们做任何事情时看到的那些文件)处于一种可怕的状态 您将遇到的另一个主要问题是缺少行 每个描述符应该有11个主标记,如下所示:
TY
T1
JF
VL
SP
EP
PY
UR
M3
AU
ER
从内存中可以看出:
TY - Title
T1 - Description
JF - ???
VL - Volume number
SP - Start page
EP - End page
PY - Published Year
UR - Url
M3 - ???
AU - Author name
ER - ???
通常,您会发现并非所有这些行都存在,但要使列在CSV中对齐,您仍然需要添加空白条目
还要注意AU字段,它可以而且经常包含一个文件的多个条目,因此您通常会得到:
TY - ....
T1 - ....
....
AU - ....
AU - ....
....
在上面Carey的回答中,使用pentaho方法处理这个问题会使您的许多行失去同步,因为这将要求每个文件的每个标记有一行
关于Carey的答案,我不得不说这是一个非常好的答案,并且比我放弃之前所做的更接近于做一个很好的转换,但冷酷的事实是,这些文件只是不适合pentaho可靠地处理
为此,Iv'e拿出了一些我写的C#文件,将这些文本文件放在一个文件夹中,并将它们转换成一个扁平的CSV
生成的CSV并不完美,仍然需要进行少量调整,但它将使您达到99.9%的效果,并且使用pentaho处理生成的文件将比源文件本身更容易
代码是相当通用的C#,因此它应该在windows和mono上编译(尽管我不得不承认,Iv'e没有在以后的版本上测试它)
代码如下:
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
namespace SciDataParse
{
class RecordData
{
public string TY { get; set; }
public string T1 { get; set; }
public string JF { get; set; }
public string VL { get; set; }
public string SP { get; set; }
public string EP { get; set; }
public string PY { get; set; }
public string UR { get; set; }
public string M3 { get; set; }
public List<string> AU { get; set; }
public string ER { get; set; }
public RecordData()
{
AU = new List<string>();
TY = string.Empty;
T1 = string.Empty;
JF = string.Empty;
VL = string.Empty;
SP = string.Empty;
EP = string.Empty;
PY = string.Empty;
UR = string.Empty;
M3 = string.Empty;
ER = string.Empty;
}
}
class Program
{
static RecordData ProcessFile(string inputName)
{
RecordData result = new RecordData();
using (StreamReader reader = new StreamReader(inputName))
{
string inputLine = reader.ReadLine();
while(!string.IsNullOrEmpty(inputLine))
{
if (!Regex.IsMatch(inputLine, @"^[A-Z,0-9][A-Z,0-9]\s+-\s+.*$"))
{
inputLine = reader.ReadLine();
continue; // Regex match to ensure lines are valid format
}
string[] lineItems = inputLine.Split('-');
string tag = lineItems[0].Trim();
string data = lineItems[1].Trim();
switch (tag)
{
// Sort and add lines to our result object. Note we check and change null to empty strings and filter commas
// so that we don't create any problems with outputting CSV data
case "TY" :
result.TY = !string.IsNullOrEmpty(data) ? data : string.Empty;
break;
case "T1":
result.T1 = !string.IsNullOrEmpty(data) ? data.Replace(",", string.Empty) : string.Empty;
break;
case "JF":
result.JF = !string.IsNullOrEmpty(data) ? data.Replace(",", string.Empty) : string.Empty;
break;
case "VL":
result.VL = !string.IsNullOrEmpty(data) ? data : string.Empty;
break;
case "SP":
result.SP = !string.IsNullOrEmpty(data) ? data : string.Empty;
break;
case "EP":
result.EP = !string.IsNullOrEmpty(data) ? data : string.Empty;
break;
case "PY":
result.PY = !string.IsNullOrEmpty(data) ? data : string.Empty;
break;
case "UR":
result.UR = !string.IsNullOrEmpty(data) ? data : string.Empty;
break;
case "M3":
result.M3 = !string.IsNullOrEmpty(data) ? data : string.Empty;
break;
case "AU":
// AU = Author items of which there can be multiple, note we also replace blank author names with "Unknown"
result.AU.Add(!string.IsNullOrEmpty(data) ? data.Replace(",", string.Empty) : "Unknown");
break;
case "ER":
result.ER = !string.IsNullOrEmpty(data) ? data : string.Empty;
break;
}
inputLine = reader.ReadLine();
}
}
return result;
}
static void Main()
{
List<RecordData> fileRecords = new List<RecordData>();
List<string> headerColumns = new List<string> {"TY", "T1", "JF", "VL", "SP", "EP", "PY", "UR", "M3", "AU", "ER"};
string baseFolder = Directory.GetCurrentDirectory();
string[] fileNames = Directory.GetFiles(baseFolder, "*.txt");
foreach (string fileName in fileNames)
{
fileRecords.Add(ProcessFile(fileName));
}
using (StreamWriter writer = new StreamWriter("consolodated_data.csv"))
{
string headerRow = string.Join(",", headerColumns);
writer.WriteLine(headerRow);
foreach (RecordData fileRecord in fileRecords)
{
string fileLine = string.Empty;
fileLine += fileRecord.TY + ",";
fileLine += fileRecord.T1 + ",";
fileLine += fileRecord.JF + ",";
fileLine += fileRecord.VL + ",";
fileLine += fileRecord.SP + ",";
fileLine += fileRecord.EP + ",";
fileLine += fileRecord.PY + ",";
fileLine += fileRecord.UR + ",";
fileLine += fileRecord.M3 + ",";
fileLine += string.Join("|",fileRecord.AU) + ","; // Join author names with a |
fileLine += fileRecord.ER;
writer.WriteLine(fileLine);
}
}
}
}
}
使用System.Collections.Generic;
使用System.IO;
使用System.Text.RegularExpressions;
命名空间数据解析
{
类记录数据
{
公共字符串TY{get;set;}
公共字符串T1{get;set;}
公共字符串JF{get;set;}
公共字符串VL{get;set;}
公共字符串SP{get;set;}
公共字符串EP{get;set;}
公共字符串PY{get;set;}
公共字符串UR{get;set;}
公共字符串M3{get;set;}
公共列表AU{get;set;}
公共字符串ER{get;set;}
公共记录数据()
{
AU=新列表();
TY=string.Empty;
T1=字符串。空;
JF=string.Empty;
VL=字符串。空;
SP=string.Empty;
EP=字符串。空;
PY=string.Empty;
UR=字符串。空;
M3=字符串。空;
ER=字符串。空;
}
}
班级计划
{