В C # в чем разница между ToUpper () и ToUpperInvariant ()?

133

В C #, в чем разница между ToUpper()и ToUpperInvariant()?

Можете ли вы привести пример, где результаты могут отличаться?

Лил Лэнси
источник
3
[Организация] Должен ли этот вопрос иметь тег "интернационализация"?
Джассо

Ответы:

154

ToUpperиспользует текущую культуру. ToUpperInvariantиспользует инвариантную культуру.

Каноническим примером является Турция, где верхний регистр «я» не «я».

Пример кода, показывающий разницу:

using System;
using System.Drawing;
using System.Globalization;
using System.Threading;
using System.Windows.Forms;

public class Test
{
    [STAThread]
    static void Main()
    {
        string invariant = "iii".ToUpperInvariant();
        CultureInfo turkey = new CultureInfo("tr-TR");
        Thread.CurrentThread.CurrentCulture = turkey;
        string cultured = "iii".ToUpper();

        Font bigFont = new Font("Arial", 40);
        Form f = new Form {
            Controls = {
                new Label { Text = invariant, Location = new Point(20, 20),
                            Font = bigFont, AutoSize = true},
                new Label { Text = cultured, Location = new Point(20, 100),
                            Font = bigFont, AutoSize = true }
            }
        };        
        Application.Run(f);
    }
}

Для получения дополнительной информации о турецком языке см. Этот пост в блоге по тестированию Турции .

Я не удивлюсь, узнав, что существуют различные другие проблемы с заглавными буквами, связанные с элитированными персонажами и т. Д. Это только один пример, который я знаю из головы ... отчасти потому, что он укусил меня много лет назад в Java, где я был выше -составить строку и сравнить ее с "MAIL". Это не сработало так хорошо в Турции ...

Джон Скит
источник
45
хаха, я прочитал это, думая ... «В Турции нет буквы« я »»
Джефф Меркадо
Уже почти 2019 год, и у меня есть Visual Studio, предлагающая ımageв качестве имени поля для ImageUnity 3D спам и внутреннюю ошибку консоли Unable to find key name that matches 'rıght'в «английской» Windows с региональными настройками Турции для даты и времени. Похоже, иногда даже Microsoft не проходит тест на Турцию, язык ПК даже не турецкий, просто смеется.
Гуней Озсан
28

Ответ Джона идеален. Я просто хотел добавить, что ToUpperInvariantэто то же самое, что звонить ToUpper(CultureInfo.InvariantCulture).

Это делает пример Джона немного проще:

using System;
using System.Drawing;
using System.Globalization;
using System.Threading;
using System.Windows.Forms;

public class Test
{
    [STAThread]
    static void Main()
    {
        string invariant = "iii".ToUpper(CultureInfo.InvariantCulture);
        string cultured = "iii".ToUpper(new CultureInfo("tr-TR"));

        Application.Run(new Form {
            Font = new Font("Times New Roman", 40),
            Controls = { 
                new Label { Text = invariant, Location = new Point(20, 20), AutoSize = true }, 
                new Label { Text = cultured, Location = new Point(20, 100), AutoSize = true }, 
            }
        });
    }
}

Я также использовал New Times Roman, потому что это более прохладный шрифт.

Я также установил свойство Form's Fontвместо двух Labelэлементов управления, потому что Fontсвойство наследуется.

И я сократил несколько других строк только потому, что мне нравится компактный (например, не производственный) код.

У меня действительно не было ничего лучшего, чтобы сделать в данный момент.

Tergiver
источник
5
«Ответ Джона идеален». Разговор об избыточном утверждении. ;)
крильгар
1
Метод ToUpper не имеет для меня перегрузки параметров? была ли более старая версия? Я не понимаю
batmaci
Я не знаю, это задокументировано здесь: msdn.microsoft.com/en-us/library/system.string.toupper.aspx
Tergiver
12

String.ToUpperи String.ToLowerможет давать разные результаты для разных культур. Самым известным примером является турецкий пример , в котором преобразование строчной латинской «i» в прописную приводит не к заглавной латинской «I», а к турецкой «I».

Прописная буква I в зависимости от культуры, верхний ряд - строчные буквы, нижний ряд - прописные буквы

Что касается меня, это сбивало с толку даже изображение выше ( исходный код ), я написал программу (см. Исходный код ниже), чтобы увидеть точный результат для турецкого примера:

# Lowercase letters
Character              | UpperInvariant | UpperTurkish | LowerInvariant | LowerTurkish
English i - i (\u0069) | I (\u0049)     | I (\u0130)   | i (\u0069)     | i (\u0069)
Turkish i - ı (\u0131) | ı (\u0131)     | I (\u0049)   | ı (\u0131)     | ı (\u0131)

# Uppercase letters
Character              | UpperInvariant | UpperTurkish | LowerInvariant | LowerTurkish
English i - I (\u0049) | I (\u0049)     | I (\u0049)   | i (\u0069)     | ı (\u0131)
Turkish i - I (\u0130) | I (\u0130)     | I (\u0130)   | I (\u0130)     | i (\u0069)

