Chinaunix首页 | 论坛 | 博客
  • 博客访问: 7096393
  • 博文数量: 703
  • 博客积分: 10821
  • 博客等级: 上将
  • 技术积分: 12042
  • 用 户 组: 普通用户
  • 注册时间: 2005-12-02 10:41
个人简介

中科院云平台架构师,专注于数字化、智能化,技术方向:云、Linux内核、AI、MES/ERP/CRM/OA、物联网、传感器、大数据、ML、微服务。

文章分类

全部博文(703)

分类: C#/.net

2013-10-07 17:43:18

问题的提出:
我有一个文本文件,里面的内容是以字节为长度分开的几个字段,现在想把每个字段读到EXCEL里面,结果发现EXCEL是以字符为单位处理字符串的。如下:
str1="张三    12345678"     '第一行
str2="王老五  12345678"     '另一行
注意,张三后面是4个空格,王老五后面是两个空格,这样姓名一列它们占的字节数是一定的,都是8个字节;在VBA里当读第一行时得用left(str1,6)读出来的才是姓名那个字段,读第二行时就得用left(str2,5)才正确了。问题在于汉字并不止这两个或三个字,我的文本文件里汉字数量有很大变化的,想了好多办法都不能很好解决。

解决案:
StrConv进行Unicode 和 Ansi 格式的切换。
http://hiyachen.blog.chinaunix.net
chf@tsinghua.org.cn

根由:
目前的计算机VBA默认为本地为Unicode 格式。在LEN,LEFT,Right,MID等函数中,汉字和字母都为一个字符。
相反Ansi 中,汉字为两个字符,字母为一个字符。

以下是详解:
VB中的Unicode 和 Ansi 格式
Visual Basic 32-bit 版本的字串处理采用 Unicode,也就是说字串在 VB 内部是以Unicode 的格式来存放。何谓 Unicode?简单的说,就是每一个字符都是以 2-byte 的形式表示,而每个「实体字符」就是一个「字符」。因此, 
Len("大家好") 
Len("abc") 
所返回的值都是 3,因为「大」和「a」都是一个字符。 
但是这对一些中文字串处理,例如纯文字的数据文件,却是一个大灾难,因为你必须以byte 来定位每个字符,可是 Unicode 却把一切的处理全搞砸了。例如: 
Len("Good Morning") 返回 12,而Len("今天天气很好") 返回 6 
对初学者而言,好不容易能使用 VB 来写程序已经是件了不起的事了,却马上在中文处理上挨了一记闷棍,所受到的打击实在不小。但是不要怕,事实上只要再多了解一些指令,就可以把中文处理的问题解决了。 
是什么指令呢?最重要的莫过于 StrConv 了。StrConv 函数的语法为:StrConv(待转换字串, 转换格式)其中转换格式在这里用到的是: 
vbUnicode 将 Ansi 字串转换为 Unicode vbFromUnicode 将 Unicode 字串转换为 Ansi将字串转成 Ansi 之后,所有的字串处理指令都要加个 B,例如:LeftB, RightB,MidB, ChrB, InstrB, LenB, InputB 等。例用这些指令来处理就行了。 
当你处理完毕之后,你可以再将它再转回 Unicode,这样就可以使用一般的字串处理指令了。 
这样讲看得懂吗?如果还是不了解,看看下面的实例说明: 
[●] 简易使用范例 
看看下面的基本范例您应该就会对 VB 的字串处理方式有些概念。 
Private Sub Command1_Click () 
Dim sUnicode As String 
Dim sAnsi As String 
' Unicode 运算 
sUnicode = "王小明,A123456789,651023,上海市中山路100号,(02)2345678" 
Debug.Print Len(sUnicode) ' 返回 44 
Debug.Print Mid$(sUnicode, 5, 10) ' 返回 A123456789 
Debug.Print Instr(sUnicode, "上海市") ' 返回 23 
' 将 Unicode 字串转成 Ansi 
sAnsi = StrConv(sUnicode, vbFromUnicode) 
' Ansi 运算 
Debug.Print LenB(sAnsi) ' 返回 54 
Debug.Print MidB$(sAnsi, 8, 10) ' 返回 ?????,因为忘了转回 Unicode 
Debug.Print StrConv(MidB$(sAnsi, 8, 10), vbUnicode) 
' 返回 A123456789,请注意转回 Unicode 的动作一定要做 
Debug.Print InStrB(sAnsi, StrConv("上海市", vbFromUnicode)) 
' 返回 23, 不要忘了要把"上海市"也转成 Ansi,否则会找不到 
End Sub 

