Visual Basic .NET按双字符对替换字符串指定字符对的技术问题
处理FoxBase/dBASE III DBF文件的完整方案(Visual Basic WinForm)
核心问题解决
针对DBF文件中字符间存在0x00字节导致字符串截断的问题,实现双字节成对替换逻辑:仅替换成对出现的0x00为0x2A(即*),跳过单独的0x00字节。
1. DBF文件导入逻辑
通过OleDb连接读取DBF文件,直接读取字段原始字节数组(避免字符串截断),应用替换逻辑后转换为可编辑的字符串。
Imports System.Data.OleDb Imports System.IO Imports System.Text Private Function LoadDBF(ByVal dbfPath As String) As DataTable Dim dt As New DataTable() Dim folderPath As String = Path.GetDirectoryName(dbfPath) Dim fileName As String = Path.GetFileName(dbfPath) ' dBASE III 连接字符串 Using conn As New OleDbConnection($"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={folderPath};Extended Properties=dBASE III;") conn.Open() Using cmd As New OleDbCommand($"SELECT * FROM [{fileName}]", conn) Using reader As OleDbDataReader = cmd.ExecuteReader() dt.Load(reader) ' 处理目标列(替换为实际需要处理的列名,例如"TechColumn") Dim targetColName As String = "TechColumn" If dt.Columns.Contains(targetColName) Then ' 重新读取原始字节 Using newReader As OleDbDataReader = cmd.ExecuteReader() Dim colOrdinal As Integer = newReader.GetOrdinal(targetColName) Dim rowIndex As Integer = 0 While newReader.Read() If Not newReader.IsDBNull(colOrdinal) Then ' 读取字段原始字节数组 Dim bufferSize As Integer = If(dt.Columns(targetColName).MaxLength > 0, dt.Columns(targetColName).MaxLength, 255) Dim buffer(bufferSize - 1) As Byte Dim bytesRead As Integer = newReader.GetBytes(colOrdinal, 0, buffer, 0, bufferSize) Dim originalBytes As Byte() = New Byte(bytesRead - 1) {} Array.Copy(buffer, originalBytes, bytesRead) ' 应用双字节替换逻辑 Dim modifiedBytes As Byte() = ReplaceZeroPairs(originalBytes) ' 转换为ASCII字符串(dBASE III默认单字节字符集) dt.Rows(rowIndex)(targetColName) = Encoding.ASCII.GetString(modifiedBytes) End If rowIndex += 1 End While End Using End If End Using End Using End Using Return dt End Function
2. 双字节成对替换函数
严格按照需求,仅替换成对出现的0x00字节,跳过单独的0x00:
Private Function ReplaceZeroPairs(ByVal originalBytes As Byte()) As Byte() Dim result As New List(Of Byte)() Dim i As Integer = 0 While i < originalBytes.Length ' 添加当前字节 result.Add(originalBytes(i)) ' 检查是否存在下一个字节(成对) If i + 1 < originalBytes.Length Then Dim nextByte As Byte = originalBytes(i + 1) ' 替换0x00为0x2A result.Add(If(nextByte = &H00, &H2A, nextByte)) i += 2 ' 步长2,处理下一对 Else ' 非成对字节,直接保留 i += 1 End If End While Return result.ToArray() End Function
3. 数据编辑
直接使用DataGridView绑定导入后的DataTable,用户可直观编辑所有字段:
' 导入按钮点击事件 Private Sub btnImport_Click(sender As Object, e As EventArgs) Handles btnImport.Click Using ofd As New OpenFileDialog() ofd.Filter = "DBF Files (*.dbf)|*.dbf" If ofd.ShowDialog() = DialogResult.OK Then Dim dt As DataTable = LoadDBF(ofd.FileName) DataGridView1.DataSource = dt DataGridView1.AutoResizeColumns() End If End Using End Sub
若不需要DataGridView,也可使用TextBox、ListBox等控件手动绑定数据,核心操作基于DataTable即可。
4. DBF文件导出逻辑
将编辑后的DataTable写入新的DBF文件,保持原表结构:
Private Sub SaveDBF(ByVal dt As DataTable, ByVal savePath As String) Dim folderPath As String = Path.GetDirectoryName(savePath) Dim fileName As String = Path.GetFileName(savePath) ' 确保目标文件夹存在 If Not Directory.Exists(folderPath) Then Directory.CreateDirectory(folderPath) End If Using conn As New OleDbConnection($"Provider=Microsoft.Jet.OLEDB.4.0;Data Source={folderPath};Extended Properties=dBASE III;") conn.Open() ' 1. 创建表结构 Dim createSqlBuilder As New StringBuilder($"CREATE TABLE [{fileName}] (") For Each col As DataColumn In dt.Columns Dim colType As String = col.DataType.Name.ToLower() switch Case "string" : $"CHAR({col.MaxLength})" Case "int32", "int16" : "INTEGER" Case "datetime" : "DATE" Case "double" : "FLOAT" Case Else : "CHAR(255)" End switch createSqlBuilder.Append($"[{col.ColumnName}] {colType}, ") Next ' 移除末尾的逗号和空格 createSqlBuilder.Length -= 2 createSqlBuilder.Append(")") Using createCmd As New OleDbCommand(createSqlBuilder.ToString(), conn) createCmd.ExecuteNonQuery() End Using ' 2. 插入数据 For Each row As DataRow In dt.Rows Dim insertSqlBuilder As New StringBuilder($"INSERT INTO [{fileName}] VALUES (") Dim valueList As New List(Of String)() For Each col As DataColumn In dt.Columns If row.IsNull(col) Then valueList.Add("NULL") Else Select Case col.DataType.Name.ToLower() Case "string" Dim strValue As String = row(col).ToString() ' 转义SQL单引号 valueList.Add($"'{strValue.Replace("'", "''")}'") Case "datetime" valueList.Add($"'{DirectCast(row(col), DateTime).ToString("yyyyMMdd")}'") Case Else valueList.Add(row(col).ToString()) End Select End If Next insertSqlBuilder.Append(String.Join(", ", valueList)) insertSqlBuilder.Append(")") Using insertCmd As New OleDbCommand(insertSqlBuilder.ToString(), conn) insertCmd.ExecuteNonQuery() End Using Next End Using End Sub ' 导出按钮点击事件 Private Sub btnExport_Click(sender As Object, e As EventArgs) Handles btnExport.Click If DataGridView1.DataSource Is Nothing Then MessageBox.Show("请先导入DBF文件!") Return End If Using sfd As New SaveFileDialog() sfd.Filter = "DBF Files (*.dbf)|*.dbf" If sfd.ShowDialog() = DialogResult.OK Then Dim dt As DataTable = DirectCast(DataGridView1.DataSource, DataTable) SaveDBF(dt, sfd.FileName) MessageBox.Show("DBF文件导出成功!") End If End Using End Sub
注意事项
- 确保系统安装了Microsoft Jet OLEDB 4.0(Win10/11需手动安装);
- 替换代码中的
TechColumn为实际需要处理的列名; - 如需导出时还原
0x2A为0x00,可在导出逻辑中添加字节替换代码; - DBF字段长度需与原文件一致,避免数据截断。
内容的提问来源于stack exchange,提问作者Griffin Manning
相关产品推荐
相关产品推荐

