#.net #string #bytearray
#.net #строка #массивы
Вопрос:
У меня возникла проблема с извлечением зашифрованных данных из поля NVARCHAR в нашей базе данных SQL Server (2008R2), где оказалось, что для некоторых записей строковое значение данных в моем приложении C # .NET отличается от значения в записи базы данных. Это было довольно сложно доказать, но в конце концов я обнаружил, что, посмотрев на представление строк в байтах [], различия действительно были.
Продолжая играть, я смог создать это тестовое приложение, которое меня немного беспокоит. Я взял массив байт (преобразованный из шестнадцатеричного для простоты настройки), преобразовал его в строку с кодировкой Unicode и обратно в массив байт и увидел, что результирующий массив байт отличается от исходного! В приведенном ниже коде первая шестнадцатеричная строка выполняется с ошибкой, в то время как вторая работает.
Что-то не так с моим методом здесь (и я не имею в виду попытку преобразовать массивы байтов в строки) или потенциально что-то не так в .NET framework?
using System;
namespace ByteArrayTest
{
class Program
{
static void Main(string[] args)
{
Console.WindowWidth = 80;
Console.Clear();
foreach (string s in new string[]
{
"00CD6C8300C2A2C09B9E6B1F258F7B1101000000AB4CB23EBE32F0DD",
"00CD6C8300C2A2C09B9E6B1F258F7B1101000000E12617F83C3F7F6A"
}
)
{
byte[] b1 = System.Runtime.Remoting.Metadata.W3cXsd2001.SoapHexBinary.Parse(s).Value;
string tmp = System.Text.Encoding.Unicode.GetString(b1);
byte[] b2 = System.Text.Encoding.Unicode.GetBytes(tmp);
Console.WriteLine("Orig: {0}", s);
string s2 = BitConverter.ToString(b2).Replace("-", "");
Console.WriteLine("Conv: {0}", s2);
Console.WriteLine(s == s2 ? "EQUAL :-)" : "** NOT EQUAL **");
Console.WriteLine();
}
Console.WriteLine("Press ENTER to exit...");
Console.ReadLine();
}
}
}
Я использую VS2010 и протестировал это в .NET framework 4 и 3.5, и результаты этого таковы:
Orig: 00CD6C8300C2A2C09B9E6B1F258F7B1101000000AB4CB23EBE32F0DD
Conv: 00CD6C8300C2A2C09B9E6B1F258F7B1101000000AB4CB23EBE32FDFF
** NOT EQUAL **
Orig: 00CD6C8300C2A2C09B9E6B1F258F7B1101000000E12617F83C3F7F6A
Conv: 00CD6C8300C2A2C09B9E6B1F258F7B1101000000E12617F83C3F7F6A
EQUAL :-)
С уважением,
Ответ №1:
Если вы пытаетесь сохранить произвольные непрозрачные двоичные данные, которые на самом деле не являются текстом в поле NVARCHAR, вам следует использовать кодировку base64 для их кодирования. Попытка просто обработать это как текстовую кодировку в UTF-16 (что вы здесь и делаете) — принципиально плохая идея, и с большой вероятностью приведет к потере данных. В качестве одного примера того, где это могло произойти, вы могли бы получить строку, которая содержит половину суррогатной пары без другой половины.
Я предполагаю, что ваши «зашифрованные данные» были сохранены простым вызовом, Encoding.Unicode.GetString(bytes)
где bytes
находятся зашифрованные данные? Если да, то это определенно не тот путь, которым нужно идти. Использовать:
string text = Convert.ToBase64String(bytes);
вместо этого и при извлечении данных используйте
byte[] bytes = Convert.FromBase64String(text);
В качестве альтернативы, используйте поле базы данных, которое в первую очередь предназначено для двоичных данных.
РЕДАКТИРОВАТЬ: (Скопировано из моего комментария) Приведенный вами пример завершается с ошибкой в конце, преобразуя U DDF0 в U FFFD. На самом деле это именно тот сценарий, о котором я упоминал выше — U DDF0 является «низким суррогатом», но у него нет соответствующего «высокого суррогата», поэтому кодирование.getString преобразует этот символ в U FFFD, который является «символом замены», который является (из диаграммы Юникода)
используется для замены входящего символа, значение которого неизвестно или непредставимо в Unicode
IIRC, вы можете указать, что Encoding
делает IIRC, когда он обнаруживает неверные двоичные данные (что фактически и является тем, что вы ему предоставляете), и потенциально заставить его выдавать исключение вместо этого.
Комментарии:
1. «В качестве альтернативы используйте поле базы данных, которое в первую очередь предназначено для двоичных данных». — звучит как хороший совет!
2. Спасибо, Джон, Да, мы понимаем, что поля базы данных для хранения зашифрованных данных, вероятно, должны быть VARBINARY-типами, но, сказав это, я думаю, мне больше интересно узнать о конкретном примере, который я опубликовал выше.
3. @Mitch: Бывают случаи, когда использование строкового представления упрощает жизнь — например, его просто вырезать и вставить. Но да, заставить хранилище отражать то, что вы пытаетесь сохранить, обычно является хорошим планом 🙂
4. @MattA: Я не на компьютере с . На данный момент установлена сеть, поэтому я не могу легко определить. Если бы вы могли сократить пример до одного образца данных, в идеале меньшего размера (инициализированного как прямой массив байтов, чтобы еще проще было тестировать что-то вроде ideone.com ) это помогло бы.
5. @MattA: Я только что попробовал ваш тест под ideone.com , и он показал РАВНОЕ значение дважды. Что это показывает на вашем компьютере?