从字符数组转换时的字符串长度

时间:2014-06-25 00:02:33

标签: .net vb.net string bytearray

我遇到严重的字符串处理问题。 由于我的问题很难描述,我将从一些演示代码开始再现它们:

Dim s1 As String = "hi"
Dim c(30) As Char
c(0) = "h"
c(1) = "i"
Dim s2 As String = CStr(c)
s2 = s2.Trim()
If not s1 = s2 Then
   MsgBox(s1 + " != " + s2 + Environment.NewLine + _
          "Anything here won't be printed anyway..." + Environment.NewLine + _ 
          "s1.length: " + s1.Length.ToString + Environment.NewLine + _
          "s2.length: " + s2.Length.ToString + Environment.NewLine)
End If                    

结果消息框如下所示:

screenshot of the messagebox showing only hi != hi but not the rest of the text

这个比较失败的原因是s2的长度为31(来自原始数组大小),而s1的长度为2.

当从字节数组中读取字符串信息时,我经常偶然发现这种问题,例如当处理来自MP3的ID3Tags或其他具有预定长度的编码(ASCII,UTF8,...)信息时。 / p>

有没有快速而干净的方法来防止这个问题?

将s2“修剪”为调试器显示的字符串的最简单方法是什么?

提前致谢, 贾尼斯

4 个答案:

答案 0 :(得分:7)

为了清晰起见,我更改了变量名称:

Dim myChars(30) As Char
myChars(0) = "h"c           ' cannot convert string to char
myChars(1) = "i"c           ' under option strict (narrowing)
Dim myStrA As New String(myChars)
Dim myStrB As String = CStr(myChars)

简短的回答是:

在引擎盖下,字符串字符数组。最后两行都使用NET代码创建一个字符串,另一个使用VB函数。问题在于,虽然数组有31个元素,但只有2个被初始化

enter image description here

其余为空/无,Char表示Chr(0)NUL。由于NUL用于标记String的结尾,因此只有NUL以外的字符才能打印在ConsoleMessageBox等文字中。字符串也不会显示。


<强> 概念

由于上面的字符串是直接从char数组创建的,因此长度是原始数组的长度。 Nul是有效的char,因此会将其添加到字符串中:

Console.WriteLine(myStrA.Length)     ' == 31

那么,为什么没有Trim删除空字符? MSDN(和Intellisense)告诉我们:

  

[Trim]从当前String对象中删除所有前导和尾随空白字符。

尾随空/ Chr(0)字符不是像Tab,Lf,Cr或Space这样的空格,而是control character

但是,String.Trim有一个重载,它允许您指定要删除的字符:

myStrA = myStrA.Trim(Convert.ToChar(0))
' using VB namespace constant
myStrA = myStrA.Trim( Microsoft.VisualBasic.ControlChars.NullChar)

您可以指定多个字符:

' nuls and spaces:
myStrA = myStrA.Trim(Convert.ToChar(0), " "c)

字符串可以被索引/迭代为char数组:

    For n As Int32 = 0 To myStrA.Length
        Console.Write("{0} is '{1}'", n, myStrA(n))  ' or myStrA.Chars(n)
    Next
  

0是&#39; h&#39;
  1是&#39; i&#39;
  2是&#39;

(输出窗口甚至不会打印尾随的CRLF。)但是,您无法更改字符串的char数组来更改字符串数据:

   myStrA(2) = "!"c

这不会编译,因为它们是只读的。

另见:

ASCII table

答案 1 :(得分:2)

如果要从字节数组创建字符串,即使用ISO-8859编码的ID3v2.4.0,那么这应该可行:

    Dim s1 As String = "Test"
    Dim b() As Byte = New Byte() {84, 101, 115, 116, 0, 0, 0}
    Dim s2 As String = System.Text.ASCIIEncoding.ASCII.GetString(b).Trim(ControlChars.NullChar)

    If s1 = s2 Then Stop

根据此http://id3.org/id3v2.4.0-structure,可能存在其他编码,如果使用其中一个,则需要调整代码。

答案 2 :(得分:1)

原因是CStr(c)将NUL(0)字符视为结果字符串的成员而不是字符串终结符。基础String.Trim()无法正常工作,因为将NUL字符视为空格。

避免此问题的一种方法是仅将字符(或字节)转换为第一个NUL(或0);在这种情况下,TakeWhile函数很有用。

Const NUL as Char = Microsoft.VisualBasic.ChrW(0)
Dim cleanChars() as Char = _
    c.TakeWhile(Function(v, i) v <> NUL) _
     .ToArray

CStr(cleanChars) ' -> "hi"

如果数据真的来自Bytes(而不是Chars),那么切换到Encoding.GetString可能是谨慎的,因此编码/过程是明确且易于理解的,例如。

Encoding.UTF8.GetString(cleanBytes) ' -> still "hi"

答案 3 :(得分:0)

一旦知道了s1字符串的长度,就可以对char数组进行Dim或ReDim。

Dim s1 As String
s1 = "hi"
Dim c(s1.Length) As Char
c(0) = "h"
c(1) = "i"
Dim s2 As String = CStr(c)

现在,无论原始字符串的长度如何,您的比较都会有效。你并没有说明30岁以下的长度是否为30%。这是一个要求与否。

但即使是这样,你仍然需要扩展或收缩数组以具有相同的CStr长度来进行比较。

所以即使在宣布

之后
Dim c(30)

您可以稍后在代码块中重新编译数组,如此

ReDim c(s1.Length) 'Or any int value you like

如果增加,你可以在preserve关键字之前,这将扩展数组,同时保持其当前内容。

ReDim Preserve c(s1.Length)