2012-04-04 5 views
3

SQLスクリプトから各挿入行を取得するための正規表現を作成しようとしています。 Regex Heroで.NET Regex Testerを使用するとI get my expected 8 matches。しかし、このsnippitをコンソールアプリケーションとして実行すると、一致するものが返されません。.NET正規表現エンジンは一致を返しませんが、私は予想しています8

const string text = 
@"INSERT INTO [AdminPrefs] ([SpayClinic] , [VaxClinic] , [ShelterClinic] , [DateModified] , [Prefix] , [UpdateCounter] , [LockedRecs] , [dbName] , [Timer] , [MedCtrClinic] , [OtherClinic] , [Da2PPPx] , [Da2PPEPx] , [FVRCPPx] , [FVRCPEPx] , [FELVTPx] , [FELVTEPx] , [FELVVPx] , [FELVVEPx] , [HWTPx] , [HWTEPx] , [RabiesPx] , [RabiesEPx] , [FIVTest] , [FIVTestE] , [OnePlusChar] , [XSHWMPx] , [XSHWMEPx] , [SHWMPx] , [SHWMEPx] , [MHWMPx] , [MHWMEPx] , [LHWMPx] , [LHWMEPx] , [DebuggerOn] , [PayThisAmount] , [free6] , [XSHWMPillPx] , [XSHWMPillEPx] , [SHWMPillPx] , [SHWMPillEPx] , [MHWMPillPx] , [MHWMPillEPx] , [LHWMPillPx] , [LHWMPillEPx] , [free7] , [free8] , [free9] , [XSPMPx] , [XSPMEPx] , [SPMPx] , [SPMEPx] , [MPMPx] , [MPMEPx] , [LPMPx] , [LPMEPx] , [ReceiptFooter] , [MonthsUntilBenefits] , [free12] , [XSPMPillPx] , [XSPMPillEPx] , [SPMPillPx] , [SPMPillEPx] , [MPMPillPx] , [MPMPillEPx] , [LPMPillPx] , [LPMPillEPx] , [free14] , [ClinicName] , [ShelterName] , [ShelterAbbr] , [Address1] , [Address2] , [City] , [State] , [ZipCode] , [MainPhone] , [MainFax] , [SplashPict] , [free17] , [free18] , [LicenseNo] , [SerialNo] , [free20] , [free21] , [free22] , [VLogCC] , [SNLogCC] , [free23] , [free24] , [free25] , [AgeAndBDay] , [free26] , [free27] , [free28] , [CurrRouteNum]) 
VALUES 
(12 , 7 , 0 , '0000/00/00 00:00:00:00' , '' , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , X'5443503408' , 0 , 0 , '' , 0 , 0 , 0 , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0), 
(15 , 53 , 0 , '0000/00/00 00:00:00:00' , '' , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , X'5443503408' , 0 , 0 , '' , 0 , 0 , 0 , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0), 
(20 , 216 , 0 , '0000/00/00 00:00:00:00' , '' , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , X'5443503408' , 0 , 0 , '' , 0 , 0 , 0 , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0), 
(16 , 8 , 0 , '0000/00/00 00:00:00:00' , '' , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , X'5443503408' , 0 , 0 , '' , 0 , 0 , 0 , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0); 

