簡體   English   中英

string.Format返回的.NET字符串中的空格與源代碼中聲明的空格不匹配 - 多個表示形式?

[英]Space in a .NET string returned by string.Format does not match space declared in source code - multiple representations?

string.Format返回的字符串似乎使用了一些奇怪的編碼。 與源代碼中聲明的字符串中包含的空格相比,格式字符串中包含的空格使用不同的字節值表示。

以下測試用例演示了該問題:

[Test]
public void FormatSize_Regression() 
{
  string size1023 = FileHelper.FormatSize(1023);
  Assert.AreEqual("1 023 Bytes", size1023);
}

失敗:

String lengths are both 11. Strings differ at index 1.
    Expected: "1 023 Bytes"
    But was:  "1 023 Bytes"
    ------------^

FormatSize方法:

public static string FormatSize(long size) 
{
  if (size < 1024)
     return string.Format("{0:N0} Bytes", size);
  else if (size < 1024 * 1024)
     return string.Format("{0:N2} KB", (double)((double)size / 1024));
  else
     return string.Format("{0:N2} MB", (double)((double)size / (1024 * 1024)));
}

在Assert行上設置斷點時從VS立即窗口:

size1023
"1 023 Bytes"

System.Text.Encoding.UTF8.GetBytes(size1023)
{byte[12]}
    [0]: 49
    [1]: 194 <--------- space is 194/160 here? Unicode bytes indicate that space should be the 160. What is the 194 then?
    [2]: 160
    [3]: 48
    [4]: 50
    [5]: 51
    [6]: 32
    [7]: 66
    [8]: 121
    [9]: 116
    [10]: 101
    [11]: 115
System.Text.Encoding.UTF8.GetBytes("1 023 Bytes")
{byte[11]}
    [0]: 49
    [1]: 32  <--------- space is 32 here
    [2]: 48
    [3]: 50
    [4]: 51
    [5]: 32
    [6]: 66
    [7]: 121
    [8]: 116
    [9]: 101
    [10]: 115

System.Text.Encoding.Unicode.GetBytes(size1023)
{byte[22]}
    [0]: 49
    [1]: 0
    [2]: 160 <----------- 160,0 here
    [3]: 0
    [4]: 48
    [5]: 0
    [6]: 50
    [7]: 0
    [8]: 51
    [9]: 0
    [10]: 32
    [11]: 0
    [12]: 66
    [13]: 0
    [14]: 121
    [15]: 0
    [16]: 116
    [17]: 0
    [18]: 101
    [19]: 0
    [20]: 115
    [21]: 0
System.Text.Encoding.Unicode.GetBytes("1 023 Bytes")
{byte[22]}
    [0]: 49
    [1]: 0
    [2]: 32 <----------- 32,0 here
    [3]: 0
    [4]: 48
    [5]: 0
    [6]: 50
    [7]: 0
    [8]: 51
    [9]: 0
    [10]: 32
    [11]: 0
    [12]: 66
    [13]: 0
    [14]: 121
    [15]: 0
    [16]: 116
    [17]: 0
    [18]: 101
    [19]: 0
    [20]: 115
    [21]: 0

問題:這怎么可能?

我懷疑你當前的文化正在使用一個有趣的“千位”分隔符--U + 00A0,這是一個不間斷的空格字符。 這不是一個完全不合理的千位分隔符,說實話......這意味着你不應該得到這樣的文字顯示:

The size of the file is 1
023 bytes.

相反,你會得到

The size of the file is
1 023 bytes.

在我的盒子上,我得到了“1,023”。 您希望FormatSize方法使用當前文化還是特定文化? 如果它是當前的文化,你應該讓你的單元測試指定文化。 我有幾個用於此的包裝器方法:

internal static void WithInvariantCulture(Action action)
{
    WithCulture(CultureInfo.InvariantCulture, action);
}

internal static void WithCulture(CultureInfo culture, Action action)
{
    CultureInfo original = Thread.CurrentThread.CurrentCulture;
    try
    {
        Thread.CurrentThread.CurrentCulture = culture;
        action();
    }
    finally
    {
        Thread.CurrentThread.CurrentCulture = original;
    }            
}

所以我可以跑:

WithInvariantCulture(() =>
{
    // Body of test
};

等等

如果你想測試你得到的確切字符串,你可以使用:

Assert.AreEqual("1\u00A0023 Bytes", size1023);

UTF8中的Unicode 160 不是由單個字節160表示,而是由兩個字節表示。 沒有檢查,我打賭那些是194 + 160。

實際上,超過127的任何Unicode代碼點都由多個字節表示。

我猜你的CultureInfo使用一個不間斷的空間(160)作為千個分組分隔符,而不是像你自己鍵入的簡單空格(32)。

194,160是代碼點160的utf8:非破壞空間 - &nbsp; 在HTML中。

這是有道理的,你不希望將一個數字視為幾個單詞。

簡而言之,你的測試揭示了一個有缺陷的假設 - 太棒了! 但是,就單元測試而言,您的測試存在問題; 在轉換為字符串和從字符串轉換時,應始終包含CultureInfo對象 - 否則,單元測試可能會失敗,具體取決於登錄用戶的區域性設置。 您期望一種特定形式的字符串格式 - 確保明確說明您期望的CultureInfo。

也許您可以在Assert.Equal方法中更改測試字符串以使用CultureInfo.CurrentCulture.NumberFormat.NumberGroupSeparator而不是單個空格字符?

160是一個非破壞性的空間,這是有道理的,因為你不希望你的數字在行之間分開。 但是194 ......哦,是的。 UTF8雙倍字節。

首先,.NET中的所有字符串都是Unicode,因此獲取UTF8字節是沒用的。 其次,在比較字符串時,您應該指定文化信息,在使用string.format時,您應該使用IFormatProvider。 這樣您可以控制這些函數中使用的字符。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM