如何将行拆分为从txt文件到数据表

时间:2015-03-06 02:06:22

标签: c# .net parsing string-split

我有一个客户数据的文本文件,看起来像这样

    :client objects (
    : (ThomasSmith
                :AdminInfo (
                    :client_uid ("{C6DD9C9C-964A-4BE5-30F1-3D64A87F73A6}")
                    :nickName (Tom)
                    )

                :addr ("1234 Pear Street")
                :city (Charlotte)
                :state (NC)
                :zip (12345)
                :phone ("555-555-5555")
                :email ("tom@someemailaddress.com")
                :gender (male)

            )       

    : (Jonathan Thomson
                :AdminInfo (
                    :client_uid ("{C6DD9C9C-964A-4BE5-30F1-3D64A87F73A7}")
                    :nickName (John)
                    )

                :addr ("5678 Apple Street")
                :city ("New York")
                :state (
                    :AdminInfo (
                    :chkpf_uid(:""{ B056A094-3164-42C9-888F-11071C1FCD9B}"")
                    :global_level(1)
                )
)
                :zip (56789)
                :phone ("555-444-6666")
                :email ("John@someemailaddress.com")
            )
    )

我需要能够将每个客户端的部分解析为列表或数据表。我所坚持的是开始在nameofclient上读取文件,并在该客户端结束时停止读取而不从nameofclient2中获取数据。当某个特定的单词或模式出现时,有没有办法停止阅读我的文件? 我不知道如何解决的一个问题是每个客户端可能有不同数量的属性,所以我不能硬编码一些行,我将需要正则表达式为" :( [az]"或类似的东西。理想情况下,我希望这个格式化为类似于此的数据表:

    Name of customer | Attribute    
    ------------------------------
    Customer1        | Address(XXXXXX)
    Customer1        | ZipCode(XXXXXX)
    Customer1        | Etc...
    Customer2        | .....
    Customer2        | .....

无论如何,我对编码很陌生,而且我还没有足够的经验来实现这个目标。这是我试过的:

 public partial class WebForm1 : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Main();
    }

    static void Main()
    {
        ruleset rs = new ruleset();
        System.IO.StreamReader br = new System.IO.StreamReader("f");

        string line = string.Empty;         
        bool GroupTrue = false;
        int numObjects1 = 0;          
        string cGroupName = "";

        while ((line = br.ReadLine()) != null)
        {
            if (line.Contains(":(client_objects"))
            {
                GroupTrue = true;

                string[] tempArray = line.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.None);
                cGroupName = tempArray[tempArray.Length - 1];
            }
            else if (GroupTrue && !Regex.IsMatch(line, "") && (numObjects1 < 50))
            {
                numObjects1 = numObjects1 + 1;

                cGroup cGroup = new cGroup(cGroupName, line);


                rs.addGroups(cGroup);
            }
            else if (GroupTrue && Regex.IsMatch(line, ".*\\b.*"))
            {
                GroupTrue = false;
            }
        }
    }

}

public class cGroup
{
    public string attribute;
    public string groups;

    public cGroup(String cGroupName, String line)
    {
        this.groups = cGroupName;
        this.attribute = line;
    }

}
public class ruleset
{
    //cGroup cResult = new cGroup();
    public List<cGroup> cGroups = new List<cGroup>();
    public void addGroups(cGroup cGroups)
    {
        this.cGroups.Add(cGroups);
    }
}

3 个答案:

答案 0 :(得分:1)

我认为你的意思不是完全停止阅读,而是暂停阅读,然后在前一批中的线上做一些工作。为此,您可以执行以下操作:

public bool MatchesMyCondition(string line) {...}
public void DoSomething(List<string> lines) {...}

List<string> lines = new List<string>();
string line;

System.IO.StreamReader file = new System.IO.StreamReader("myFile.txt");
while((line = file.ReadLine()) != null)
{
    if (MatchesMyCondition(line))
    {
       DoSomething(lines);
       lines.Clear();
    }
    else
    {
        lines.Add(line);
    }
}
//handle the last items
DoSomething(lines);

正如Shenku所说,使用someRegex.IsMatch(line)是查找某条内容的最常用方法,但line.Contains(someSting)也足够了。

答案 1 :(得分:0)

我建议使用Regex处理您的文件,只要您尝试根据模式抓取字符串数据,它就显然是赢家。

不幸的是,要做到正确,继续Regexr进行体验并获得一些参考信息可能相当复杂。

例如,\((.*?)\)会获取您的parenthisis中的所有值。

答案 2 :(得分:0)