Как вы видете:

  1. Прописные строчные буквы и строчные прописные буквы дают разные результаты для инвариантной культуры и турецкой культуры.
  2. Прописные буквы в верхнем регистре и строчные буквы в нижнем регистре не действуют, независимо от языка и региональных параметров.
  3. Culture.CultureInvariant оставляет турецкие символы как есть
  4. ToUpperи ToLowerявляются обратимыми, то есть нижний регистр символа после верхнего регистра приводит его к исходной форме, если для обеих операций использовалась одна и та же культура.

Согласно MSDN , для Char.ToUpper и Char.ToLower турецкий и азербайджанский являются единственными затронутыми культурами, потому что они единственные с односимвольными различиями в регистре. Для строк могут быть затронуты другие культуры.


Исходный код консольного приложения, используемого для генерации вывода:

using System;
using System.Globalization;
using System.Linq;
using System.Text;

namespace TurkishI
{
    class Program
    {
        static void Main(string[] args)
        {
            var englishI = new UnicodeCharacter('\u0069', "English i");
            var turkishI = new UnicodeCharacter('\u0131', "Turkish i");

            Console.WriteLine("# Lowercase letters");
            Console.WriteLine("Character              | UpperInvariant | UpperTurkish | LowerInvariant | LowerTurkish");
            WriteUpperToConsole(englishI);
            WriteLowerToConsole(turkishI);

            Console.WriteLine("\n# Uppercase letters");
            var uppercaseEnglishI = new UnicodeCharacter('\u0049', "English i");
            var uppercaseTurkishI = new UnicodeCharacter('\u0130', "Turkish i");
            Console.WriteLine("Character              | UpperInvariant | UpperTurkish | LowerInvariant | LowerTurkish");
            WriteLowerToConsole(uppercaseEnglishI);
            WriteLowerToConsole(uppercaseTurkishI);

            Console.ReadKey();
        }

        static void WriteUpperToConsole(UnicodeCharacter character)
        {
            Console.WriteLine("{0,-9} - {1,10} | {2,-14} | {3,-12} | {4,-14} | {5,-12}",
                character.Description,
                character,
                character.UpperInvariant,
                character.UpperTurkish,
                character.LowerInvariant,
                character.LowerTurkish
            );
        }

        static void WriteLowerToConsole(UnicodeCharacter character)
        {
            Console.WriteLine("{0,-9} - {1,10} | {2,-14} | {3,-12} | {4,-14} | {5,-12}",
                character.Description,
                character,
                character.UpperInvariant,
                character.UpperTurkish,
                character.LowerInvariant,
                character.LowerTurkish
            );
        }
    }


    class UnicodeCharacter
    {
        public static readonly CultureInfo TurkishCulture = new CultureInfo("tr-TR");

        public char Character { get; }

        public string Description { get; }

        public UnicodeCharacter(char character) : this(character, string.Empty) {  }

        public UnicodeCharacter(char character, string description)
        {
            if (description == null) {
                throw new ArgumentNullException(nameof(description));
            }

            Character = character;
            Description = description;
        }

        public string EscapeSequence => ToUnicodeEscapeSequence(Character);

        public UnicodeCharacter LowerInvariant => new UnicodeCharacter(Char.ToLowerInvariant(Character));

        public UnicodeCharacter UpperInvariant => new UnicodeCharacter(Char.ToUpperInvariant(Character));

        public UnicodeCharacter LowerTurkish => new UnicodeCharacter(Char.ToLower(Character, TurkishCulture));

        public UnicodeCharacter UpperTurkish => new UnicodeCharacter(Char.ToUpper(Character, TurkishCulture));


        private static string ToUnicodeEscapeSequence(char character)
        {
            var bytes = Encoding.Unicode.GetBytes(new[] {character});
            var prefix = bytes.Length == 4 ? @"\U" : @"\u";
            var hex = BitConverter.ToString(bytes.Reverse().ToArray()).Replace("-", string.Empty);
            return $"{prefix}{hex}";
        }

        public override string ToString()
        {
            return $"{Character} ({EscapeSequence})";
        }
    }
}
krzychu
источник
Таблица случаев была очень полезной. Спасибо!
VoteCoffee
2

в английском нет разницы. только в турецкой культуре можно найти разницу.

Stefanvds
источник
13
И вы уверены, что турецкий язык - единственная культура в мире, в которой правила для верхнего регистра отличаются от правил английского? Я считаю, что трудно поверить.
Джоэл Мюллер,
3
Турецкий - наиболее часто используемый, но не единственный пример. И это язык, а не культура, у которой есть четыре разных «я». Тем не менее, +1 за турецкий.
Armstrongest
конечно, должны быть и другие. большинство людей в любом случае никогда не встретят эти языки в программировании,
Стефанвдс,
8
Конечно, будут. Веб-приложения открыты для всего земного шара, и хорошо установить свои параметры. Что делать, если вы работаете с устаревшей базой данных, которая не поддерживает Unicode? Какие символы вы примете в качестве имени пользователя? Что, если вам нужно указать имена клиентов в устаревшей ERP, построенной на COBOL? Много случаев, когда важна культура. Не говоря уже о датах и ​​цифрах. 4.54 написано 4,54 на некоторых языках. Притворяться, что этих других языков не существует, в долгосрочной перспективе далеко не уедешь.
Armstrongest
очевидно, что культура важна для дат и чисел, я просто говорю, что большинство людей никогда не встретят языки, которые имеют разные результаты в toUpper и toUpperInvariant.
Stefanvds