Ошибки кодировки символов в .NET Core в Linux

Это сводило меня с ума в течение нескольких дней, и я, наконец, решил проблему с простой воспроизводимой проблемой.

У меня есть тестовый проект NUnit, это .NET Core 2.1. Он ссылается на библиотеку (назовем ее «Core»), которая является .NET Standard 2.0.

В моем тестовом проекте:

[TestCase(true, false)]
[TestCase(false, false)]
[TestCase(false, true)]
public void ShouldStartWith(bool useInternal, bool passStartsWith)
{
    var result = useInternal ? StartsWithQ("¿Que?") : StringUtilities.StartsWithQ("¿Que?", passStartsWith ? "¿" : null);
    result.ShouldBeTrue();
}

public static bool StartsWithQ(string s)
{
    return _q.Any(q => s.StartsWith(q, StringComparison.InvariantCultureIgnoreCase));
}

и в проекте Core в классе StringUtilities:

public static bool StartsWithQ(string s, string startsWith = null)
{
    return startsWith == null
        ? _q.Any(q => s.StartsWith(q, StringComparison.InvariantCultureIgnoreCase))
        : s.StartsWith(startsWith, StringComparison.InvariantCultureIgnoreCase);
}

Оба класса определили список специальных символов:

private static readonly List<string> _q = new List<string>
{
    "¡",
    "¿"
};

В среде Windows все тестовые примеры проходят успешно. Но когда те же тесты выполняются в среде Linux, тестовый пример ShouldStartWith(False,False) терпит неудачу!

Это означает, что когда все работает в тестовом проекте, сравнение строк работает правильно, и даже если вы передаете специальные символы методу StringUtilities, сравнение работает. Но когда вы сравниваете со строкой, которая была скомпилирована в проекте Core, специальные символы больше не эквивалентны!

Кто-нибудь знает, почему это? Это ошибка .NET? Как это обойти?


person Shaul Behr    schedule 16.07.2018    source источник
comment
Что file показывает как тип файла как для модульного теста, так и для реализации в Linux? На моей машине это отображается как Program.cs: C++ source, UTF-8 Unicode text, и тест проходит.   -  person omajid    schedule 16.07.2018
comment
@omajid Я не понимаю вопроса. Не могли бы вы объяснить, как пройти предложенный вами тест?   -  person Shaul Behr    schedule 17.07.2018
comment
Как и в случае с любым текстовым файлом, вы должны правильно передать кодировку символов программам, которые его читают. Какова кодировка ваших исходных файлов и что вы сообщаете компилятору? UTF-8 был бы хорошим планом. (file - это программа, которая угадывает кодировку. Она может оказаться полезной, а может и не оказаться полезной для демонстрации того, что файл не имеет кодировки, которая, по вашему мнению, есть. Требуется некоторая интерпретация.)   -  person Tom Blodget    schedule 17.07.2018
comment
@omajid Test.dll: PE32+ executable (console) x86-64 Mono/.Net assembly, for MS Windows Core.dll: PE32 executable (DLL) (console) Intel 80386 Mono/.Net assembly, for MS Windows   -  person Shaul Behr    schedule 17.07.2018
comment
@TomBlodget см. Комментарий выше   -  person Shaul Behr    schedule 17.07.2018
comment
Не могли бы вы запустить файл в исходных файлах? Все файлы, содержащие перевернутый вопросительный знак. Еще одна вещь, которую следует учитывать: можете ли вы использовать escape-код Unicode (\udddd) для этих символов, отличных от ascii? Помогает ли это результатам теста?   -  person omajid    schedule 17.07.2018
comment
Обсуждение было продолжено на github (но однозначного ответа нет): github.com/dotnet/corefx/issues / 31278   -  person Hermann.Gruber    schedule 05.06.2019


Ответы (1)


Кодировки ваших исходных файлов, скорее всего, не соответствуют друг другу и / или не соответствуют настройкам компилятора.

Пример:

Исходный файл, содержащий public void ShouldStartWith(bool useInternal, bool passStartsWith), может быть закодирован с использованием utf-8, в то время как исходный файл со списком закодирован в Latin-1 (или что-то в этом роде).

Когда мы проигрываем это:

  • Представление ¿ в utf-8 будет: 0xC2 0xBF.
  • Латинское-1 представление ¿ будет: 0xBF.

Таким образом, когда компилятор интерпретирует ваши исходные файлы как Latin-1, тогда он будет читать 2 байта в случае сохраненного файла utf-8 (и, согласно Latin-1, также 2 символа) и, следовательно, не сможет сопоставить строки.

Как уже было сказано в комментариях: лучший способ преодолеть это - закодировать исходные файлы в кодировке, которую ожидает компилятор.

Другой способ исключить операционную систему как источник ошибки: скопировать скомпилированный проект (dll - не перекомпилировать исходный код в другой операционной системе) из одной операционной системы в другую и выполнить код. Вы должны увидеть одинаковое поведение в обеих операционных системах с одним и тем же выводом двоичного компилятора.

person Matthias    schedule 14.04.2019
comment
.Net использует UTF-16 для представления символов и строк. См. Также Кодировку символов в .NET на MSDN. Если UTF-8 или Latin-1 используется без явного преобразования, тогда все в порядке. - person jww; 16.07.2019
comment
Вы путаете кодировку в исходных файлах и кодировку, используемую во время выполнения. - person Matthias; 23.07.2019