【问题标题】:C# removing unicode characterC# 删除 unicode 字符
【发布时间】:2015-10-27 13:21:19
【问题描述】:

我正在尝试使用 C# sqlBulkCopy 类将 Excel 工作表中的文本数据插入到 MS SQL 表中。我遇到的问题是我收到了臭名昭著的 Received an invalid column length from the bcp client for colid 6 错误。后来我发现真正的问题是注释文本中的 Excel Unicode 格式。如果我只是在 Excel 单元格中输入注释,则数据插入有效,否则将失败。这是我尝试在我的 C# 脚本中使用的代码:

 SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(DBconn.ConnectionString);
            {
                sqlBulkCopy.DestinationTableName = "##MasterFileTemp";



                foreach (DataColumn dc in MasterFileTemp.Columns)
                {
                    for (int j = 1; j < MasterFileTemp.Rows.Count - 1; j++)
                    {
                        if (MasterFileTemp.Rows[0][dc].ToString() == "Notes")
                        {

                            int pos = dc.Ordinal;
                            string dataText = Regex.Replace(MasterFileTemp.Rows[j][pos].ToString(), @"[^\u0000-\u007F]", string.Empty);
                            MasterFileTemp.Rows[j][pos] = dataText;
                            MasterFileTemp.AcceptChanges();
                            MessageBox.Show(MasterFileTemp.Rows[j][pos].ToString());


                        }
                    }
                }
                sqlBulkCopy.WriteToServer(MasterFileTemp);

这是 Excel 文本数据的样子,注意 Excel 生成的前导和尾随双引号:

" - 如果需要更多信息,请在工作空间内的“服务订单”选项卡上联系合作伙伴请求者。对于任何其他操作问题,请给 James 发送电子邮件

**服务请求包括投资组合管理(观察名单)、止赎、止赎前、其他拥有的房地产 (OREO)、仅资产估值(无贷款)

"

string strCn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filePath + ";Extended Properties=\"Excel 12.0;HDR=NO;IMEX=1;\";";
string GetExcelData = "Select * From [" + tabName + "A23:Z100]";
OleDbConnection cn = new OleDbConnection(strCn);
OleDbDataAdapter objAdapter2 = new OleDbDataAdapter(GetExcelData, cn);
                    DataSet ds2 = new DataSet();
                    objAdapter2.Fill(ds2, "dSheet1");
                    DataTable dt2 = ds2.Tables["dSheet1"];


      Here the entired code: 

命名空间 ST_426cda87cffe4ef6a10722ecf5f7fe65.csproj { [System.AddIn.AddIn("ScriptMain", Version = "1.0", Publisher = "", Description = "")] 公共部分类 ScriptMain:Microsoft.SqlServer.Dts.Tasks.ScriptTask.VSTARTScriptObjectModelBase {

    #region VSTA generated code
    enum ScriptResults
    {
        Success = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Success,
        Failure = Microsoft.SqlServer.Dts.Runtime.DTSExecResult.Failure
    };
    #endregion

    public void Main()
    {

        Microsoft.Office.Interop.Excel.Application xlApp = new Microsoft.Office.Interop.Excel.Application();
        Microsoft.Office.Interop.Excel.Workbook excelBook = xlApp.Workbooks.Open(Dts.Variables["User::MasterFileTemplate"].Value.ToString(),
             0, false, 5, "", "", false, Excel.XlPlatform.xlWindows, "", true, false, 0, true, false, false);

        String[] excelSheets = new String[excelBook.Worksheets.Count];
        int z = 0;
        foreach (Microsoft.Office.Interop.Excel.Worksheet wSheet in excelBook.Worksheets)
        {
            excelSheets[z] = wSheet.Name;
            z++;
        }
        excelBook.Close(false, Dts.Variables["User::MasterFileTemplate"].Value.ToString(), Missing.Value);
        xlApp.Quit();
        process_worksheets(excelSheets);
    }

    public void process_worksheets(string[] wsheets)
       {
        int r;            
        string[] vars = new string[1];
        string field;
        string filePath = (Dts.Variables["User::MasterFileTemplate"].Value.ToString());
        string DataServer = (Dts.Variables["User::DataServer"].Value.ToString());
        string strCn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + filePath + ";Extended Properties=\"Excel 12.0;HDR=NO;IMEX=1;\";";


          for (r=0; r < wsheets.Length; r++)
               {
                string tabName = wsheets[r].ToString() + "$";
                string GetExcelDate = "Select * From [" + tabName + "A15:B16]";   //This is the Excel line number for Prod Completed date and Completed by
                string GetExcelData = "Select * From [" + tabName + "A23:Z100]";  //This is the Excel line number where the header columns and data start   

                OleDbConnection cn = new OleDbConnection(strCn);
                SqlConnection DBconn = new SqlConnection();
                DBconn.ConnectionString = "Data Source="+DataServer + ";Initial Catalog=FNC;" + "Integrated Security=SSPI";

                OleDbDataAdapter objAdapter = new OleDbDataAdapter(GetExcelDate, cn);

                // This Dataset contains the header columns and data
                DataSet ds = new DataSet();
                objAdapter.Fill(ds, "dSheet1");
                DataTable dt = ds.Tables["dSheet1"];

                ///****  Parse Excel Serial Date  ***/
                string dtt = (dt.Rows[0][1].ToString());
                DateTime signoffDte = DateTime.Parse(dtt);
                DateTime currentDte = System.DateTime.Today;

                /// Check to see if Production sign-off date is less than current date and signed by is empty
                if ((signoffDte > currentDte) || (dt.Rows[1][1].ToString() == ""))
                {
                 //   MessageBox.Show(tabName.ToString() + "Date requirment Failed..processing next");
                    continue;  //Skip worksheet if Production signoff date or signature is invalid
                }
                else
                {
                    //This Dataset contains the header columns and data
                    OleDbDataAdapter objAdapter2 = new OleDbDataAdapter(GetExcelData, cn);
                    DataSet ds2 = new DataSet();
                    objAdapter2.Fill(ds2, "dSheet1");
                    DataTable dt2 = ds2.Tables["dSheet1"];


                    DataTable dth = dt2.Clone();
                    dth.ImportRow(dt2.Rows[0]);

                    /*** Create Master File Temp Table from Excel Template source file ***/
                    CreateTempTableAndBulkCopyData(dt2,DBconn);

                    /*****************************************************************************/
                    /*   Loop thru Excel Template File and only select the first row (Headers)   */
                    /*****************************************************************************/

                    for (int i = 0; i < 1; i++)  //Gets first row "A1:Z1" (column headers of excel spreadsheet)
                    {
                        // y=3 is static and must not be changed. This sets Partner_org_PK,Partner_ID,Partner_Name as key columns to perform SQL JOIN on
                        for (int y = 3; y < dth.Columns.Count; y++)
                        {
                            field = dth.Rows[0][y].ToString();
                            vars[0] = field;
                           UpdateMasterFileTable(DBconn, vars, dth); // Performs an update to the Partner Profile Table via a join on the Master File Temp table 

                        }
                        UpdateValidation(DBconn, dth, tabName);
                    }
                    ds.Clear();
                    ds2.Clear();
                    dt.Clear();
                    dth.Clear();

                    cn.Close();
                    DBconn.Close();
                    Dts.TaskResult = (int)ScriptResults.Success;
                }
                MessageBox.Show("Processed......" + tabName.ToString());
                System.Threading.Thread.Sleep(3000);
            }
 }
     /**************************************************************************************************/
     /*   Creates Master File Global Temp Table ###MasterFileTemp and Bulk Copy Excel data into it   */
     /**************************************************************************************************/
    public static void CreateTempTableAndBulkCopyData(DataTable dt2, SqlConnection DBconn)
    {
        DataTable MasterFileTemp = dt2;



        string createTempTable = "CREATE TABLE ##MasterFileTemp(";
        foreach (DataColumn dc in MasterFileTemp.Columns)
        {
            createTempTable += MasterFileTemp.Rows[0][dc] + " Varchar(255),";
        }
        createTempTable = createTempTable.Remove(createTempTable.Length - 1);   //remove trailing, unecessary comma
        createTempTable += ")"; // cap-off with ")" to complete the CREATE ##TEMP TABLE DDL
        {

            //Create temp table command
            SqlCommand command = new SqlCommand(createTempTable, DBconn);
            DBconn.Open();
            command.ExecuteNonQuery();
            MessageBox.Show(createTempTable.ToString());

            //Copy the DataTable to SQL Server Table using SqlBulkCopy
            SqlBulkCopy sqlBulkCopy = new SqlBulkCopy(DBconn.ConnectionString);
            {
                sqlBulkCopy.DestinationTableName = "##MasterFileTemp";

                foreach (DataColumn dc in MasterFileTemp.Columns)
                {
                    for (int j = 1; j < MasterFileTemp.Rows.Count - 1; j++)
                    {
                        if (MasterFileTemp.Rows[0][dc].ToString() == "Notes")
                        {

                            int pos = dc.Ordinal;
                            string dataText = MasterFileTemp.Rows[j][pos].ToString().Replace("\r\n", String.Empty);
                            MasterFileTemp.Rows[j][pos] = dataText;
                            MasterFileTemp.AcceptChanges();
                            //MessageBox.Show(MasterFileTemp.Rows[j][pos].ToString());

                        }
                    }
                }
                sqlBulkCopy.WriteToServer(MasterFileTemp);

            }
        }
    }
    /**************************************************************************************************/
    /*   Performs an up to the Partner Profile Table via a UPDATE join on the Master File Temp table  */
    /**************************************************************************************************/
    public void UpdateMasterFileTable(SqlConnection DBconn, string[] vars, DataTable dth)
        {
         string[] upvariable = vars;
         string sqlUpate = "UPDATE [dbo].[xstg_Partner_Profile]" +
         " SET [dbo].[xstg_Partner_Profile]." + upvariable[0] + "=##MasterFileTemp." + upvariable[0] +
         " FROM ##MasterFileTemp" +
         " WHERE [dbo].[xstg_Partner_Profile].Partner_Id= ##MasterFileTemp." + dth.Rows[0][1].ToString() +
         " AND [dbo].[xstg_Partner_Profile].Partner_Name= ##MasterFileTemp." + dth.Rows[0][2].ToString();
          SqlCommand command = new SqlCommand(sqlUpate, DBconn);
           command.ExecuteNonQuery();
          MessageBox.Show(sqlUpate.ToString());

         }
    /**************************************************************************************************/
    /*   Performs the update validation against production 90100 UI Report and creates Excel mismatch */
    /*   output for each worksheet tab in masterfileupdate template
    /**************************************************************************************************/
    public void UpdateValidation(SqlConnection DBconn, DataTable dth, string tabName)
    {
        string SelectSQL;
        string SelectFields=null;
        for (int x = 3; x < dth.Columns.Count; x++)
        {
           SelectFields += " p2." +dth.Rows[0][x]+ ", ";
        }
        SelectFields = SelectFields.Remove(SelectFields.Length - 2);  //remove trailing comma
        SelectSQL = "SELECT p2.Partner_ID, p2.Partner_Name,";
        SelectSQL += SelectFields;
        string ValidationSQL = " FROM (select * from dbo.Partner_Profile_CMS_Settings) p1" +
                               " FULL OUTER JOIN (Select * from dbo.xstg_Partner_Profile) p2" +
                               " ON p1.Partner_ID = p2.Partner_ID and p1.Partner_Name=p2.Partner_Name" +
                               " WHERE";

        SelectSQL += ValidationSQL; //Append select statement as one
        string ValidationSQLWhere=null;
        for (int y = 3; y < dth.Columns.Count; y++) //loop through data columns to get columns for update - mismatch. This is dynamic and makes up the Where clause
        {
            ValidationSQLWhere += " (P1."+dth.Rows[0][y]+" <> p2."+dth.Rows[0][y]+") OR";
        }
        ValidationSQLWhere = ValidationSQLWhere.Remove(ValidationSQLWhere.Length - 2);  //Remove "OR" two characters from the select statement where clause
        SelectSQL += ValidationSQLWhere; //Append Where clause string to main Select string
        MessageBox.Show("Validating... " + tabName); //Display entire string

        //Build SQL connection to run mismatch query, passing in SELECT statement above
        SqlDataAdapter VSAdapter = new SqlDataAdapter(SelectSQL, DBconn);
        // This Dataset contains Vaildate data
        DataSet validateDs = new DataSet();
        VSAdapter.Fill(validateDs, "VSheet1");
        DataTable validationTemp = validateDs.Tables["VSheet1"];
        String currentDate = DateTime.Now.ToString("MMddyyyy");
        String outputStatus="Validation is 100% accurate";


        /* Set up Excel workbook instance and loop through each worksheet avaialble file output */
        /****************************************************************************************/
        Excel.Application oXL = new Excel.ApplicationClass();
        oXL.DisplayAlerts = false;
        Excel.Workbooks oWBs = oXL.Workbooks;
        Excel.Workbook oWB = null;
        Excel.Worksheet oSheet;
        tabName = tabName.Remove(tabName.Length-1);  //remove training '$' from mismatch worksheets

        /* If the mismatch output file does not exist, create mismatch file and write out first worksheet */
        if (!File.Exists(Dts.Variables["User::MismatchOutputFile"].Value.ToString()))
        {


            oWB = oXL.Workbooks.Add(Missing.Value);
            // Get the active sheet 
            oSheet = (Excel.Worksheet)oWB.Worksheets.get_Item(1);
            oSheet.Name = tabName;

            int rowCount = 0;
            if (validationTemp.Rows.Count >= 1)
            {
                foreach (DataRow dr in validationTemp.Rows)
                {
                    rowCount += 1;
                    for (int i = 1; i < validationTemp.Columns.Count + 1; i++)
                    {
                        // Add the header time first only
                        if (rowCount == 2)
                        {
                            oSheet.Cells[1, i] = validationTemp.Columns[i - 1].ColumnName;
                        }                           
                        oSheet.Cells[rowCount, i] = dr[i - 1].ToString();
                    }
                }
            }
            else
            {
               // MessageBox.Show("Validation is 100% accurate");
                oSheet.Cells[rowCount, 2] = outputStatus.ToString();
            }
            oWB.SaveAs(Dts.Variables["User::MismatchOutputFile"].Value.ToString(), Excel.XlFileFormat.xlWorkbookNormal,
               Missing.Value, Missing.Value, Missing.Value, Missing.Value,
               Excel.XlSaveAsAccessMode.xlShared,
               Missing.Value, Missing.Value, Missing.Value,
               Missing.Value, Missing.Value);
         }
        else /* If mismatch file already exists, loop thru and append additional worksheets */
        {
           System.Threading.Thread.Sleep(1000);
            try
            {
                oXL.DisplayAlerts = false;
                Excel.Sheets xlSheets = null;
                oWB = oXL.Workbooks.Open(Dts.Variables["User::MismatchOutputFile"].Value.ToString(),
                         Missing.Value, Missing.Value, Missing.Value, Missing.Value,
                         Missing.Value, Missing.Value, Missing.Value, Missing.Value,
                         Missing.Value, Missing.Value, Missing.Value,
                         Missing.Value, Missing.Value, Missing.Value);
                xlSheets = (Excel.Sheets)oWB.Sheets;
                oSheet = (Excel.Worksheet)xlSheets.Add(Type.Missing, xlSheets[1], Type.Missing, Type.Missing);
                oSheet.Name = tabName;
                int rowCount = 0;
                if (validationTemp.Rows.Count > 1)
                {
                    foreach (DataRow dr in validationTemp.Rows)
                    {
                        rowCount += 1;
                        for (int i = 1; i < validationTemp.Columns.Count + 1; i++)
                        {
                            // Add the header time first only
                            if (rowCount == 2)
                            {
                                oSheet.Cells[1, i] = validationTemp.Columns[i - 1].ColumnName;
                            }
                            oSheet.Cells[rowCount, i] = dr[i - 1].ToString();
                        }
                    }
                 }else
                        {
                           // MessageBox.Show("Validation is 100% accurate");
                            oSheet.Cells[rowCount, 2] = outputStatus.ToString();
                        }   
                oWB.SaveAs(Dts.Variables["User::MismatchOutputFile"].Value.ToString(), Excel.XlFileFormat.xlWorkbookNormal,
                   Missing.Value, Missing.Value, Missing.Value, Missing.Value,
                   Excel.XlSaveAsAccessMode.xlExclusive,
                   Missing.Value, Missing.Value, Missing.Value,
                   Missing.Value, Missing.Value);
                oWB.Close(true, Dts.Variables["User::MismatchOutputFile"].Value.ToString(), Type.Missing);
                oWBs.Close();
                oXL.Quit();
                Marshal.ReleaseComObject(oXL);
            }
            finally
            {

            }
        }

    }

}

}


【问题讨论】:

  • 如果你尝试插入一个大于表格列长度的值,你会得到一个错误。这没有什么臭名昭著的。要么放大列,要么检查您的数据。 .NET 始终使用 Unicode,因此试图找出 Unicode 错误是没有意义的。此外,Unicode 根本与 formatting 无关。是否有理由不将其作为this question 的副本关闭?
  • 如果数据有问题,你应该检查读取Excel文件的代码。但是,您还没有提供此信息,因此无法提供帮助。 如何你正在阅读该文件?使用 Jet 驱动程序? EPPlus?使用 Excel 互操作?如果您将其作为文本阅读,则根本没有 Excel 文件 - Excel 使用二进制文件格式
  • Excel 不生成引号。正如我所说,它是一种二进制格式。我怀疑您正在尝试加载使用 xlsxlsx 扩展名的 CSV 文件来欺骗 Excel。发布您用于读取 CSV 的代码。请注意,虽然在 CSV 文件中存储带有换行符的文本并不是最明智的做法 - CSV 应该是一种简单,而不是一种精确格式
  • 请发布读取 CSV 的代码。否则无法提供帮助。生成文件的人在文本列中使用换行符保存数据。如果您尝试逐行加载文件,您最终会读取错误的数据。问题是文本中的 换行符,而不是 Unicode
  • 我在 Excel 工作表中读取为 *.xlsx。数据输入人员正在从另一个来源复制/粘贴文本注释。我的脚本将工作表数据导入到 C# 数据表中,我在将数据批量复制到 db 表之前对其进行处理

标签: c# excel unicode sqlbulkcopy


【解决方案1】:

您说的是 Excel,但您的代码使用的是 DataTable。为什么不直接从 Excel 本身 SqlBulkCopy?

您可以使用 SqlBulkCopy 到本地临时表,而不是使用全局临时表,将文本字段的数据类型设置为 NVarchar(max),您应该没有问题。 SQL server 支持 unicode。

编辑:啊哈,我想现在我知道你哪里错了。你说的“Excel 生成的文本数据”。那是一个CSV文件吗? Excel 首先不知道如何保存合法的 CSV。不要保存到文本文件。如果你这样做了,那么就没有一个“进口商”可以正确地读回该数据。而是直接从 Excel 中 SqlBulkCopy,而不使用任何其他中间文本文件或数据表。

【讨论】:

  • 本地和全局临时表是什么意思? DataTable 是内存中的 ADO.NET 类。至于 CSV,OP 并没有尝试将 Excel 保存到 CSV,似乎他正在尝试加载伪装成 Excel 文件的 CSV。关于“合法”的 CSV,没有严格的 CSV 格式规范。 Excel 可以轻松生成任何其他程序都可以读取的 CSV。 OP 发布的数据显示,无论谁导出 CSV,都在文本列中包含换行符。在这种情况下,大多数 csv 读取代码都会失败,因为大多数程序员希望逐行读取 CSV
  • 如果您阅读他的第一行代码,它会说( sqlBulkCopy.DestinationTableName = "##MasterFileTemp"; )。它表示 MS SQL 服务器中的全局临时表,不是吗?他可以使用本地的,而不是使用全局的。它与 DataTable 无关。而且正如我所说,如果是 Excel,那么他不需要介于两者之间的 DataTable,也不需要使用中间 CSV 文件。而且正如我所指出的,Excel 无法编写合法的 CSV 文件(对于 Excel,任何带有标题行 + ...
  • 以逗号分隔的单元格内容是 CSV - 但实际上 CSV 不仅仅是一个 CommaSeparatedValue,如果在数据中使用 CR\LF,则行终止符应该有不同的字符) . Excel 不能生成这样一个合法的 CSV,它会为文本数据吐出 CR\LF,也可以作为行终止符。
  • 首先,对于这个问题,它是否是临时表并不重要。其次,OP 毕竟没有使用 CSV,他正在使用 Jet 驱动程序从 Excel 文件中读取数据。最后,使用\r\n 作为行分隔符并没有错。这并不意味着该文件是“非法的”——没有官方的 CSV 标准。至于DataTable,SqlBulkCopy需要一个DataTable、DataReader或DataRows数组
  • 我没有说直接相关,但是如果是本地临时表,那么他将对其进行更多控制。这是一个建议,而不是“必须”项目。当我第一次阅读它时,并不是说它不是 CSV,而是他发送的部分看起来像 CSV——他说的是“生成”并且带有双引号的样本让我认为 CSV 存在问题。如果您在字段中使用 \r\n 并作为行终止符,那么它是非法的 CSV - 或者如果您愿意,它是超出正常解析器的 CSV。 DataReader 很好,他不需要可能是两者之间的罪魁祸首的 DataTable。
【解决方案2】:

问题已解决。我发现列大小太小而无法容纳 sqlBlkCopy 操作。将大小从 varchar(255) 更改为 varchar(2000)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-05-08
    • 2015-04-23
    • 1970-01-01
    • 1970-01-01
    • 2016-08-29
    • 1970-01-01
    • 2016-08-30
    相关资源
    最近更新 更多