Проблема дублирования строк в C # - PullRequest
2 голосов
/ 05 августа 2009

Я использую VSTS 2008 + C # + .Net 3.0. У меня есть две строки ввода, я думаю, что они разные. Но следующий код C # думает, что они одинаковы, и выдает System.Data.ConstraintException, говорит, что имя столбца, напротив, уникально, но значение уже существует Есть идеи, что не так?

Вот мой код и мои входные строки,

шестнадцатеричный вид моих входных строк,

http://i30.tinypic.com/2anx2b.jpg

Блокнот моих строк ввода,

http://i30.tinypic.com/2q03hn4.jpg

Мой код,

    static void Main(string[] args)
    {
        string[] buf = new string[] { "2ch", "2ch" };

        DataTable bulkInserTable = new DataTable("BulkTable");
        DataColumn column = null;
        DataRow row = null;

        column = new DataColumn();
        column.DataType = System.Type.GetType("System.String");
        column.ColumnName = "Name";
        column.ReadOnly = true;
        column.Unique = true;
        bulkInserTable.Columns.Add(column);

        foreach (string item in buf)
        {
            row = bulkInserTable.NewRow();
            row["Name"] = item;
            bulkInserTable.Rows.Add(row);
        }
    }

РЕДАКТИРОВАТЬ 1:

Моя путаница в том, почему C # Dictionary думает, что они разные, но DataSet думает, что они одинаковы. Любое решение, чтобы сделать поведение последовательным? Вот мой код, чтобы доказать, что C # Dictionary думает, что они разные, возвращаемый массив buf состоит из двух элементов.

            Dictionary<string, bool> dic = new Dictionary<string, bool>();
            foreach (string s in buf)
            {
                dic[s] = true;
            }
            buf = new List<string>(dic.Keys).ToArray(); // we got two strings here, other than one, which proves Dictionary thinks the two strings are different.

Ответы [ 4 ]

6 голосов
/ 05 августа 2009

Это зависит от того, что вы подразумеваете под «тем же».

Две строки имеют разные значения Unicode, но я подозреваю, что при некоторых правилах нормализации они будут одинаковыми. Чтобы другие могли легко воспроизвести его без проблем с вырезанием и вставкой, вторая строка:

"\uff12\uff43\uff48"

Это "полная ширина" версии "2ch".

РЕДАКТИРОВАТЬ: Чтобы ответить на ваше редактирование, очевидно, что DataSet использует другую идею равенства, тогда как если вы не предоставите что-то конкретное, Dictionary будет использовать порядковые сравнения (как предусмотрено самой строкой).

РЕДАКТИРОВАТЬ: Я почти уверен, что проблема в том, что DataTable использует CompareOptions.IgnoreWidth:

using System;
using System.Data;
using System.Globalization;

class Test
{
    static void Main()
    { 
        string a = "2ch";
        string b = "\uff12\uff43\uff48";

        DataTable table = new DataTable();            
        CompareInfo ci = table.Locale.CompareInfo;

        // Prints 0, i.e. equal
        Console.WriteLine(ci.Compare(a, b, CompareOptions.IgnoreWidth));
    }
}

РЕДАКТИРОВАТЬ: Если для свойства DataTable CaseSensitive установлено значение true, я подозреваю, что оно будет вести себя так же, как Dictionary.

5 голосов
/ 05 августа 2009

хорошо, для начала вам нужен пример кода:

foreach (string item in buf)
{
    row = bulkInserTable.NewRow();
    row["Name"] = item;
    bulkInserTable.Rows.Add(row);
}

Хотя это все еще показывает проблему, по крайней мере, по реальной причине

Причиной этого является то, что при создании таблицы данных применяются параметры сравнения по умолчанию:

this._compareFlags = CompareOptions.IgnoreWidth 
                     CompareOptions.IgnoreKanaType | 
                     CompareOptions.IgnoreCase;

Из документов Ширина игнорирования:

Указывает, что сравнение строк должно игнорировать ширину символа. Например, японские символы катакана могут быть написаны как полная ширина или половина ширины. Если выбрано это значение, символы катакана, написанные как полная ширина, считаются равными тем же символам, написанным как полуширина.

System.Globalization.CultureInfo.CurrentCulture.CompareInfo.Compare(
    "2ch", "2ch", System.Globalization.CompareOptions.IgnoreWidth);

возвращает 0, то есть идентично

Я настоятельно рекомендую вам сделать считать такие значения идентичными или вызвать дальнейшую путаницу, однако, если вы действительно хотите изменить это:

//CaseSensitive property uses this under the hood
internal bool SetCaseSensitiveValue(
    bool isCaseSensitive, bool userSet, bool resetIndexes)
{
    if (!userSet && (
        this._caseSensitiveUserSet || (this._caseSensitive == isCaseSensitive)))
    {
        return false;
    }
    this._caseSensitive = isCaseSensitive;
    if (isCaseSensitive)
    {
        this._compareFlags = CompareOptions.None;
    }
    else
    {
        this._compareFlags = CompareOptions.IgnoreWidth | 
                             CompareOptions.IgnoreKanaType | 
                             CompareOptions.IgnoreCase;
    }
    if (resetIndexes)
    {
        this.ResetIndexes();
        foreach (Constraint constraint in this.Constraints)
        {
            constraint.CheckConstraint();
        }
    }
    return true;
}

Таким образом, вы можете игнорировать регистр и полностью отключить опции комплексного сравнения.

Если вы хотите создать словарь с таким же поведением, используйте следующий компаратор:

public class DataTableIgnoreCaseComparer : IEqualityComparer<string>
{
    private readonly System.Globalization.CompareInfo ci =
        System.Globalization.CultureInfo.CurrentCulture.CompareInfo; 
    private const System.Globalization.CompareOptions options = 
        CompareOptions.IgnoreCase | 
        CompareOptions.IgnoreKanaType | 
        CompareOptions.IgnoreWidth;

    public DataTableIgnoreCaseComparer() {}

    public bool Equals(string a, string b)
    {
        return ci.Compare(a, b, options) == 0;
    }

    public int GetHashCode(string s)
    {
        return ci.GetSortKey(s, options).GetHashCode();
    }
}
5 голосов
/ 05 августа 2009

Куда вы кладете строку в ряд? Мне кажется, что вы создаете пустые строки и вставляете 2 из них?

Как то так?

        foreach (string item in buf)
        {
            row = bulkInserTable.NewRow();
            row["Name"] = item;//Set the data<------------
            bulkInserTable.Rows.Add(row);
        }
1 голос
/ 05 августа 2009

Похоже, кодировка отличается от второй строки. При отладке вторая строка возвращается как мусор. Если я удалю вторую строку и введу «2 c h» в Visual Studio, она будет работать правильно.

...