J'ai besoin de résoudre un problème de conversion de données dans SQL Server 2008. J'ai obtenu une modification des exigences sur le stockage des données. Sans grand soin, j'ai converti les données varbinary
existantes en varchar
en utilisant CONVERT(NVARCHAR(max), @bytearraydata, 1)
.
La même conversion en C # est effectuée à l'aide des méthodes Encoding.Default.GetString
Et Encoding.Default.GetBytes
. Encoding.Default.GetBytes(string)
récupère le bytearray tel qu'il était auparavant. Mais quand j'essaye de récupérer le tableau d'octets de chaîne que j'ai converti en utilisant CONVERT()
me donne un mauvais résultat.
Mon travail consiste à récupérer le tableau d'octets stocké sous forme de chaîne dans la base de données et à le convertir en tableau d'octets et enfin à rendre le contenu au format PDF. Les données passant par le mécanisme de codage (pendant la sauvegarde et la récupération) fonctionnent bien pour moi. Mais lorsque j'essaie de récupérer les données qui ont été converties à l'aide de CONVERT
, il ne parvient pas à générer de PDF.
Comment puis-je résoudre ce problème?
La colonne du tableau d'octets a été remplacée par une chaîne.
Conversion de données existante effectuée à l'aide de cette fonction:
Convert(NVARCHAR(MAX), @bytearraydata, 1)
Dans l'application, la conversion du tableau d'octets se fait à l'aide de Encoding.Default.GetString(bytearraydata)
Encoding
et CONVERT
ne sont-ils pas compatibles?
Lorsque vous utilisez Encoding.Default
le résultat dépend des paramètres locaux:
Un codage pour la page de codes ANSI actuelle du système d'exploitation.
La spécification est assez directe sur les dangers de l'utilisation de l'encodage par défaut et appelle très spécifiquement la recommandation de ne pas l'utiliser:
Différents ordinateurs peuvent utiliser différents encodages par défaut, et l'encodage par défaut peut même changer sur un seul ordinateur. Par conséquent, les données transmises en continu d'un ordinateur à un autre ou même récupérées à différents moments sur le même ordinateur peuvent être traduites incorrectement. En outre, le codage renvoyé par la propriété Default utilise la solution de secours la mieux adaptée pour mapper les caractères non pris en charge aux caractères pris en charge par la page de codes. Pour ces deux raisons, l'utilisation de l'encodage par défaut n'est généralement pas recommandée
Maintenant, pour une raison quelconque, vous vous attendez à ce que le codage local actuel aléatoire corresponde au codage du serveur. Même si la fonction CONVERT
ferait ce que vous croyez, les résultats seraient aléatoires et imprévisibles car:
De plus, CONVERT
ne fait pas ce que vous attendez. CONVERT
convertira un VARBINARY
en NVARCHAR
en utilisant le codage UCS-2, car il s'agit du codage utilisé par SQL Server pour les données NVARCHAR.
Je vous suggère d'aborder votre problème urgent en lisant d'abord cet article Le minimum absolu que chaque développeur de logiciels doit absolument connaître positivement Unicode et les jeux de caractères (pas d'excuses!) . Suivi avec Considérations internationales pour SQL Server .
Il y a quelques confusions dans la question, conduisant à des résultats inattendus:
Les termes VARCHAR
et NVARCHAR
sont utilisés de manière interchangeable (du moins il semble), mais ils sont très différents. NVARCHAR
est un encodage 16 bits - UTF-16 LE (Little Endian) pour être exact - et cela ne change pas. VARCHAR
est un codage 8 bits, et le codage 8 bits spécifique utilisé est déterminé par la page de codes associée au classement de la colonne (nous ignorerons les données VARCHAR
dans les littéraux de chaîne et pour l'instant puisque cette question concerne les données stockées dans une table). Si vous voulez savoir quelle page de code est associée à un classement particulier, vous pouvez utiliser les fonctions intégrées COLLATIONPROPERTY
:
SELECT COLLATIONPROPERTY(N'Latin1_General_100_CI_AS_SC', 'CodePage') AS [CodePage];
-- 1252
Lors de la conversion entre VARBINARY
et VARCHAR
ou NVARCHAR
, vous devez faire attention à être cohérent avec ce type de données de chaîne. Vous ne pouvez pas convertir de VARCHAR
en VARBINARY
, puis prendre le même VARBINARY
et le convertir en NVARCHAR
.
La classe Encoding
dans .NET représente un encodage particulier du texte, qu'il soit 7 bits, 8 bits, 16 bits, 32 bits ou variable (comme UTF-8). Pour obtenir des résultats "attendus", vous devez créer un codage qui correspond à ce dont vous avez besoin de convertir vers ou depuis, en ce qui concerne la représentation byte[]
. Les chaînes dans .NET sont toujours UTF-16 LE (comme NVARCHAR
), et c'est à cela que fait référence le codage Unicode
dans .NET. La représentation byte[]
D'un encodage sera de n'importe quel encodage créé, mais la représentation sous forme de chaîne sera toujours UTF-16 LE. L'encodage à créer dépend donc du type de données que vous traitez:
Encoding.Unicode
COLLATIONPROPERTY(N'collation_name', 'CodePage')
, puis utilisez cette valeur int
dans Encoding.GetEncoding(CodePageIntValue)
.Lorsque vous utilisez la fonction intégrée CONVERT
, faites attention au numéro de "style" que vous utilisez. Par exemple:
SELECT CONVERT(VARBINARY(50), N'bob');
-- 0x62006F006200
Maintenant, prenez cette valeur renvoyée VARBINARY
et convertissez-la en NVARCHAR
, en utilisant des valeurs de "style" de 0 (par défaut) et 1 (qui est ce que votre fonction CONVERT
utilise dans la question):
SELECT CONVERT(NVARCHAR(MAX), 0x62006F006200, 0) AS [Style_0],
CONVERT(NVARCHAR(MAX), 0x62006F006200, 1) AS [Style_1];
Retour:
Style_0 Style_1
bob 0x62006F006200
Donc, si l'énoncé suivant de la question est vrai:
Dans l'application, la conversion du tableau d'octets se fait à l'aide de
Encoding.Default.GetString(bytearraydata)
alors cela équivaudrait à utiliser VARCHAR
au lieu de NVARCHAR
, et une valeur de "style" de 0
(ou rien) au lieu de 1
:
CONVERT(VARCHAR(MAX), 0x62006F006200)
Je ne peux pas reproduire ce problème. Des étapes supplémentaires ont-elles été impliquées? Je peux convertir du texte en binaire et vice versa, ou vice versa, sans perte:
DECLARE @OrigText VARCHAR (100) = 'There once was a bear'
DECLARE @Binary VARBINARY(100) = CONVERT(VARBINARY(100), @OrigText)
DECLARE @RoundTripText VARCHAR (100) = CONVERT(VARCHAR (100), @Binary)
DECLARE @RoundTripBin VARBINARY(100) = CONVERT(VARBINARY(100), @RoundTripText)
SELECT @OrigText, @Binary, @RoundTripText, @RoundTripBin
Résultats:
Cela fonctionne également avec NVARCHAR
et en utilisant CAST
plutôt que CONVERT
. Notez que je ne spécifie pas de style pour CONVERT
; si vous en spécifiez une, je crois comprendre que votre texte doit être une chaîne hexadécimale. Est-ce ce que vous stockez ou est-ce un texte plus conventionnel?