Vba 将工作表中的单元格字符限制在某些字节内。\u适用于所有语言的更改

Vba 将工作表中的单元格字符限制在某些字节内。\u适用于所有语言的更改,vba,excel,character-encoding,Vba,Excel,Character Encoding,我知道可以将单元格值限制为特定长度的代码,如下所示,但如何将其限制为特定的字节(例如240字节)并在限制范围内截断?此外,字符集将是“shift-jis”,即日语+英语 If Not Intersect(Target, Range("A2:B200")) Is Nothing Then For Each cell In Intersect(Target, Columns("A:B")) If (cell.Value) > 20 Then ce

我知道可以将单元格值限制为特定长度的代码,如下所示,但如何将其限制为特定的字节(例如240字节)并在限制范围内截断?此外,字符集将是“shift-jis”,即日语+英语

If Not Intersect(Target, Range("A2:B200")) Is Nothing Then
    For Each cell In Intersect(Target, Columns("A:B"))
        If (cell.Value) > 20 Then
            cell.Value = VBA.Left(cell.Value, 20)
            cell.Select
            MsgBox "Character limit for the cell is 20." & vbNewLine & "Truncated to 20 characters."
        End If
    Next cell
End If

谢谢你的帮助

如果我正确理解了这个问题,您最终需要做的是确定一个字符是否属于ANSI字符范围(可以用一个字节-0到255表示)。Excel并没有像评论中提到的那样让这变得容易。VBA也是如此,它在内部将所有字符串表示为。VB4之后,VBA
Len
LenB
的行为发生了变化,这一问题更加复杂。在此更改之前,他们会为Unicode或ANSI输入返回不同的结果。现在,它们都将返回相同的结果,因为
LenB
返回字符串的内存长度,该长度始终为每个字符2个字节。ANSI范围的区别在于第二个字节始终为零

StrConv
函数确实提供了一种检查字符串是否包含非ANSI字符的方法-您可以将其转换为
字节
数组,并检查是否设置了任何高位字节。例如,字符串“ABCD”存储在VBA的内存中,格式如下:

65  0   66  0   67  0   68  0
您可以使用VBA的“Unicode转换”技巧,使用StrConv(“ABCD”,vbUnicode)再次将其扩展到2个字节,结果如下:

65  0   0   0   66  0   0   0   67  0   0   0   68  0   0   0
为了进行比较,如果您从某处提取字符串“ΑΒΓΔ”(由于无法在IDE中键入它,因此必须这样做),则可能会导致这种情况,具体取决于编码:

24  32  3   0   25  32  3   0   28  32  3   0   29  32  3   0
所以,一旦你有了一个字节数组,你需要做的就是每隔一个字节检查一次——如果你找到一个非零值,它就不能缩小到ANSI:

Private Function IsANSI(test As String) As Boolean
    Dim bytes() As Byte, i As Long
    bytes = StrConv(test, vbUnicode)
    For i = 1 To UBound(bytes) Step 2
        If bytes(i) <> 0 Then
            IsANSI = False
            Exit Function
        End If
    Next i
    IsANSI = True
End Function
请注意,字节长度始终取决于编码。如果您需要特定编码中字符串的长度,VBA在本机上不会对您有多大帮助,除非它是固定宽度编码(即UTF-32,VBA可能已经破坏了它),您必须进入Windows API并显式转换它,然后查看您得到了什么。您可以找到一个VBA示例。

您可以使用它来获取以特定字符集表示的有限字节长度的字符串。因为您没有指定任何字符集,所以我选择了一个示例。我修改了您的代码并添加了
LeftUTF8()
函数,下面的代码放在工作表模块中:

Private子工作表\u更改(ByVal目标作为范围)
Application.EnableEvents=False
如果不相交(目标,范围(“A2:B200”))则为零
对于Intersect中的每个单元格(目标、列(“A:B”))
cell.Value=LeftUTF8(cell.Value,240)
下一个细胞
如果结束
Application.EnableEvents=True
端接头
私有函数LeftUTF8(strCont作为字符串,lngLenght作为长字符串)作为字符串
我想我会坚持多久
Dim arrCont()作为字节
'添加对Microsoft ActiveX数据对象库的引用(2.5或更高版本)
静态对象流作为新的ADODB.Stream
i=绿色
带objStream
.Type=adTypeText
打开
.Charset=“utf-8”
.WriteText strCont“使用BOM将字符串转换为UTF-8
.位置=0
.Type=adTypeBinary
如果.Size>i+3,则“以字节为单位的大小大于限制+3字节UTF-8 BOM
对于i=i到1步骤-1',如果最后一个多字节字符被拆分,则减少输出
.Position=i+3'最后一个+BOM后的下一个字节
如果AscB(.Read(1))和192 128,则“下一个字节是下一个字符的第一个字节”的退出
下一个
如果结束
.位置=0
arrCont=.Read(i+3)'读取BOM对应限制的字节
.关闭“清流”
打开
.Type=adTypeBinary
.Write arrCont的写入字节
.位置=0
.Type=adTypeText
LeftUTF8=.ReadText'读取字符串
.结束
以
端函数
更新

以下是获取以shift jis字符集表示的240字节字符串的代码:

Private子工作表\u更改(ByVal目标作为范围)
Application.EnableEvents=False
如果不相交(目标,范围(“A2:B200”))则为零
对于Intersect中的每个单元格(目标、列(“A:B”))
cell.Value=LeftShiftJis(cell.Value,240)
下一个细胞
如果结束
Application.EnableEvents=True
端接头
私有函数LeftShiftJis(strCont作为字符串,lngLenght作为长)作为字符串
我想我会坚持多久
Dim arrCont()作为字节
'添加对Microsoft ActiveX数据对象库的引用(2.5或更高版本)
静态对象流作为新的ADODB.Stream
i=绿色
使用CreateObject(“ADODB.Stream”)
.Type=adTypeText
打开
.Charset=“shift jis”
.WriteText strCont'将字符串转换为shift jis二进制表示形式
.位置=0
.Type=adTypeBinary
arrCont=.Read(i)'读取有限的字节数
.关闭“清流”
打开
.Type=adTypeBinary
.Write arrCont“写入所有内容,如果最后两个字节字符的第二个字节被截断,则将添加零,而不是缺少的字节
.位置=0
.Type=adTypeText
LeftShiftJis=.ReadText'如果最后一个字符是两个字节,并且第二个字节被截断,那么它将被修剪
.结束
以
端函数

您必须确定是否正在使用DBCS语言。一旦确定了长度,其中一个工作表就可以正确地确定长度。@Jeeped-我不确定这是否有效。如果默认为DBCS语言,LENB将返回每个字符2个字节,即使对于ANSI范围内的字符也是如此。因此,您需要非DBCS字符的长度计数和DBCS字符的长度计数。也许AscW会有所帮助。限制byt的目标是什么
Private Function ByteLength(test As String) As Long
    If IsANSI(test) Then
        ByteLength = Len(test)
    Else
        ByteLength = LenB(test)
    End If
End Function