INSERT INTO [AdminPrefs] ([SpayClinic] , [VaxClinic] , [ShelterClinic] , [DateModified] , [Prefix] , [UpdateCounter] , [LockedRecs] , [dbName] , [Timer] , [MedCtrClinic] , [OtherClinic] , [Da2PPPx] , [Da2PPEPx] , [FVRCPPx] , [FVRCPEPx] , [FELVTPx] , [FELVTEPx] , [FELVVPx] , [FELVVEPx] , [HWTPx] , [HWTEPx] , [RabiesPx] , [RabiesEPx] , [FIVTest] , [FIVTestE] , [OnePlusChar] , [XSHWMPx] , [XSHWMEPx] , [SHWMPx] , [SHWMEPx] , [MHWMPx] , [MHWMEPx] , [LHWMPx] , [LHWMEPx] , [DebuggerOn] , [PayThisAmount] , [free6] , [XSHWMPillPx] , [XSHWMPillEPx] , [SHWMPillPx] , [SHWMPillEPx] , [MHWMPillPx] , [MHWMPillEPx] , [LHWMPillPx] , [LHWMPillEPx] , [free7] , [free8] , [free9] , [XSPMPx] , [XSPMEPx] , [SPMPx] , [SPMEPx] , [MPMPx] , [MPMEPx] , [LPMPx] , [LPMEPx] , [ReceiptFooter] , [MonthsUntilBenefits] , [free12] , [XSPMPillPx] , [XSPMPillEPx] , [SPMPillPx] , [SPMPillEPx] , [MPMPillPx] , [MPMPillEPx] , [LPMPillPx] , [LPMPillEPx] , [free14] , [ClinicName] , [ShelterName] , [ShelterAbbr] , [Address1] , [Address2] , [City] , [State] , [ZipCode] , [MainPhone] , [MainFax] , [SplashPict] , [free17] , [free18] , [LicenseNo] , [SerialNo] , [free20] , [free21] , [free22] , [VLogCC] , [SNLogCC] , [free23] , [free24] , [free25] , [AgeAndBDay] , [free26] , [free27] , [free28] , [CurrRouteNum]) 
VALUES 
(26 , 5 , 0 , '0000/00/00 00:00:00:00' , '' , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , X'5443503408' , 0 , 0 , '' , 0 , 0 , 0 , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0), 
(18 , 12 , 0 , '0000/00/00 00:00:00:00' , '' , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , X'5443503408' , 0 , 0 , '' , 0 , 0 , 0 , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0), 
(9 , 10 , 0 , '0000/00/00 00:00:00:00' , '' , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , X'5443503408' , 0 , 0 , '' , 0 , 0 , 0 , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0), 
(2 , 72 , 0 , '0000/00/00 00:00:00:00' , '' , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , '' , '' , '' , '' , '' , '' , '' , '' , '' , '' , X'5443503408' , 0 , 0 , '' , 0 , 0 , 0 , 0 , '' , '' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0); 
"; 

static void Main(string[] args) 
{ 
    string query = @"^\(.*?\)(,|;)$"; 

    var matches = Regex.Matches(text, query, RegexOptions.Singleline | RegexOptions.Multiline); 

    Console.WriteLine("Expected Matches: 8"); 
    Console.WriteLine("Matches Found: {0}", matches.Count); 

    Console.ReadLine(); 
} 

私のオプションは、ウェブサイトと私のコード(複数行と単一行)のために、全く同じであるあらゆるそれらは同じ.NETの正規表現エンジンを使用しなければならないので、2つの間の差異の原因となっているもの?


最終結果:

これらすべての好奇心が私の最後の正規表現は、R & D(ぼったくりと展開)のためにこれを使用することを計画して、すべてのものに

