【问题标题】:Transform Flat File without delimiters转换没有分隔符的平面文件
【发布时间】:2015-04-27 06:01:48
【问题描述】:

我想将平面文件 test.txt 转换为平面文件 test-output.txt

方案如下:


输入示例:test.txt
COD/ID:37
PRJ/NAME: Josephy Murphy
PRJ/EMAIL: jmurphy@email.com
PRJ/DESCRIPTION: test37, test37, test37 ...

COD/ID:38
PRJ/NAME: Paul Newman
PRJ/EMAIL: pnewman@email.com
PRJ/DESCRIPTION: test38, test38, test38 ...

.
.

示例输出:test-output.txt(管道分隔,无标签)

37|Josephy Murphy|jmurphy@email.com|test37, test37, test37 ...
38|Paul Newman|pnewman@email.com|test38, test38, test38 ...
.
.


截图链接:
test.txt
test-output.txt

我想将此文件导入 SQL Server。但是文件 test.txt(15,000,000 行)默认情况下不用于带分隔符的导入。

我将使用 SSIS 导入数据,但必须是 CSV 格式或其他带分隔符的格式。

我考虑过使用 REGEX 或 SSIS 脚本组件。我知道带有格式化文本的 SSIS 文件的导入过程,但是这个文件没有格式化。

【问题讨论】:

  • 请将这些文本(或至少是综合样本)放入问题中。并请分享您尝试过的内容。这是必须的。
  • 有什么难度?你被困在哪里了?
  • 我想将此文件导入 SQL Server。但是文件 test.txt(15,000,000 行)默认情况下不用于带分隔符的导入。我将使用 SSIS 导入数据,但必须是 CSV 格式或其他带分隔符的格式。我考虑过使用 REGEX 或脚本组件 SSIS。你有什么建议?
  • 最好的选择是创建一个 Source SSIS 脚本组件,使用例如 Florian Schmidinger 的 Regex 来拆分行,并为每个客户返回一行,然后使用其他 SSIS 步骤来处理行并写入他们到一个文件。 不要使用字符串操作函数,生成的临时字符串会对内存和垃圾回收造成巨大压力

标签: c# regex text ssis


【解决方案1】:

以正则表达式为例:

    class Program
    {
        private static Regex reg = new Regex(@"COD/ID:\s(?<id>\d+)\r\nPRJ/NAME:\s(?<name>.+?)\r\nPRJ/EMAIL:\s(?<email>\S+?@\S+?\.\S+?)\r\nPRJ/DESCRIPTION:\s(?<description>.*?)(?:\n|$)");

        static void Main(string[] args)
        {
            string original = @"
COD/ID: 37
PRJ/NAME: Josephy Murphy
PRJ/EMAIL: jmurphy@email.com
PRJ/DESCRIPTION: test37, test37, test37 ...

COD/ID: 38
PRJ/NAME: Paul Newman
PRJ/EMAIL: pnewman@email.com
PRJ/DESCRIPTION: test38, test38, test38 ...";


            string result = string.Join(
                "\n",
                reg.Matches(original)
                .Cast<Match>()
                .Select(m => string.Format("{0}|{1}|{2}|{3}",m.Groups["id"].Value,m.Groups["name"].Value,m.Groups["email"].Value,m.Groups["description"].Value)));
            Console.WriteLine(result);
        }
    }

编辑

class Program
{
    private static Regex reg = new Regex(@"COD/ID:\s(?<id>\d+)\r\nPRJ/NAME:\s(?<name>.+?)\r\nPRJ/EMAIL:\s(?<email>\S+?@\S+?\.\S+?)\r\nPRJ/DESCRIPTION:\s(?<description>.*?)\r\n");

    static void Main(string[] args)
    {
        StringBuilder intermediateStringBuilder = new StringBuilder();

        using (StreamReader reader = new StreamReader(@"YourInputPath.txt",true))
        {               
            using (StreamWriter writer = new StreamWriter("YourOutputPath.txt"))
            {
                while (reader.Peek() > 0)
                {
                    string line = reader.ReadLine();
                    if (!string.IsNullOrWhiteSpace(line))
                    {
                        intermediateStringBuilder.AppendLine(line);
                    }
                    else
                    {
                        WriteToFile(intermediateStringBuilder, writer);
                    }
                } 
                WriteToFile(intermediateStringBuilder,writer);
            }
        }
    }

    private static void WriteToFile(StringBuilder intermediateStringBuilder, StreamWriter writer)
    {
        Match m = reg.Match(intermediateStringBuilder.ToString());
        writer.WriteLine("{0}|{1}|{2}|{3}", m.Groups["id"].Value, m.Groups["name"].Value, m.Groups["email"].Value, m.Groups["description"].Value);
        intermediateStringBuilder.Clear();
    }
}