[●] 读入文本文件 
在 VB 的小技巧中,有一个是快速读文件法: 
Private Sub Command1_Click () 
Dim sFile As String 
Open "C:\filename.txt" For Input As #1 
sFile = Input$(LOF(1), #1) 
Close #1 
End Sub 
但是很不幸地,如果你读取的文件内含中文字,那上面这段程序会出现 Input pastend 
of file 的错误。因为 LOF 返回的是文件的 byte 数,而 Input 函数读取的是字符 
数,由于文件内含中文,因此文件中的字符数将会小于 byte 数,于是就发生错误了。 
要解决这个问题,我们就要用到 StrConv 和 InputB 这两个函数了: 
Private Sub Command1_Click () 
Dim sFile As String 
Open "C:\filename.txt" For Input As #1 
sFile = StrConv(InputB$(LOF(1), #1), vbUnicode) 
Close #1 
End Sub 
上面修正程序先用 InputB 将文件读进来,不过使用 InputB 所读入的文件是 Ansi格 
式的,所以要再用 StrConv 转成 Unicode 才行。 
[●] 随机数据文件 
许多文字数据文件是以固定字节的位置来加以区格,例如下面的数据格式: 
王小民650110上海市中山路100号 (02)1234567 
张大呆660824花莲县大甲镇广东街23号(03)9876543 
...... 
像这种类型的文件要如何处理呢?这是就必须用到 Type 以及 byte array 了。 
Private Type tagRecord 
Username(5) As Byte ' 姓名 6 bytes 
Birthday(5) As Byte ' 生日 6 bytes 
Address(21) As Byte ' 地址 22 bytes 
TEL(11) As Byte ' 电话 12 bytes 
CrLf(1) As Byte ' 换行字符 2 bytes 
End Type 
Private Sub Command1_Click() 
Dim uRecord As tagRecord 
Open "C:\filename.dat" For Random As #1 Len = LenB(uRecord) 
Get #1, 2, uRecord ' 取第二笔数据 
With uRecord ' With ... End With 应该会用吧 
Debug.Print .Username ' 返回 ??? 
Debug.Print StrConv(.Username, vbUnicode) ' 返回 "张大呆" 
End With 
Close #1 
End Sub 
在这个例子中,一定要用到 byte array,因为只有 byte array 才能正确地定位到每 
个 byte 的位置。以前使用字串来定位的方法已经不适用了,千万要记住!但是使用 
byte array 所读入的数据是 Ansi 格式,若要处理或是做运算的话,记得还要转成 
Unicode 格式才行。 
[●] 使用 Byte Array 
除了上面必须使用 byte 精确定位的例子之外,纯文字的处理基本上是用不到bytearray 的。byte array 通常是用在处理 binary 数据。这方面的问题我们将另文讨论。 
看吧!只要熟悉使用 StrConv,你就可以在 Unicode 及 Ansi 格式之间自由自在地变来变去,相信当您看完这篇文章之后,对处理中文应该不再烦恼了吧! 

关于StrConv 函数
返回按指定类型转换的 Variant (String)。
语法
StrConv(string, conversion, LCID)
StrConv 函数的语法有下面的命名参数:
部分 说明
string 必要参数。要转换的串。
conversion 必要参数。Integer。其值的和决定转换的类型。
LCID 可选的。如果与系统LocaleID不同,则为LocaleID(系统LocaleID为缺省值。)
设置值
conversion 参数的设置值为:
常数 值 说明
vbUpperCase 1 将字符串文字转成大写。
vbLowerCase 2 将字符串文字转成小写。
vbProperCase 3 将字符串中每个字的开头字母转成大写。
vbWide* 4* 将字符串中单字符转成双字符。
vbNarrow* 8* 将串中双字节字符转成单字节字符。
vbKatakana** 16** 将串中平假名字符转成片假名字符。
vbHiragana** 32** 将串中片假名字符转成平假名字符。
vbUnicode 64 根据系统的缺省码页将字符串转成 Unicode。
vbFromUnicode 128 将字符串由 Unicode 转成系统的缺省码页。
*应用到远东国别。
**仅应用到日本。
注意 这些常数是由 VBA 指定的。可以在程序中使用它们来替换真正的值。其中大部分是可以组合的,例如 vbUpperCase + vbWide,互斥的常数不能组合,例如 vbUnicode + vbFromUnicode。当在不适用的国别使用常数 vbWide、vbNarrow、vbKatakana,和 vbHiragana 时,就会导致运行时错误。
下面是一些一般情况下的有效分界符:Null (Chr$(0)),水平 (Chr$(9)),换行 (Chr$(10)),垂直制表符 (Chr$(11)),换页 (Chr$(12)) ,回车 (Chr$(13)),空白 (SBCS) (Chr$(32))。在 DBCS中,空白的实际值会随国家/地区而不同。
说明
在把 ANSI 格式的 Byte 转换为字符串时,您应该使用 StrConv 函数。当您转换 Unicode 格式的这种时,使用。
StrConv 函数示例
本示例使用StrConv 函数来将Unicode 字符串转换成 ANSI 字符串。
Dim i As Long
Dim x() As Byte
x = StrConv("ABCDEFG", vbFromUnicode)    ' 转换字符串。
For i = 0 To UBound(x)
Debug.Print x(i)
Next
--------------------------------------------------------------------------------
相关基础知识:
命名参数
一参数,在对象库中预先定义了其名称。对每个参数,不必拘泥于语法所规定的特定顺序来提供值,而是只需按任何顺序用命名参数分配值。例如,假设一方法接受了三个参数:
DoSomeThing namedarg1, namedarg2, namedarg3
在对命名参数赋值时,可使用以下语句:
DoSomeThing namedarg3 := 4, namedarg2 := 5, namedarg1 := 20
注意,命名的参数不必按语法中安排的正规顺序出现。
字符串
任何其值为一连串的。字符串的元素可包含返回字符串的函数、字符串文字、字符串常数、字符串、字符串 Variant 或返回字符串 Variant (VarType 8) 的函数。
Integer
一种,将作为 2 整数存储起来,范围是 -32,768 到 32,767 之间的数字。Integer 也可表示列举值。在 Visual Basic 可用百分比符号 (%) 的类型声明来表示一个 Integer。
参数
传递给一个过程的常数、或表达式。
Unicode
国际标准化组织 (ISO) 标准。Unicode 使用 16 位 (2 ) 编码方案,允许 65,536 个不同的空间。Unicode 包含标点符号、数学符号、修饰符号等的表示。
常数
执行程序时保持常数值的命名项目。常数可以是字符串、数值、另一常数、任何( 除乘幂与 Is 之外的) 算术运算符或的组合。每个主应用程序皆可定义自己的一组常数。用户也可以 Const 语句定义附加常数。可在代码中的任何地方使用常数代替实际的值。
区域
关于国家/地区及其语言的信息集合。代码会影响关键字等术语的语言,也会定义区域专用设置,像小数点、日期格式、排序顺序等。
系统的会影响到判别区域的功能,例如,在显示数字或将字符串转换成日期时,可使用操作系统“”的实用程序来设置系统所在区域。
虽然代码区域和系统区域通常设置为相同的设置值,但在一些情况仍有不同之处。例如,在标准版 Visual Basic 和专业版 Visual Basic 中,并没有把代码从 English-U.S. 翻译过来。可以把系统成用户的语言和国家/地区,但总是将代码区域设置成 English-U.S.,而且不能改变。在这种情况下,将使用 English-U.S. 、和排序次序。
运行时错误
在代码正在运行的时发生的错误。当一语句要进行非法操作时就会发生运行时错误。
Null
一个值,指示不含有效数据。 Null 是将 Null 显式赋值给一的结果,或者是包含 Null 的之间的任何运算。
DBCS
使用 1 或 2 来表示一的,超过个 256 字符也可表示。

阅读(13362) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~