我理解对正则表达式的偏见,因为人们不愿意学习基础知识。通过使用这些基本原则(并避免使用正则表达式.*来消费所有)

  • 使用+一个或多个经文*零或更多(仅谨慎使用*)。
  • ( )基本匹配捕获,我们对括号中的内容感兴趣
  • (?<{Name is here}> )命名匹配捕获,以便更轻松地提取匹配数据。
  • [^ ]+ not 设置,直到您点击^之后的字符为止。

因此,根据这些规则,我们在每个规则上构建并在数据中找到我称之为 anchors 的规则。这就是我们可以引导正则表达式解析器并使用命名匹配捕获来使用数据的地方。

模式

这是C#变量中的模式。

string pattern = @"
:\s+\(                 # Anchor text of Operation Start
(?<Name>[^\r\n]+)  # Named capture into `Name` match capture.
[^:]+:AdminInfo[^:]+   # More whitespace to admin and into first admin node.
    (                    # 1 to many admin nodes start.
      :                  # Anchor for admin node
      (?<ADKey>[^\s]+)       # Node key name into `ADKey` match capture
        \s+\(\x22?            # Anchor of `(` and possible quote (\x22) Start
       (?<ADValue>[^\x22\)]+) # Value of admin node
      \x22?\)\s+              # Anchor optional quote and `)` End
     )+                  # 1 to many admin nodes end
 \)                    # Close of Admin Info
 (                   # 1 to many nodes start.
    [^:]+:           # Consume whitespace and `:` anchor
    (?<Key>[^\s]+)      # Node name into match capture group `Key`
        \s+\(\x22?       # Anchor of `(` and possible quote (\x22) start
    (?<Value>[^\x22\)]+) # Value of admin node
      \x22?\)\s+         # Anchor End
  )+            # 1 to many nodes end
\s*\)           # Close of whole operation END";

请注意NameADKeyADValueKeyValue的指定匹配捕获。在按比赛匹配的基础上(每场比赛将是一个人),我们将提取该人的姓名。然后,在ADKeyADValueKeyValue中会有四个指定匹配值的单独列表。那些代表数据的键值对,我们将Zip(您正在使用.net 4对吗?)到键值对的字典中。

C#Linq Logic

// Ignore pattern whitespace only allows us to comment the pattern
// it does not affect regex parsing.
// Explicit capture says only keep the items which fall within `(` and `)` for the final result.
// It is used to streamline the process somewhat for we don't need all the extraneous text/space.
Regex.Matches(text, pattern, RegexOptions.IgnorePatternWhitespace | RegexOptions.ExplicitCapture)
     .OfType<Match>()
     .Select (mt => new
     {
        Name      = mt.Groups["Name"].Value,
        AdminInfo = mt.Groups["ADKey"].Captures
                                      .OfType<Capture>()
                                      .Select (cp => cp.Value)
                                      .Zip(mt.Groups["ADValue"].Captures.OfType<Capture>().Select (cp => cp.Value),
                                           (k,v) => new {key = k, value = v})
                                      .ToDictionary (cp => cp.key, cp => cp.value ),
        Nodes     = mt.Groups["Key"].Captures
                                      .OfType<Capture>()
                                      .Select (cp => cp.Value)
                                      .Zip(mt.Groups["Value"].Captures.OfType<Capture>().Select (cp => cp.Value),
                                           (k,v) => new {key = k, value = v})
                                      .ToDictionary (cp => cp.key, cp => cp.value ),

    })

这会创建单独的数据实体,其中每个匹配都是投影(这是Select项目数据从一种形式投射到另一种形式的实体) NameAdminInfoNodes的属性。 AdminInfoNodes是包含1到多个键值对的字典。根据数据(下面)处理时,这是Linqpad中显示的结果数据

enter image description here

数据

string text = @":client objects (
: (ThomasSmith
            :AdminInfo (
                :client_uid (""{C6DD9C9C-964A-4BE5-30F1-3D64A87F73A6}"")
                :nickName (Tom)
                )

            :addr (""1234 Pear Street"")
            :city (Charlotte)
            :state (NC)
            :zip (12345)
            :phone (""555-555-5555"")
            :email (""tom@someemailaddress.com"")
            :gender (male)

        )

: (Jonathan Thomson
            :AdminInfo (
                :client_uid (""{C6DD9C9C-964A-4BE5-30F1-3D64A87F73A7}"")
                :nickName (John)
                )

            :addr (""5678 Apple Street"")
            :city (""New York"")
            :state (NY)
            :zip (56789)
            :phone (""555-444-6666"")
            :email (""John@someemailaddress.com"")
        )
";

我留给你来处理上面Regex.Matches调用的最终实体结果。