@"(?<=^\()      # The beginning of a line followed by a (
((('(?<c>.*?)'(?!')(?=[\s\)])) | # Text string in SQL supports line breaks 
    (?<c>-?[\d\.]+) |    # Any numbers 
    (X'(?<c>[0-9a-f]*)')   # Something formatted like X'abcdef' 
)(\s,\s)?      # Spaces and commas between the records 
)+        # Repeat the pattern at least one time 
(?=(?<!'')\)[;,]\r?$)   # The End of the line ending with); or), and not immediately proceeded by ''";  

注だったために開発は非常に規則的なので、これは私のSQLのためにのみ動作します。私のサードパーティのプログラムでは生成されなかったSQLを使用する場合に対処する必要のない多くのエッジケースを処理するために調整が必要になります。

ここにパーサーの解析コードの完全なコードがあります。うまくいけば、それは類似のものにこだわった他の誰かを助けるでしょう。

foreach (var tableFolder in Directory.GetDirectories(_exportFolder)) 
{ 
    //Popluate the schema of the DataTable 
    DataTable table = new DataTable(); 
    using (SqlDataAdapter ada = new SqlDataAdapter(String.Format("Select top 0 * from [{0}]", Path.GetFileName(tableFolder)), conn)) 
    { 
     ada.Fill(table); 
    } 

    //All of the files to import for this table 
    string[] filePaths = Directory.GetFiles(tableFolder, "*.sql"); 

    foreach (string file in filePaths) 
    { 
     string text; 
     using (var txtRdr = new StreamReader(file)) 
     { 
      text = txtRdr.ReadToEnd(); 
     } 

     const string recordRegex = 
         @"(?<=^\()      #The begining of a line followed by a (
         ((('(?<s>.*?)'(?!')(?=[\s\)])) | # Something formatted like 'some text' supports line breaks 
          (?<n>-?[\d\.]+) |   # Any numbers 
          (X'(?<h>[0-9a-f]*)')   # Something formatted like X'abcdef' 
          )(\s,\s)?     # Spaces and commas between the records 
         )+        # Repeat the pattern at least one time 
         (?=(?<!'')\)[;,]\r?$)   # The End of the line ending with); or), and not immedatly proceded by ''";    

     //Creates one match per row in the database 
     var records = Regex.Matches(text, recordRegex, RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.IgnorePatternWhitespace | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); 

     const string headerRegex = @"^INSERT\sINTO\s\[[\w_\-\s]+\]\s\(\s(?:\[([\w_\-\s]+)\]\s(?:,\s)?)+\)"; 
     var header = Regex.Match(text, headerRegex).Groups[1].Captures.Cast<Capture>().ToArray(); 

     foreach (Match record in records) 
     { 
      //Due to how we captured the 3 groups we had to put them back in order in one list. 
      var columns = record.Groups.Cast<Group>() 
           .Skip(1) //Groups[0] contins the entire record. 
           .SelectMany(group => group.Captures.Cast<Capture>()) //Flattens all of the captures in the three groups in to one list 
           .OrderBy(capture => capture.Index) //Reorder the combined list as the SelectMany will not be outputting the correct order. 
           .ToArray(); 

      DataRow row = table.NewRow(); 
      for (int i = 0; i < columns.Length; i++) 
      { 
       Type columnType = table.Columns[header[i].Value].DataType; 
       if (columnType == typeof(String)) 
       { 
        row[header[i].Value] = columns[i].Value; 
       } 
       else if (columnType == typeof(Int32)) 
       { 
        row[header[i].Value] = Convert.ToInt32(columns[i].Value); 
       } 
       else if (columnType == typeof(Double)) 
       { 
        row[header[i].Value] = Convert.ToDouble(columns[i].Value); 
       } 
       else if (columnType == typeof(Boolean)) 
       { 
        if (columns[i].Value == "0") 
         row[header[i].Value] = false; 
        else if (columns[i].Value == "1") 
         row[header[i].Value] = true; 
        else 
         throw new InvalidDataException(); 
       } 
       else if (columnType == typeof(Int16)) 
       { 
        row[header[i].Value] = Convert.ToInt16(columns[i].Value); 
       } 
       else if (columnType == typeof(Byte[])) 
       { 
        row[header[i].Value] = StringToByteArray(columns[i].Value); 
       } 
       else 
       { 
        throw new NotImplementedException(); 
       } 

      } 
      table.Rows.Add(row); 
     } 

     using (var bulkCopy = new SqlBulkCopy(conn)) 
     { 
      bulkCopy.DestinationTableName = Path.GetFileName(tableFolder); 
      bulkCopy.BulkCopyTimeout = 0; 
      bulkCopy.WriteToServer(table); 
     } 
    } 
} 

アップデート:すべての同じ名前の.NETの正規表現エンジンは私のためにそれらを組み合わせたにcaputreグループをリネームのことで

var columns = record.Groups[1].Cast<Group>().Skip(1).SelectMany(group => group.Captures.Cast<Capture>()).OrderBy(capture => capture.Index).ToArray(); 

を簡素化

var columns = record.Groups[1].Captures.Cast<Capture>().ToArray(); 
+0

、 '(、|;)' '[;、]'と書くほうが、常に1文字だけであることをエンジンに知らせることができます。 – Blindy

+0

@Blindyありがとう、私は私のコピーを更新しました。 –

答えて

6

Regex Heroページで "CrLfが行末をマークする"設定を切り替えると、8行の一致が停止することに注意してください。これは問題の原因を突き止める手掛かりです。

C#コードでは、リテラル文字列内の改行がCR/LFペア("\r\n")としてエンコードされます。正規表現内の$(複数行モードの行末に一致)は、\n文字にのみ一致します。したがって、正規表現では考慮されない最後のカンマ(またはセミコロン)の間に余分な\r文字があり、一致が失敗します。

あなたはこの問題に対処することができいくつかの方法が含まれます:

  1. ストリップキャリッジリターン:text = text.Replace("\r\n", "\n");、または
  2. マッチキャリッジリターン:ところでstring query = @"^\(.*?\)(,|;)\r$";
+0

を入力するか、パターンの行頭と行末の文字をドロップします。彼らはすでにあなたがparentheseesの間にちょうどグラブを教えているので、不要です。入れ子のparentheseesはありません –

+0

私はCrLfボタンがそれを説明した疑いがあったが、私はそれを修正する方法を知らなかった、\ r問題を修正する。 –

+0

@booこの単純な例では、このクエリで1.4GBのsqlファイルを解析していません。他のファイルに存在する可能性があります。 –

関連する問題