我需要将大量文本文件加载到数据库中。它们不是采用更常见的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/01/
UR - http://dx.doi.org/10.1098/rstb.1887.0008
M3 - doi:10.1098/rstb.1887.0008
AU - Ward, H.
ER -
其中每一行是不同的字段,字段名称由前导字符表示。
我想要做的是将每一行加载到记录中的相应字段中。我想通过pentaho这样做,任何人都知道如何实现这一目标。文本输入步骤设置为csv输入。
答案 0 :(得分:5)
该文本示例看起来非常熟悉.....
急忙检查
如果那个文本样本是我认为它是“皇家科学社会期刊描述文件”那么你将无法编写一个pentaho脚本来解析这些东西。
我去过那里,尝试过,这很痛苦,非常痛苦。
为什么?
很多事情。
首先,文件格式是 NOT 严格检查,所以你会查找一些没有2个字符ID后跟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可靠处理的合适状态。对此,我拖了一些C#,我写了一个文件夹,里面装满了这些文本文件,然后将它们变成一个扁平的CSV。
生成的CSV并不完美,仍然需要进行少量的调整,但它会让你获得99.9%的方式,并且使用pentaho比使用源文件本身更容易处理生成的文件。
代码是相当通用的C#所以它应该在windows和单声道上编译(虽然我不得不承认,我不会在后面测试它)
这是代码:
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);
}
}
}
}
}
编译文件,然后将生成的EXE复制到所有txt文件所在的文件夹中并运行它。
现在,在你们任何一个C#纯粹主义者开始跳入这里并挑选我的代码之前,请注意这个......
A)它是在不久前写的,作为解决我遇到的问题的快速工具,它从来就不是生产代码。
B)是的我知道有更好的方法可以做一些事情,比如字符串构建器和连接,参见A点
C)如果你确实进入并开始在我的代码中挑选错误,而不是试图用他的问题帮助OP(就像我有的那样)那么你只需要一个没有更好的事情的douchebag。
D)你试图用我的代码指出的任何错误都不会导致我失眠,因为坦率地说,我并不在意。它有效,我知道它有效(因为它完成了我当时需要它做的事情)而这就是我所困扰的。回到OP的问题。
正如我所说,它并不完美,你必须进行少量的编辑。
你需要做任何编辑,你会想要使用纯文本编辑器,如果内存服务的某些描述行非常长并且将超过excel中可用的最大列宽,从而导致一行' ###################“
您当然可以使用pentaho将CSV直接导入数据库,然后只需编辑其中的记录以整理数据(我就是这样做的)
最后要注意的是,作者姓名(或至少在有多个名称的地方)使用|来加入将字符放入“AU”标记下的一个CSV字段中,因此当您进一步处理它们时,您可能需要考虑将它们放在自己的表中,并使用自己的ID和指向源记录的外键对它们进行反规范化
如果你无法编译它(或者不想编译它)请给我说一句,告诉我你的平台,我会为你构建它并发送给你二进制文件。
答案 1 :(得分:3)
您需要使用Row Denormaliser步骤对行进行非规范化。
步骤:
使用文本文件输入将数据读入一个字段
使用字段拆分器拆分“ - ”
对组字段中的数据进行排序(我没有在您的示例中标识组ID)。如果没有可用的组ID,那么希望每组有固定的行数,那么您可以添加计算的组ID
将行传递给Row Normaliser并指定以下内容:
4.1。将您的组ID字段添加到组ID中的组ID。
4.2。在字段中为您需要的每一行添加目标字段名称。我在样品中添加了11个来自TY,T1,JF等。它们可以是您选择的任何名称。
4.3。对于每个新字段指定值字段名称,您分配给第二个字段的字段由分割产生。在我的分段器中的示例中,我分配了两个字段--fld_hdr和fld_content。我的值字段包含fld_content fld。
4.4。指定每行的字段类型和可选的剩余列。
我创建了一个示例,但没有看到上传ktr文件的位置。