【讨论】:

  • 恭喜!使用正则表达式(regex)的完美解决方案。
  • @J.LopesSilvestre 是的,但是有数百万行你可能会遇到内存问题(我刚刚阅读了你的评论)......所以这应该是如何处理较小文件的示例
  • 性能确实受损!然后解决方案将逐行读取并写入文本文件?使用 System.IO 的方法;读取和写入文本文件。
  • @J.LopesSilvestre 这种方法会解析整个内容...用 15M 行你会遇到麻烦...我会尝试实现一个可以处理的解决方案...给我一个几个
  • 恭喜!它工作得很好!谢谢你。只是一个更正。对于那些使用旧版本 .Net 3.5 版的人,需要调整代码才能工作。包含方法: private static bool IsNullOrWhiteSpace (this string value) { if (value == null) return true; return string.IsNullOrEmpty(value.Trim());并且还要更改intermediateStringBuilder.Clear();通过intermediateStringBuilder.Length = 0;
【解决方案2】:

在这种情况下,您可以不使用正则表达式,因为上下文是已知的。

使用这个:

public class EntryN
{
   public string id { get; set; }
   public string name { get; set; }
   public string email { get; set; }
   public string description { get; set; }

   public EntryN()
   {
      this.id = this.name = this.email = this.description = string.Empty;
   }
   public string ToLine()
   { 
       return this.id + "|" + this.name + "|" + this.email + "|" + this.description; 
   }
}

var entries = new List<EntryN>();
using (var sl = new StreamReader(@"c:\YOURPATH.txt", true))
{
    var entry = new EntryN();
    var line = string.Empty;
    while ((line = sl.ReadLine()) != null)
    {
       if (line.StartsWith("COD/ID:"))
          entry.id = line.Substring(8).Trim();
       else if (line.StartsWith("PRJ/NAME:"))
          entry.name = line.Substring(10).Trim();
       else if (line.StartsWith("PRJ/EMAIL"))
          entry.email = line.Substring(11).Trim();
       else if (line.StartsWith("PRJ/DESCRIPTION"))
          entry.description = line.Substring(17).Trim();
      else if (line.Trim() == string.Empty)
      {
          entries.Add(entry);
          entry = new EntryN();
      }
    }
    if (!entry.Equals(new EntryN()))
       entries.Add(entry);
    sl.Close();
}

var resulted = entries.Select(p => p.ToLine()).ToList();

输出:

编辑:另一个没有单独类的代码,将直接编写而不创建额外的字符串:

var id = string.Empty;
var name = string.Empty;
var email = string.Empty;
var description = string.Empty;
using (var sw = new StreamWriter(@"OUTPUT_FILE", false, Encoding.UTF8))
{
    using (var sl = new StreamReader(@"INPUT_FILE", true))
    {
       var line = string.Empty;
       while ((line = sl.ReadLine()) != null)
       {
           if (line.StartsWith("COD/ID:"))
              id = line.Substring(8).Trim();
           else if (line.StartsWith("PRJ/NAME:"))
              name = line.Substring(10).Trim();
           else if (line.StartsWith("PRJ/EMAIL"))
              email = line.Substring(11).Trim();
           else if (line.StartsWith("PRJ/DESCRIPTION"))
              description = line.Substring(17).Trim();
           else if (line.Trim() == string.Empty)
           {
               sw.WriteLine(string.Format("{0}|{1}|{2}|{3}", id, name, email, description));
               id = name = email = description = string.Empty;
            }
        }
        if (!new string[] {id, name, email, description}.Any(p => string.IsNullOrWhiteSpace(p)))
            sw.WriteLine(string.Format("{0}|{1}|{2}|{3}", id, name, email, description));
        sl.Close();
     }
     sw.Close();
 }

【讨论】:

  • 很高兴为您提供帮助。顺便说一句,请将您之前的尝试添加到问题中,即使它们不成功。在询问之前,请始终尝试至少发布一些内容来证明您的努力。否则,您的问题将被关闭,并最终被删除。
  • 好的。感谢您的提示。
  • BTW 的缩写是什么?
  • 由于生成了大量临时字符串,此代码对于大文件存在严重的性能问题。每个SubstringStartsWithTrim 调用都会创建一个新的临时字符串,必须对其进行垃圾回收,从而使CPU 和内存都紧张。 应该 在这里使用正则表达式,因为它不会生成任何临时字符串,从而使其更适合处理大文件。即使按: 拆分也将是一个显着的改进。
  • 另外,最后的字符串连接也会因为临时字符串的生成而影响性能。这可以通过使用String.Join(string,IEnumerable<string>) 来避免,例如:String.Join("|",entries)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-11
  • 1970-01-01
相关资源
最近更新 更多