【问题标题】:Index was outside the bounds of the array exception索引超出了数组异常的范围
【发布时间】:2014-05-17 02:29:59
【问题描述】:

这是我从平面文件中获取数据并插入 SQL Server 的代码。它正在生成异常 (Index was outside the bounds of the array)。

string path = string.Concat(Server.MapPath("~/TempFiles/"), Fileupload1.FileName);                       
string text = System.IO.File.ReadAllText(path);               
string[] lines = text.Split(' ');                                  
con.Open();                 
SqlCommand cmd = new SqlCommand();                 
string[] Values = new string[3];                                 
foreach (string line1 in lines)                 
{                     
    Values = line1.Split(';');                                           
    string query = "INSERT INTO demooo VALUES ('" + Values[0] + "','" + Values[1] + "','" + Values[2] + "')";                     
    cmd = new SqlCommand(query,con);                     
    cmd.ExecuteNonQuery();                  
} 

【问题讨论】:

    标签: c# .net indexoutofboundsexception


    【解决方案1】:

    发生异常是因为其中一行的元素少于三个,用分号隔开。即使您将Values 声明为包含三个元素的String 数组,将变量影响到String.Split() 函数的结果也会使这无关紧要:您的数组将具有返回数组的任何长度。如果它更少,你的代码肯定会失败。

    如果它不应该发生,我建议你在代码中做一个断言来帮助你调试:

    // ...
    Values = line1.Split(';');
    // the following will make the debugger stop execution if line.Length is smaller than 3
    Debug.Assert(line1.Length >= 3);
    // ...
    

    作为旁注,我应该提到批量处理INSERT 会更有效率。此外,您声明和重新影响 cmd 变量的方式也不太正确。最后,您应该在您的值上调用String.Replace,以确保任何撇号都加倍。否则,您的代码将受到 SQL 注入攻击。

    【讨论】:

      【解决方案2】:

      关于您的代码在运行时如何表现的一些细节:

      // This line declares a variable named Values and sets its value to 
      // a new array of strings. However, this new array is never used 
      // because the loop overwrites Values with a new array before doing 
      // anything else with it.
      string[] Values = new string[3];                                 
      foreach (string line1 in lines)                 
      {                     
          Values = line1.Split(';');        
      // At this point in the code, whatever was previously stored in Values has been
      // tossed on the garbage heap, and Values now contains a brand new array containing
      // the results of splitting line1 on semicolons.
      // That means that it is no longer safe to assume how many elements the Values array has.
      // For example, if line1 is blank (which often happens at the end of a text file), then
      // Values will be an empty array, and trying to get anything out of it will throw an
      // exception                                   
          string query = "INSERT INTO demooo VALUES ('" + Values[0] + "','" + Values[1] + "','" + Values[2] + "')";                     
          cmd = new SqlCommand(query,con);                     
          cmd.ExecuteNonQuery();                  
      } 
      

      与 Values 不断被覆盖的方式类似,在循环之外创建的 SqlCommand 也永远不会被使用。将这两个声明都放在循环中是安全的。下面的代码做到了这一点,并且还添加了一些错误检查以确保从该行中检索到可用数量的值。它会简单地跳过任何不够长的行 - 如果还不行,那么您可能需要自己创建一些更复杂的错误处理代码。

      foreach(string line in lines) 
      {
          string[] values = line.split[';'];
          if(values.Length >= 3)
          {
              string query = "INSERT INTO demooo VALUES ('" + Values[0] + "','" + Values[1] + "','" + Values[2] + "')";      
              using (SqlCommand command = new SqlCommand(query, con))
              {
                  cmd.ExecuteNonQuery();
              }
          }
      }
      

      最后一点,如果您在 Web 应用程序之类的东西中使用上面的代码,它可能容易受到黑客攻击。想想如果您正在处理一个看起来像这样的文件,可能会向服务器发送什么命令:

      1;2;3
      4;5;6
      7;8;9') DROP TABLE demooo SELECT DATALENGTH('1    
      

      更安全的选择是使用参数化查询,这将有助于防止此类攻击。他们通过将命令与其参数分开来做到这一点,这有助于防止您传入看起来像 SQL 代码的参数的值。如何以这种方式进行设置的示例如下所示:

      string query = "INSERT INTO demooo VALUES (@val1, @val2, @val3);
      using (var command = new SqlCommand(query, con))
      {
          command.Parameters.AddWithValue("@val1", Values[0]);
          command.Parameters.AddWithValue("@val2", Values[1]);
          command.Parameters.AddWithValue("@val3", Values[2]);
          command.ExecuteNonQuery();
      }
      

      【讨论】:

      • +1 用于提及 SQL 注入。但是,OP 问题中的文本由空格分隔(不是您的示例中的换行符),这使得这种攻击更加困难。
      • 其实我相信 OP 做一个批量插入会更好。不过,+1 以获得非常详细的答案。
      【解决方案3】:

      试试这个。

      string path = string.Concat(Server.MapPath("~/TempFiles/"), Fileupload1.FileName);
      string text = System.IO.File.ReadAllText(path);
      string[] lines = text.Split(' ');
      con.Open();
      string[] Values;
      foreach (string line1 in lines)
      {
          Values = line1.Split(';');
      
          if (Values.Length >= 3)
          {
              string query = "INSERT INTO demooo VALUES ('" + Values[0] + "','" + Values[1] + "','" + Values[2] + "')";
          }
          else
          {
            //Some error occured
          }
      
          using (var cmd = new SqlCommand(query,con))
          {
              cmd.ExecuteNonQuery();
          }
      }
      

      【讨论】:

      • 你的意思是>=,当然。
      • @MrLister 即使这样,恕我直言,这仍然是个坏建议。这只会悄悄地跳过少于 4 个值的行,而不是提供有关文件可能已损坏或其他情况的线索。我认为可以安全地假设一行的值少于四个分号分隔的值根本不应该发生。
      • 这就是为什么会有 else 部分。您可以在 else 子句中添加该错误消息。
      • @MoraRockey 啊,没错。我错过了那部分,我很抱歉。我会冒昧地重新格式化您的答案。 +1
      • @Crono 但是你还是应该说>=4 (或者切换ifelse 中的操作)。顺便说一句,应该是 3,而不是 4。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-05
      • 2012-11-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多