Jet.OLEDB.4.0 从 CSV 导入时截断字符串
Jet.OLEDB.4.0 Truncating String When importing from CSV
我有一个 windows 表单应用程序,它接收 CSV/Excel 个文件,允许用户将它们导入 SQL 数据库。
最近,我遇到了一个奇怪的错误,用户上传 CSV 文件时,它会截断列中的文本。
这是源文件:
这是我应用中的 DatagridView,在转换为数据表后显示相同的数据:
请注意,有些值完全空白并以红色突出显示,而其他值似乎被截断了。
然而,这只发生在 CSV 文件上,而不是 excel。这使我相信这可能是驱动程序问题。
下面是将平面文件数据转换为数据表的代码:
Private Function ConvertCSVToDataTable(ByVal path As String) As DataTable
Using con As OleDb.OleDbConnection = New OleDb.OleDbConnection()
Try
If System.IO.Path.GetExtension(path) = ".csv" Then
con.ConnectionString = String.Format("Provider={0};Data Source={1};Extended Properties=""Text;HDR=YES;FMT=Delimited""", "Microsoft.Jet.OLEDB.4.0", IO.Path.GetDirectoryName(path))
Using cmd As OleDb.OleDbCommand = New OleDb.OleDbCommand("SELECT * FROM [" & IO.Path.GetFileName(path) & "]", con)
Using da As OleDb.OleDbDataAdapter = New OleDb.OleDbDataAdapter(cmd)
con.Open()
da.Fill(dt)
con.Close()
End Using
End Using
ElseIf System.IO.Path.GetExtension(path) = ".xlsx" Then
con.ConnectionString = String.Format("Provider={0};Data Source={1};Extended Properties=""Excel 12.0 Xml;HDR=Yes;IMEX=1""", "Microsoft.ACE.OLEDB.12.0", path)
con.Open()
Dim dbSchema As DataTable = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, Nothing)
Dim firstSheetname As String = dbSchema.Rows(0)("TABLE_NAME").ToString
Using cmd As OleDb.OleDbCommand = New OleDb.OleDbCommand("SELECT * FROM [" & firstSheetname & "]", con)
Using da As OleDb.OleDbDataAdapter = New OleDb.OleDbDataAdapter(cmd)
'con.Open()
da.Fill(dt)
con.Close()
End Using
End Using
End If
Catch ex As Exception
MessageBox.Show(ex.ToString(), "Conversion Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk)
Finally
If con IsNot Nothing AndAlso con.State = ConnectionState.Open Then
con.Close()
End If
End Try
End Using
Return dt
End Function
知道是什么原因造成的吗?
我还应该注意,我的一些用户只能导入 CSV 而不能 Excel。我发现使用 32 位 Access 驱动程序的用户可以导入 excel 文件,而 64 位用户则不能。我让他们在这里下载驱动程序:
https://www.microsoft.com/en-us/download/details.aspx?id=23734
现在这些用户可以导入 Excel,但他们仍然遇到字符串被截断的问题。这让我相信它可能仍然是驱动程序问题。
测试数据:
Sales Order #
US00123
US00123
US00123
SG0000123
SG0000123
S00123
S00123
S00123
S00123
S00123
我已经能够重现您的问题。
问题是由于某种原因,Jet 提供程序使用的文本文件驱动程序的数值为 interpreting/parsing "S00123"。我必须使用 MaxScanRows=0 和 ImportMixedTypes="Majority Type".
配置注册表
我不知道为什么会这样,但我只是将其归因于使用记录不完整的技术的另一种乐趣。似乎任何以 "S" 开头后跟数字的字段都被解释为数字。
如果您坚持使用此技术来完成一项有更好选择的任务,那么您将不得不忍受它的许多缺点和怪癖。
解决方案 1:
将提供程序配置为导入,注册表值 ImportMixedTypes 设置为 "Text"。现在,如果您进行此更改以允许您的程序运行,您还将负责破坏依赖于现有配置的任何其他代码。
注册表值在 64 位上的位置 OS。
对于 Jet 提供商:
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Jet.0\Engines\Text
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Jet.0\Engines\Excel
对于 ACE 提供商:
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Office.0\Access Connectivity Engine\Engines\Text
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Office.0\Access Connectivity Engine\Engines\Excel
解决方案 2:
使用 [Schema.ini] 文件 https://docs.microsoft.com/en-us/sql/odbc/microsoft/schema-ini-file-text-file-driver) 来定义如何使用定义的列解释文本文件。
这是使用该技术的首选方法。请注意,文件的编码非常重要;它必须采用 .Net 编码 System.Text.Encoding.ASCII
或 System.Text.Encoding.Unicode
。如果您使用其他编码,例如 UTF-8
,将不会读取该文件并使用注册表中的设置。
我有一个 windows 表单应用程序,它接收 CSV/Excel 个文件,允许用户将它们导入 SQL 数据库。
最近,我遇到了一个奇怪的错误,用户上传 CSV 文件时,它会截断列中的文本。
这是源文件:
这是我应用中的 DatagridView,在转换为数据表后显示相同的数据:
请注意,有些值完全空白并以红色突出显示,而其他值似乎被截断了。 然而,这只发生在 CSV 文件上,而不是 excel。这使我相信这可能是驱动程序问题。
下面是将平面文件数据转换为数据表的代码:
Private Function ConvertCSVToDataTable(ByVal path As String) As DataTable
Using con As OleDb.OleDbConnection = New OleDb.OleDbConnection()
Try
If System.IO.Path.GetExtension(path) = ".csv" Then
con.ConnectionString = String.Format("Provider={0};Data Source={1};Extended Properties=""Text;HDR=YES;FMT=Delimited""", "Microsoft.Jet.OLEDB.4.0", IO.Path.GetDirectoryName(path))
Using cmd As OleDb.OleDbCommand = New OleDb.OleDbCommand("SELECT * FROM [" & IO.Path.GetFileName(path) & "]", con)
Using da As OleDb.OleDbDataAdapter = New OleDb.OleDbDataAdapter(cmd)
con.Open()
da.Fill(dt)
con.Close()
End Using
End Using
ElseIf System.IO.Path.GetExtension(path) = ".xlsx" Then
con.ConnectionString = String.Format("Provider={0};Data Source={1};Extended Properties=""Excel 12.0 Xml;HDR=Yes;IMEX=1""", "Microsoft.ACE.OLEDB.12.0", path)
con.Open()
Dim dbSchema As DataTable = con.GetOleDbSchemaTable(OleDbSchemaGuid.Tables, Nothing)
Dim firstSheetname As String = dbSchema.Rows(0)("TABLE_NAME").ToString
Using cmd As OleDb.OleDbCommand = New OleDb.OleDbCommand("SELECT * FROM [" & firstSheetname & "]", con)
Using da As OleDb.OleDbDataAdapter = New OleDb.OleDbDataAdapter(cmd)
'con.Open()
da.Fill(dt)
con.Close()
End Using
End Using
End If
Catch ex As Exception
MessageBox.Show(ex.ToString(), "Conversion Error", MessageBoxButtons.OK, MessageBoxIcon.Asterisk)
Finally
If con IsNot Nothing AndAlso con.State = ConnectionState.Open Then
con.Close()
End If
End Try
End Using
Return dt
End Function
知道是什么原因造成的吗? 我还应该注意,我的一些用户只能导入 CSV 而不能 Excel。我发现使用 32 位 Access 驱动程序的用户可以导入 excel 文件,而 64 位用户则不能。我让他们在这里下载驱动程序:
https://www.microsoft.com/en-us/download/details.aspx?id=23734
现在这些用户可以导入 Excel,但他们仍然遇到字符串被截断的问题。这让我相信它可能仍然是驱动程序问题。
测试数据:
Sales Order #
US00123
US00123
US00123
SG0000123
SG0000123
S00123
S00123
S00123
S00123
S00123
我已经能够重现您的问题。
问题是由于某种原因,Jet 提供程序使用的文本文件驱动程序的数值为 interpreting/parsing "S00123"。我必须使用 MaxScanRows=0 和 ImportMixedTypes="Majority Type".
配置注册表我不知道为什么会这样,但我只是将其归因于使用记录不完整的技术的另一种乐趣。似乎任何以 "S" 开头后跟数字的字段都被解释为数字。
如果您坚持使用此技术来完成一项有更好选择的任务,那么您将不得不忍受它的许多缺点和怪癖。
解决方案 1:
将提供程序配置为导入,注册表值 ImportMixedTypes 设置为 "Text"。现在,如果您进行此更改以允许您的程序运行,您还将负责破坏依赖于现有配置的任何其他代码。
注册表值在 64 位上的位置 OS。
对于 Jet 提供商:
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Jet.0\Engines\Text
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Jet.0\Engines\Excel
对于 ACE 提供商:
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Office.0\Access Connectivity Engine\Engines\Text
Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Microsoft\Office.0\Access Connectivity Engine\Engines\Excel
解决方案 2:
使用 [Schema.ini] 文件 https://docs.microsoft.com/en-us/sql/odbc/microsoft/schema-ini-file-text-file-driver) 来定义如何使用定义的列解释文本文件。
这是使用该技术的首选方法。请注意,文件的编码非常重要;它必须采用 .Net 编码 System.Text.Encoding.ASCII
或 System.Text.Encoding.Unicode
。如果您使用其他编码,例如 UTF-8
,将不会读取该文件并使用注册表中的设置。