Страницы

Поиск по вопросам

Показаны сообщения с ярлыком char. Показать все сообщения
Показаны сообщения с ярлыком char. Показать все сообщения

четверг, 9 апреля 2020 г.

Как прочитать с консоли строку неизвестной заранее длины не используя string?

#cpp #массивы #char #строки

                    
Я прекрасно понимаю, что можно использовать класс string и просто написать cin>>
 str; а затем получить количество элементов.
Я даже видел веселое решение при помощи malloc и realloc функций из C.
но все же, интересно, есть ведь какой-то способ при помощи средств C++, а конкретно
их потоков ввода и стандартных строк в виде массива символов прочитать из консоли строку,
не имея понятия о ее размере, получить впоследствии ее размер и положить в динамически
выделенный массив такого размера??
Вопрос не к спеху и не для какого-то задания, все чисто ради интереса, так что ваши
ответы, типа "не парься и пользуйся тем, что дают" также приветствуются )
    


Ответы

Ответ 1



cin >> str; запишет только до первого разделителя: http://ideone.com/Y5CJxD. Если под строкой понимается последовательность, которая заканчивается переводом строки ('\n'), то вам: std::getline() нужен: http://www.cplusplus.com/reference/string/string/getline/ А так, std::string это и так стандартное средство в C++. Если же хочется извратиться, то нужно читать блоками, проверять - есть ли перевод строки и агрегировать код, но лишнее нужно как-то хранить между вызовами. Либо, что проще, но медленнее - читать посимвольно, проверяя конец строки, например так: istream& getline(istream& is, char *&buffer) { std::istream::sentry s(is); if (s) { std::istreambuf_iterator it(is); std::istreambuf_iterator end; size_t size = 64; size_t grow = 64; size_t len = 0; buffer = new char[size]; while (it != end && *it != '\n') { if (len == (size - 1)) { buffer = buffer_realloc(buffer, size, size + grow); size += grow; } buffer[len++] = *it++; } buffer[len] = '\0'; } return is; } Здесь я не привожу функцию buffer_realloc() - она простая. Использовать как-то так: char *line = nullptr; while (getline(std::cin, line)) { if (line) { cout << line << endl; delete[] line; } } Либо вот вариант, где использует вектор: istream& getline(istream& is, vector &buffer) { std::istream::sentry s(is); if (s) { std::istreambuf_iterator it(is); std::istreambuf_iterator end; const size_t grow = 64; buffer.reserve(grow); while (it != end && *it != '\n') { if (buffer.size() == buffer.capacity() - 1) { buffer.reserve(buffer.size() + grow); } buffer.push_back(*it++); } buffer.push_back('\0'); } return is; } Использовать как-то так: vector line; while (getline(std::cin, line)) { if (!line.empty()) { cout << line.data() << endl; line.clear(); } } Тут реаллокация памяти ложится на плечи vector'а. От вас только почистить его (а можно и в гетлайн всунуть). Плюс бонусом сразу длинна строки за O(1): line.size() - 1 Вот законченные примеры: http://ideone.com/Huj4eQ http://ideone.com/CxNfg4 Но повторюсь: std::string стандартны для С++. Я слабо представляю условия, где есть потоки, но нет строк :)

Ответ 2



По сути, реализация cin выглядит так (игнорируя тот факт, что оба потока и строка шаблоны): std::istream& operator>> (std::istream& in, std::string& value) { std::istream::sentry cerberos(in); if (cerberos) { value.erase(); std::istreambuf_iterator it(in), end; if (it != end) { std::ctype const& ctype(std::use_facet >(in.getloc())); std::back_insert_iterator to(value); std::streamsize n(0), width(in.width()? in.width(): std::string::max_size()); for (; it != end && n != width && !ctype.is(std::ctype_base::space, *it); ++it, ++to) { *to = *it; } } } else { in.setstate(std::ios_base::failbit); } return in; } Выводы думаю вы можете сделать сами.

Ответ 3



Немного странный вопрос, т.к. если нужно сделать то же самое, что можно сделать с помощью std::string - следует хотя бы посмотреть реализацию operator>> для потока ввода и строкового аргумента. Основной смысл в том, что читаем посимвольно и посимвольно же добавляем. В итоге получим ещё и некоторое подобие реализации std::vector для символьного типа.

воскресенье, 29 марта 2020 г.

Как преобразовать string в char?

#cpp #строки #char #преобразование


Есть произвольная строка не больше 25 символов. Например "wo1fram"
Как преобразовать ее в массив char[255]?
Чтобы потом с char можно было работать как с полноценным массивом символов, оканчивающимся
нуль-символом.
    


Ответы

Ответ 1



Тут, видите ли, есть два решения. Одно - если вам надо только читать эту строку, или там, поменять в ней пару символов - но не менять ее размер (так что всякие strcpy отменяются) - то можно воспользоваться функциями c_str() и data(). Очень рекомендую внимательно почитать описания, а главное - ограничения, накладываемые этими функциями. И другое - если нужно работать с ней как со строкой в стиле С со всеми возможностями - то просто скопируйте ее в массив, типа char buf[255]; strcpy(buf,s.c_str()); или char * buf = strdup(s); Примерно так.

суббота, 7 марта 2020 г.

.c_str() возвращает какой-то бред

#cpp #кодировка #visual_cpp #char


День добрый! На C++ считываю данные из файла. В конце имею string, которую мне надо
представить в виде const char*, для чего использую .c_str(). Но возвращает он какой-то
бред (не только с кириллицей проблемы, а вообще с любым текстом). Файл сохранены в
кодировке ANSI. Как можно решить проблему?
Код чтения:

ifstream ifs(path);
if (ifs.is_open())
{
    string s;
    s.assign((istreambuf_iterator(ifs.rdbuf())), istreambuf_iterator());
    cout << s;
    ifs.close();
    return s.c_str();
}
else
    return "";

    


Ответы

Ответ 1



Ну, начнем с того, что вы возвращаете указатель локального объекта, который после вашего return уничтожается, и указатель указывает... куда? Дальше не пояснять?

вторник, 18 февраля 2020 г.

Уместно ли использовать тип unsigned char для хранения чисел

#c #char


Например, если мне нужна целочисленная переменная в диапазоне [0; 255] могу я использовать
тип unsigned char? Появились сомнения по этому поводу, т.к. этот тип обычно используется
для хранения символов, а использование в качестве числа будет не совсем очевидно. Интересует,
не повредит ли это читабельности программы.
    


Ответы

Ответ 1



unsigned char не хранит символы. Это - числовой тип данных, также как и signed char. Символы представляются типом данных char, который может быть как знаковым, так и беззнаковым - и это третий тип данных, который отличается от первых двух. Поэтому использовать unsigned char для хранения чисел - вполне уместно. Но если вам нужен именно диапазон [0; 255] - то правильнее использовать uint8_t, а не unsigned char - потому что размер unsigned char может отличаться от привычного на экзотических платформах, а uint8_t либо 8 бит, либо не существует.

Ответ 2



Ответ для вопроса с редакцией #1: Строго говоря, для диапазона [0; 255] нужен по крайней мере unsigned char. компилятор сам выбирает каким именно типом будет char - signed или unsigned, в моем случае - последним. Вы вот задали вопрос: "Интересует, не повредит ли это читабельности программы". И тут же оправдываете свою ошибку поведением компилятора. Нелогично. В таком контексте ответ может быть только - да, повредит и даже очень! Если Вы заботитесь о читабельности кода, то должны по крайней мере убрать из него лишние зависимости. И да, char, signed char и unsigned char - разные типы данных.

Ответ 3



Строго говоря, ответ "нет". Тому есть несколько причин. 1) неоднократно упомянутая, для хранения целых чисел в диапазоне [0, 255] почти всегда можно использовать тип unsigned char. То, что на Вашем компиляторе тип char беззнаковый, приведет лишь к тому, что попытавшись использовать свой код на другой платформе (другом компиляторе) Вы столкнетесь с непредсказуемым поведением кода. 2) Более важная причина. Стандарт никак не гарантирует, что тип char занимает в памяти 8 бит. Да, я не знаю современных платформ, где это было бы не так, но тем не менее, Вы получите код, не соответствующий стандарту. В конце концов, кодировка ASCII неспроста изначально 7-битная. Корректно было бы использовать тип uint8_t определенный в заголовочном файле . Но и это надо делать с осторожностью, ибо (provided only if the implementation directly supports the type) (см http://en.cppreference.com/w/c/types/integer)

воскресенье, 16 февраля 2020 г.

Уместно ли использовать тип unsigned char для хранения чисел

#c #char


Например, если мне нужна целочисленная переменная в диапазоне [0; 255] могу я использовать
тип unsigned char? Появились сомнения по этому поводу, т.к. этот тип обычно используется
для хранения символов, а использование в качестве числа будет не совсем очевидно. Интересует,
не повредит ли это читабельности программы.
    


Ответы

Ответ 1



unsigned char не хранит символы. Это - числовой тип данных, также как и signed char. Символы представляются типом данных char, который может быть как знаковым, так и беззнаковым - и это третий тип данных, который отличается от первых двух. Поэтому использовать unsigned char для хранения чисел - вполне уместно. Но если вам нужен именно диапазон [0; 255] - то правильнее использовать uint8_t, а не unsigned char - потому что размер unsigned char может отличаться от привычного на экзотических платформах, а uint8_t либо 8 бит, либо не существует.

Ответ 2



Ответ для вопроса с редакцией #1: Строго говоря, для диапазона [0; 255] нужен по крайней мере unsigned char. компилятор сам выбирает каким именно типом будет char - signed или unsigned, в моем случае - последним. Вы вот задали вопрос: "Интересует, не повредит ли это читабельности программы". И тут же оправдываете свою ошибку поведением компилятора. Нелогично. В таком контексте ответ может быть только - да, повредит и даже очень! Если Вы заботитесь о читабельности кода, то должны по крайней мере убрать из него лишние зависимости. И да, char, signed char и unsigned char - разные типы данных.

Ответ 3



Строго говоря, ответ "нет". Тому есть несколько причин. 1) неоднократно упомянутая, для хранения целых чисел в диапазоне [0, 255] почти всегда можно использовать тип unsigned char. То, что на Вашем компиляторе тип char беззнаковый, приведет лишь к тому, что попытавшись использовать свой код на другой платформе (другом компиляторе) Вы столкнетесь с непредсказуемым поведением кода. 2) Более важная причина. Стандарт никак не гарантирует, что тип char занимает в памяти 8 бит. Да, я не знаю современных платформ, где это было бы не так, но тем не менее, Вы получите код, не соответствующий стандарту. В конце концов, кодировка ASCII неспроста изначально 7-битная. Корректно было бы использовать тип uint8_t определенный в заголовочном файле . Но и это надо делать с осторожностью, ибо (provided only if the implementation directly supports the type) (см http://en.cppreference.com/w/c/types/integer)

пятница, 14 февраля 2020 г.

Связь int и char C++

#cpp #char


Здраствуйте! Проводил несколько опытов, и увидел, что когда читаешь цифру из чаровой
строки, то число ровно на 48 больше чем эта цифра. Правда ли это, и почему?
    


Ответы

Ответ 1



Посмотрите таблицу символов ASCII. В этой таблице можно увидеть, как каждый символ представляется числом. Для буквы 'a' это 97, а для '0' - 48. Соответственно, '1' - 49 и т.д. Сам же тип char обычно имеет размер 1 байт с диапазоном значений от -128 до 127, что позволяет хранить один символ. Конкретные диапазон значений и размер зависят от платформы.

Ответ 2



Правда то, что int('1' - '0') == 1. Какое значение имеет символ '0', зависит от реализации. Т.е. можно инициализировать строку символами_цифрами следующим образом string s(10, ' '); for (int i = 0; i < 10; ++i) s[i] = ('0' + i); //'0', '1'... ( isdigit(s[i]) == true; ) а вот инициализация следующим образом: for (int i = 48, j = 0; j < 10; ++i, ++j) s[j] = char(i); не является переносимым кодом

понедельник, 10 февраля 2020 г.

Количество сколько байтов в поле mysql таблицы

#mysql #sql #char


  CREATE TABLE `NewTable` (
  `usname`  varchar(32) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL
  )
  ENGINE=InnoDB
  DEFAULT CHARACTER SET=utf8 COLLATE=utf8_bin
  ROW_FORMAT=COMPACT
  ;


Если в поле будут введены не utf-8 символы, то максимум 32 символа можно хранить
в этом поле.

А если в поле будут utf-8 символы, то и utf-8 можно 32 шт сохранять в этом поле.

Сколько байт будет занимать поле если в нем только utf-8 символы?

И сколько байт, если на utf-8 символы будут хранится  в нем?

Режим traditional как-то влияет на это?



Изменил заголовок: добавил слово "сколько" - для автопоиска, если кто-то будет задавать
схожий вопрос.
    


Ответы

Ответ 1



Циферка в скобках varchar указывает именно число символов используемой кодировки. Длина в байтах напрямую зависит от используемой для поля кодировки CHARACTER SET. Для utf8 mysql использует максимум 3-байтовое представление. Поэтому varchar(32) будет занимать до 3 * 32 = 96 байт сами данные, плюс 1 или 2 байта на хранение длины данных в байтах. 96 меньше 255, поэтому требуется 1 байт. Итого такой varchar будет занимать от 1 до 97 байт на диске. Интересный момент, что в индексе и всяких сортировках/группировках в памяти это поле всегда развёрнуто до максимальной байтовой длины. Внимательный читатель может удивиться ещё одному моменту - ведь utf может занимать до 4 байт. А mysql под utf8 умеет хранить только 3 байта. Да, utf8 в остальном мире и utf8 в mysql - разные вещи. При попытке записать 4-байтный utf8 (именно в 4 байте размещены модные нынче смайлики, т.е. это совсем не экзотика мёртвых языков, а реально встречаемые данные) mysql вернёт ошибку и ничего не запишет. Для полноценного utf8 в mysql 5.5 добавлена кодировка utf8mb4. А utf8mb4 использует уже до 4 байт на символ. Что немного изменяет результат байтовой длины: 32 символа будут занимать до 32*4 = 128 байт, плюс один на хранение длины строки.

Ответ 2



Количество занимаемых байтов в данном случае будет на 1 больше байтового представления хранимой строки. Грубо, если в строке 2 символа однобайтовых и 2 двухбайтовых, то нужно 7 байтов. Подробнее см. на http://dev.mysql.com/doc/refman/5.7/en/storage-requirements.html И да, если используются корейские/китайские/японские символы, то может быть и 3 байта на символ. Об этом см. http://dev.mysql.com/doc/refman/5.7/en/charset-unicode-utf8.html

среда, 29 января 2020 г.

Как узнать, есть ли в строке большие буквы?

#char #java #строки


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


Ответы

Ответ 1



Регулярные выражения вас спасут. Если вы хотите по букве проверять, нужно использовать к строке с буквой метод str.matches("A-Z"), что даст логическое true, если символ есть большая буква. Немного пошаманив с регулярным выражением, можно написать и для проверки целой строки, но тут надо учитывать все возможные варианты. Проще - побуквенно. Есть ещё вариант - создать вторую строку через: String str2 = str1.toLowerCase(); и потом сравнить строки. Если будет несовпадение - где-то есть большие буквы, но этот вариант подходит, только если нужен сам факт наличия больших букв. Есть оракловая документация по регулярным выражениям, правда, на английском.

Ответ 2



Есть такой статический метод Character.isUpperCase(char); он сам проверяет буква ли это, и неважно какого алфавита. Как-то так String str="teststrinG123OPIvaaaav"; boolean result=false; char[] array=str.toCharArray(); for(char x:array){ if(Character.isUpperCase(x)){ result=true; break; } }

Ответ 3



Еще вариант с новыми Stream в JDK 8 public class UpperCaseTest { public static void main(String[] args) { String someupper = "this string Contains Some Uppercase Chars"; String alllower = "this string contains no uppercase chars"; System.out.println(containsUppercase(someupper)); System.out.println(containsUppercase(alllower)); } private static boolean containsUppercase(String str) { return str.codePoints().filter(Character::isUpperCase) .findFirst().isPresent(); } }

Ответ 4



Эх вы... Все же просто, надо всю строку перевести в lowerCase и сравнить с исходной строкой. Если строки идентичны, значит все символы были маленькие, иначе хотя бы одна была большая: boolean isAnyUpperCase(String s) { if(s.equals(s.toLowerCase())) return false; return true; } P.S. Regexp'ы и проч. LinQ - в данном контексте это зло

Ответ 5



Guava: CharMatcher.JAVA_UPPER_CASE.matchesAnyOf("abC"); // true CharMatcher.JAVA_UPPER_CASE.matchesAnyOf("abc"); // false

Ответ 6



На самом деле у каждого символа только 1 бит отвечает за регистр, можно только его и проверять(Если нужен только английский алфавит например)... Это и будет самый быстрый путь

Как привести массив byte[] к виду CharSequence?

#java #массивы #char #byte


Нужно написать класс AsciiCharSequence, который реализует хранение последовательности
ASCII-символов в массиве байт. По сравнению с классом String, хранящим каждый символ
как char, AsciiCharSequence будет занимать меньше памяти.

Класс AsciiCharSequence должен:


реализовывать интерфейс java.lang.CharSequence;
иметь конструктор, принимающий массив байт;
определять методы length(), charAt(), subSequence() и toString()


Ну вот я почти написал: 

public class AsciiCharSequence implements CharSequence {

     byte[] a1;

  public AsciiCharSequence(byte[] a){  //Конструктор класса
        a = this.a1;
    }

    @Override
    public String toString() {    //Переопределение toString()
        return "AsciiCharSequence{" +
                "a1=" + Arrays.toString(a1) +
                '}';
    }

    @Override
    public int length(){
        return a1.length;
    }; //Переопределение length()

    @Override
    public char charAt(int index){      //Переопределение charAt()
        char t000 = (char)a1[index];
        return t000;
    }

    @Override
    public CharSequence subSequence(int start, int end){  //Переопределение subSequence()
        CharSequence buf[] = new CharSequence[end - start];

       for(int i=start; i<=end;i++)
       {
           byte t = a1[i-start];
          buf[i]=(CharSequence)t;
       }
         return buf[end -start];
    }
}


Самый проблемный участок = Переопределение subSequence(). Это последние 12 строчек
с конца. Проблема такая: я никак не могу написать код, чтобы метод возвращал корректный
тип (CharSequence?). Если алгоритм формирования массива более-менее понятен, то преобразование
массива byte[] в CharSequence совсем не ясен. Сам метод должен применяться на массив
символов и возвращать массив символов с индексами лежащими от start до end. К примеру, 

String s0 = "Вова и Дима козлы";
   s0.subSequence(14,16) 


Должен возвращать "злы". 
    


Ответы

Ответ 1



Просто создайте новый экземпляр своего типа: @Override public CharSequence subSequence(int start, int end){ //Переопределение subSequence() return new AsciiCharSequence(Arrays.copyOfRange(a1, start, end)); } Заметьте, что toString() вы переопределили неправильно. В документации ясно сказано: String toString() Returns a string containing the characters in this sequence in the same order as this sequence. The length of the string will be the length of this sequence. То есть если вы переопределяете интерфейс CharSequence, вы обязаны вернуть в toString() строку, которая соответствует содержимому вашего CharSequence. Сделать это несложно: @Override public String toString() { //Переопределение toString() return new String(a1, java.nio.charset.StandardCharsets.ISO_8859_1); }

Ответ 2



Можно переписать класс в таком виде: public class AsciiCharSequence implements CharSequence { private byte[] data; public AsciiCharSequence(byte[] data) { this.data = data; } @Override public int length() { return data.length; } @Override public char charAt(int index) { return (char) (data[index] & 0xff); } @Override public CharSequence subSequence(int start, int end) { int length = end - start; byte[] bytes = new byte[length]; for (int i = 0, j = start; i < length; i++, j++) { bytes[i] = data[j]; } return new AsciiCharSequence(bytes); } @Override public String toString() { return new String(data); } } Дернуто с гитхаба. Автор - Михаил Валейко.

вторник, 28 января 2020 г.

Указатели в Си, функции

#c #массивы #указатели #char


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

Может кто нибудь объяснить, что да как с этим делом? Я понимаю, как работают указатели,
но чёт догнать не могу, почему в функции стоит входной параметр char, а отправляют
получается адрес указателя. И если можно, пример для использования возможных вариантов.
Заранее спасибо!
    


Ответы

Ответ 1



В выражениях массивы за редким исключением неявно преобразуются в указатель на свой первый элемент. Поэтому когда вы передаете массив в функцию, как в нижеприведенном примере void f( int *a, size_t n ) { for ( size_t i = 0; i < n; i++ ) printf( "%d ", a[i] ); printf( "\n" ); } int a[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; f( a, 10 ); то не надо указывать перед именем массива оператор &. Массив уже преобразован в указатель типа int *. Однако если вы имеете дело со скалярными объектами, как, например, выражение a[0], которое представляет собой первый элемент массива, то есть скалярный объект со значением 0, как следует из определения массива в примере выше, то если вы хотите передать его адрес в вышеуказанную функцию , то вам надо будет записать f( &a[0], 10 ); Фактически, данные два вызова f( a, 10 ); и f( &a[0], 10 ); эквивалентны, так как в обоих случаях передается адрес на первый элемент массива. В виду этого данные объявления функции эквивалентны и объявляют одну и ту же функцию void f( int a[10], size_t n ) void f( int a[20], size_t n ) void f( int a[], size_t n ) void f( int *a, size_t n ) Вы все эти объявления можете одновременно включить в программу, и программа будет успешно компилироваться. То же самое справедливо и для символьных массивов. Имейте в виду, что строковые литералы также имеют тип массивов. И если имеется вызов вида h( "Hello" ); где h - это некоторая функция, то в функцию передается адрес первого символа литерала, так как, как описано выше, данный литерал, который представляет из себя символьный массив, неявно преобразуется в указатель на свой первый символ. В заключение приведу демонстрационную программу, в которую также включен пример, показывающий, что строковые литералы - это массивы. #include void f( int a[10], size_t n ); void f( int a[20], size_t n ); void f( int a[], size_t n ); void f( int *a, size_t n ); void f( int *a, size_t n ) { for ( size_t i = 0; i < n; i++ ) printf( "%d ", a[i] ); printf( "\n" ); } void h( char c ) { printf( "%c\n", c ); } int main(void) { int a[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; f( a, 10 ); f( &a[0], 10 ); h( "Hello"[0] ); return 0; } Вывод программы на консоль 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 H

Ответ 2



char *name = "Я уже указатель :)"; function(name); function(name+3); // Я указатель на 3 символа дальше, чем name function(&name[3]); // А я не был, я был третьим символом строки, пока не взяли адрес char name='q'; // Я символ, а не указатель function(&name);

воскресенье, 12 января 2020 г.

Как кодировка *.java файла влияет на тип char в Java?

#java #char #encoding


Почему данный фрагмент кода выводит 0, если кодировка кода в Windows-1251 и 15 если
кодировка UTF-8?

String s1 = "П";
String s2 = "А";
System.out.println(s1.compareTo(s2));


В таблице UTF-8 отсутствует кириллица, а в Windows-1251 есть все кириллические символы.
Поправьте, если ошибаюсь.
    


Ответы

Ответ 1



Проведем эксперимент: public class Cp1251Src { public static void main( String[] args ) { String s1 = "П"; String s2 = "А"; System.out.println(s1.compareTo(s2)); } } Сохраним файл в кодировке cp1251, попробуем собрать javac 1.8.0_45 с указанием правильной кодировки: >javac -encoding cp1251 Cp1251Src.java >java Cp1251Src 15 15 - правильный ответ, т.к. String.compareTo возвращает разность первых отличающихся символов. Укажем неправильную кодировку: >javac -encoding utf8 Cp1251Src.java Cp1251Src.java:3: error: unmappable character for encoding utf8 String s1 = "?"; ^ Cp1251Src.java:4: error: unmappable character for encoding utf8 String s2 = "?"; ^ 2 errors javac отказывается компилировать, т.к. не может преобразовать байты файла в символы, используя указанную кодировку. IDEA у меня нет, но есть Eclipse. Если в нем указать кодировку файла UTF-8, то вместо "А" и "П" будет виден символ "�" (U+FFFD, REPLACEMENT CHARACTER). Код успешно скомпилируется, выполнится и выведет 0, т.к. строки теперь равны. Т.е. там, где javac из-за ошибки преобразования байт в символы отказывается продолжать работу, Eclipse (и, скорее всего, Idea), заменяют непреобразуемые байты на U+FFFD, и работает дальше. А если файл, сохраненный в кодировке Utf-8 скомпилировать с параметром -encoding cp1251, программа выведет 13.

Ответ 2



На тип char кодировка java-файла не влияет. Компилятор java не может волшебным образом догадаться, какая кодировка у вашего файла. Он использует какую-то кодировку по умолчанию, которую спрашивает у вашей системы. Чтобы всё работало, компилируйте с параметром javac -encoding utf-8 ... или javac -encoding cp1251 ..., указывая именно ту кодировку, которую вы реально используете.

понедельник, 6 января 2020 г.

Как перевести char в int, без изменения кодировки. Работа с StringBuffer

#java #кодировка #char


Задача моего метода- преобразовать каждое число типа StringBuffer в массив int.

При попытке преобразовать каждый символ "9 8 7 6 5 4 3 2 1 " с убранными пробелами
я получаю результат: {57,56,55,54,53,52,51,50,49} 

Как мне поместить в массив те же числа, что и в строке? 

public static void main(String[] args){
StringBuffer strB = new StringBuffer("9 8 7 6 5 4 3 2 1 ");
int arrInt=strBToArrInt(strB);
for(int i=0;i


Ответы

Ответ 1



В твоём случае он возвращает ASCI-код символа. Тебе надо использовать метод digit. Замени arrInt[i]= Integer.valueOf(strBuff.charAt(0)); На arrInt[i]= Character.digit(strBuff.charAt(0), 10); Но этот метод годен только для цифр. Если у тебя числа, то сначала надо разбить исходную строку на массив строк при помощи метода split. String []stringsArray=strBuff.toString().trim().split("\\s+"); А потом пройтись и распарсить каждую строку как число int[] arrInt =new int[stringsArray.length()]; for (int i = 0; i < stringsArray.length; i++) { arrInt[i]= Integer.parseInt(stringsArray[i]); }

Ответ 2



Конвертируйте строку в массив символов: char[] = String.toCharArray(); Используйте int[i] = Character.getNumericValue(char[i]);. Конечно, в вашем случае используйте charAt();, при чем в одной строке можно конвертировать к int.

вторник, 31 декабря 2019 г.

Почему Marshal.SizeOf возвращает 1 байт для переменной типа char?

#c_sharp #net #unicode #char #sizeof


Приведу код:

char charVal = '૧'; // код 0AE7
Console.WriteLine(sizeof(char)); // 2

Console.WriteLine(System.Runtime.InteropServices.Marshal.SizeOf(charVal)); // 1?


Здесь объявляем переменную типа char и пытаемся выяснить ее размер. Как видно, для
размера типа результат будет 2, тогда как для переменной результат будет 1. Что-то
находил по поводу того, что результат Marshal.SizeOf(charVal) зависит от установленного
значения CharSet, но так и не понял, как управлять всем этим. 

По идее, я указал в коде символ юникода, поэтому символ должен занимать 2 байта,
но оказывается, что это не так. Почему?
    


Ответы

Ответ 1



Короткий ответ: Потому что по-умолчанию (если не указана кодировка) символы маршалируются в ANSI. При этом кодировку можно установить с помощью [StructLayout] и [DllImport] (Default Marshaling for Characters): When a managed Char type, which has Unicode formatting by default, is passed to unmanaged code, the interop marshaler converts the character set to ANSI. You can apply the DllImportAttribute attribute to platform invoke declarations and the StructLayoutAttribute attribute to a COM interop declaration to control which character set a marshaled Char type uses. Использовать Marshal.SizeOf имеет смысл только при взаимодействии с неуправляемым кодом. Результат для char мало что означает. Длинный ответ: Вроде выковырял описание этого поведения из документации. В документации Marshal.SizeOf говорится, что для символов результат определяется установленным CharSet: ... For character types, the size is affected by the CharSet value applied to that class. ... Что это означает? Это означает, что если тип char используется в структуре, то размер, который для символа вернет SizeOf определяется свойством CharSet атрибута StructLayout: [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi)] public struct OneByte { public char ch; //1 } [StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)] public struct TwoByte { public char ch; //2 } Теперь осталось выяснить какая проставлена кодировка для структуры System.Char. В документации использованный атрибут явно не указан, но указывается, что по умолчанию используется ANSI: Char values and interop When a managed Char type, which is represented as a Unicode UTF-16 encoded code unit, is passed to unmanaged code, the interop marshaler converts the character set to ANSI by default. You can apply the DllImportAttribute attribute to platform invoke declarations and the StructLayoutAttribute attribute to a COM interop declaration to control which character set a marshaled Char type uses. Для верности проверяем исходный код Char: [System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)] public struct Char Как видим CharSet не задан, соответственно, он принимает значение по умолчанию. В документации указывается LPSTR (однобайтный тип): Indicates whether string data fields within the class should be marshaled as LPWSTR or LPSTR by default. Но опять для верности проверяем код StructLayoutAttribute и выясняем что по умолчанию используется CharSet.None: CharSet charSet = CharSet.None; switch (type.Attributes & TypeAttributes.StringFormatMask) { ... Который, согласно документации, устарел и эквивалентен Ansi. Marshal.SizeOf скорее всего просто проверяет значение кодировки в атрибуте и возвращает на его основании 1. Этот результат имеет мало смысла т.к. Char не будет маршалится как отдельная структура В практических сценариях маршалинга будет использоваться кодировка, заданная для конкретной структуры. В процессе поиска нашел актуальную цитату из старой книги .NET and COM: The Complete Interoperability Guide Адама Натана: Caution Never use Marshal.SizeOf to determine the "real size" of a managed type, because the results can be misleading. Just think of Marshal.SizeOf as Marshal.UnmanagedSizeOf. An appropriate use of Marshal.SizeOf is to fill a struct's "size field" when passing it to unmanaged code. ... Furthermore, you should never use Marshal.SizeOf with the System.Char type to determine the current platform's character size — use Marshal.SystemDefaultCharSize instead. This is because Marshal.SizeOf always reports 1 for the size of the System.Char type regardless of platform due to an extremely subtle reason. It reports the size of the System.Char value type in the mscorlib assembly, which has a single character field. Because the definition is marked with CharSet.Ansi, the structure would have a size of 1 byte if marshalled to unmanaged code as a plain struct. ... But because the System.Char type is treated specially as a character primitive type, it is never passed as a plain struct to unmanaged code. By default, character parameters and return types are marshalled as 2-byte Unicode characters, and character fields in a structure are marshalled depending on the structure's character set. Note that doing sizeof(char) in C# unsafe code always returns 2 regardless of platform because .Net characters are always Unicode. Не уверен насчет маршалинга параметров и возвращаемых значений, но остальная информация вроде бы не устарела.

C++ - некорректное определение отдельного символа в строке

#cpp #linux #unicode #char


Всем привет!
Проблема заключается в следующем - имеются Linux с Code:Blocks IDE и g++ в качестве
компилятора, программный код C++, а так же символьная строка (string либо char []),
которой присваивается текст в кириллических символах. При выводе всей строки, выводимый
текст в консоли отображается нормально. Но если обращаться к любому отдельному элементу
строки, то вместо символа выводится вопросительный знак на фоне ромба и я так понимаю,
что сама программа не может определить - какой это символ, так как условный оператор
на него не реагирует. Кодировка естественно - Unicode (UTF-8).  

#include 
#include 

using namespace std;

int main ()
{
  string word = "Слово";
  cout << word; //нормально выводится "Слово"
  cout << word [0]; //выводится '?'
  if (word [0] == "С") cout << word [0]; //оператор if считает, что false
  return 0;
}    


UPD: в общем и целом мне не нужна была большая программа, поэтому я просто создал
отдельную строку, к которой присваивается необходимая пара элементов проверяемой строки
содержащая один нужный символ ([0], [1]; [2], [3] и тд). Полученную строку вполне удаётся
использовать в условных операторах:

#include 
#include 

using namespace std;

int main ()
{
 string word = "Слово", letter;
 letter += word [0];
 letter += word [1];
 if (letter == "С") cout << letter;//true, выводится "С"
 return 0;
}


Разумеется, это выглядит топорно, но на мой взгляд это самый простой способ в контексте,
скажем например, университетской лабораторной работы.
Спасибо всем за помощь и полезную информацию (=
    


Ответы

Ответ 1



Кодировка UTF-8 это многобайтовая кодировка, один символ может кодироваться несколькими байтами. В частности русские буквы кодируются двумя байтами, например 'ы' кодируется как "\xd1\x8b". Когда Вы пытаетесь напечатать один байт закодированной буквы, например "\xd1" или "\x8b", то выводится как знак вопроса, потому что это не валидная UTF-8 строка.

Ответ 2



В качестве альтернативного решения, если вам нужна работа с отдельными символами, и вы уверены, что вам не придётся работать с экзотикой наподобие символов Unicode, лежащих за главной плоскостью, попробуйте перевести вашу программу на широкие строки: wstring word = L"Слово"; wcout << word; wcout << word[0]; if (word[0] == L'С') wcout << word[0]; Это повысит расход памяти, но избавит вас от великого и ужасного ICU. Тем не менее, это оставляет вас наедине с возможной диакритикой (акценты наподобие точек и крючочков у ü или там ç всё равно занимают отдельный wchar_t). В C++ нет строк, смиритесь. Всё вручную. Да, и убедитесь, что ваша консоль работает в нужном режиме (это системно-зависимая вещь).

Ответ 3



В общем и целом, мне не нужна была большая программа, поэтому я просто создал отдельную строку, к которой присваивается необходимая пара элементов проверяемой строки содержащая один нужный символ ([0], [1]; [2], [3] и тд). Полученную строку вполне удаётся использовать в условных операторах: #include #include using namespace std; int main () { string word = "Слово", letter; letter += word [0]; letter += word [1]; if (letter == "С") cout << letter;//true, выводится "С" return 0; } Разумеется, это выглядит топорно, но на мой взгляд это самый простой способ в контексте, скажем например, университетской лабораторной работы. Спасибо всем за помощь и полезную информацию (=

понедельник, 30 декабря 2019 г.

Перевод char или String в двоичный код (Java)

#java #строки #char


Ребят, вопрос, в принципе, не такой и сложный, однако, я гуглил, но не нашел толкового
ответа.
Как в Java перевести символ (char или даже лучше String) в двоичный код? 
То есть дана строка Str = "abс";, нужно перевести её в тот же стринг, но уже двоичным
кодом: 1100001 1100010 11010001 10000001.
    


Ответы

Ответ 1



Для этого есть готовый метод: String a = Integer.toBinaryString('a'); System.out.println(a); Выведет на экран: 1100001 Соответственно, если необходимо перевести целую строку в двоичный код, а не отдельный символ, можно написать для этого свой метод, например: public String stringToBinary(String s) { StringBuilder answer = new StringBuilder(); for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); answer.append(Integer.toBinaryString(c)).append(' '); } return answer.toString(); } Более красивое решение на Java 8: public String stringToBinary(String s) { return s .chars() .collect(StringBuilder::new, (sb, c) -> sb.append(Integer.toBinaryString(c)).append(' '), StringBuilder::append) .toString(); }

пятница, 27 декабря 2019 г.

Развернуть слово

#строки #char


Надо развернуть слова наоборот, чтобы при этом небуквенные символы оставались на
своих местах.

Например:
a1bcd => d1cba
    


Ответы

Ответ 1



Заводим указатели на начало и конец строки, идем в цикле, первый указатель увеличиваем, второй уменьшаем, цифры проскакиваем. Меняем между собой буквы на которые смотрят указатели. Завершаемся, когда указатели сравнялись. В виде кода как то так (правда вы язык не указали): int j = 0; int k = ch.length-1; while(j

Ответ 2



var s = "a1bcd"; var rev = s.match(/[a-z]/gi).reverse(), i = 0; var res = s.replace(/[a-z]/gi, m => rev[i++]); console.log(res);

Ответ 3



На C#: public string ReverseSkippingNonLetters(String source) { var result = new StringBuilder(); for (int i = 0; i < source.Length; i++) { Char forward = source[i], backward = source[source.Length - i - 1]; if(!Char.IsLetter(forward)) result.Append(forward); if(Char.IsLetter(backward)) result.Append(backward); } return result.ToString(); }

Ответ 4



Ну, вот развертка слова наоборот вручную без использования доп.методов с сортировкой, вдруг понадобится private char[] SortArray(char[] arr) { char max = arr[0]; for (int i = 0; i < arr.Length; i++ { max = arr[i]; for (int j = i; j < arr.Length; j++) { if (max < arr[j]) { max = arr[j]; arr[j] = arr[i]; arr[i] = max; } else if (max>='0'&&max<='9') { arr[i] = max; break; } } } return arr; }

Ответ 5



Решение на C++11 с использованием STL: #include #include #include std::string specReverse(const std::string& str) { std::string result(str); for (auto begin = result.begin(), end = result.end() - 1; begin < end; ) { if (!isalpha(*begin)) ++begin; else if (!isalpha(*end)) --end; else std::swap(*begin++, *end--); } return result; } int main() { const std::string input = "a1bcd"; std::cout << "Input=\"" << input << "\", output=\"" << specReverse(input) << "\"." << std::endl; }

воскресенье, 22 декабря 2019 г.

char[] в Character[]

#java #массивы #сортировка #char #java_8


Как перевести массив char[] к его оболочке Character[] для дальнейшей сортировки
в обратном порядке?
Делаю по аналогии как с int -> Integer:

int[] array = {...};
Integer[] newArray  = Arrays.stream(array).boxed().toArray(Integer[]::new);
Arrays.sort(newArray, Collections.reverseOrder());


Но с char так не работает:

Character[] newArray = Arrays.stream(array).boxed().toArray(Character[]::new);
Arrays.sort(newArray, Collections.reverseOrder());

    


Ответы

Ответ 1



Да, для примитивов char есть такая проблема. Можно например так её решить: char[] array = {'a','c','b'}; Character[] newArray = IntStream.range(0, array.length) .mapToObj(i -> array[i]) .toArray(Character[]::new); Arrays.sort(newArray, Collections.reverseOrder()); c, b, a

Ответ 2



Как вариант, можно использовать преобразование в строку и метод chars: char[] array = new char[] {'a', 'c', 'b'}; Character[] newArray = new String(array).chars() .mapToObj(i -> (char) i).toArray(Character[]::new);

Ответ 3



Используйте ArrayUtills Character[] charObjectArray = ArrayUtils.toObject(array); Arrays.sort(charObjectArray, Collections.reverseOrder());

пятница, 20 декабря 2019 г.

Почему при превышении размера символьного массива и передаче его в функцию не возникает ошибка?

#cpp #массивы #char


Когда отправляю символьный массив размера 20 в функцию, которая принимает символьный
массив размером до 5, не срабатывает ошибка, вроде того, что размер превышен, или что
нибудь подобное. Он пропускает и при этом он принимает всю строку, а не только первые
5 символов.

Почему это происходит?

#include 

#define DL_FIO 20

using namespace std;
void checkup(char str[20], int* fl);
int main()
{
    int k;
    char word[DL_FIO];
    cin >> word;
    int f1 = 1;
    checkup(word, &f1);
    cout << word;
}
void checkup(char str[5], int* fl)
{
    cout << str[9];
    int x, i, n, flag1 = 1;
    //для цифр
    if (*fl != 0)
    {
        for (n = 0; n < 5; n++)
        {
            if ((str[n] >= 'a' && str[n] <= 'z') || (str[n] >= 'A' && str[n] <= 'Z'))flag1 = 0;
        }
        while (strlen(str) > 5 || flag1 == 0)
        {
            puts("\nOnly numeral can be entered. Try again.");
            cin>>str;
            flag1 = 1;
            for (n = 0; n < 5; n++)
            {
                if ((str[n] >= 'a' && str[n] <= 'z') || (str[n] >= 'A' && str[n]
<= 'Z'))flag1 = 0;
            }
        }
    }
    //для фамилии
    else
    {
        for (n = 0; n < DL_FIO; n++)
        {
            if (str[n] >= '0' && str[n] <= '9')flag1 = 0;
        }
        while (strlen(str) > DL_FIO || flag1 == 0)
        {
            puts("\nOnly letters can be entered. Try again.");
            cin>>str;
            flag1 = 1;
            for (n = 0; n < DL_FIO; n++)
            {
                if (str[n] >= '0' && str[n] <= '9')flag1 = 0;
            }
        }
        (*fl)++;
    }
}

    


Ответы

Ответ 1



В объявлениях вида void checkup(char str[20], int* fl); размер массива не имеет никакого значения и никак не учитывается языком. Все объявления параметров типа "массив" в языках C и C++ всегда сразу автоматически трансформируются в параметры типа "указатель". Ваше вышепроцитированное объявление эквивалентно объявлениям void checkup(char str[123], int* fl); void checkup(char str[], int* fl); и в конечном итоге все они эквивалентны объявлению void checkup(char *str, int* fl); Никакого контроля размера массива тут нет и быть не может. Если в языке С++ вы хотите потребовать от вызывающего кода передачи массива какого-то однозначно заданного размера, то передавайте ваш массив по ссылке void checkup(char (&str)[20], int* fl);

Ответ 2



Для того, чтобы понять почему так происходит достаточно посмотреть в скомпилированной проге ее функции. Вот что вывел nm: 0000000000004060 B __bss_start 0000000000004298 b completed.7325 U __cxa_atexit@@GLIBC_2.2.5 w __cxa_finalize@@GLIBC_2.2.5 0000000000004050 D __data_start 0000000000004050 W data_start 00000000000010e0 t deregister_tm_clones 0000000000001150 t __do_global_dtors_aux 0000000000003db8 t __do_global_dtors_aux_fini_array_entry 0000000000004058 D __dso_handle 0000000000003dc0 d _DYNAMIC 0000000000004060 D _edata 00000000000042a0 B _end 00000000000014d4 T _fini 0000000000001190 t frame_dummy 0000000000003da8 t __frame_dummy_init_array_entry 0000000000002224 r __FRAME_END__ 0000000000004000 d _GLOBAL_OFFSET_TABLE_ 0000000000001458 t _GLOBAL__sub_I_main w __gmon_start__ 000000000000206c r __GNU_EH_FRAME_HDR 0000000000001000 t _init 0000000000003db8 t __init_array_end 0000000000003da8 t __init_array_start 0000000000002000 R _IO_stdin_used w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 00000000000014d0 T __libc_csu_fini 0000000000001470 T __libc_csu_init U __libc_start_main@@GLIBC_2.2.5 0000000000001195 T main U puts@@GLIBC_2.2.5 0000000000001110 t register_tm_clones 00000000000010b0 T _start U strlen@@GLIBC_2.2.5 0000000000004060 D __TMC_END__ 000000000000140f t __static_initialization_and_destruction_0(int, int) 00000000000011e4 T checkup(char*, int*) U std::ios_base::Init::Init()@@GLIBCXX_3.4 U std::ios_base::Init::~Init()@@GLIBCXX_3.4 0000000000004180 B std::cin@@GLIBCXX_3.4 0000000000004060 B std::cout@@GLIBCXX_3.4 0000000000002008 r std::piecewise_construct 0000000000004299 b std::__ioinit U std::basic_ostream >& std::operator<< >(std::basic_ostream >&, char)@@GLIBCXX_3.4 U std::basic_ostream >& std::operator<< >(std::basic_ostream >&, char const*)@@GLIBCXX_3.4 U std::basic_istream >& std::operator>> >(std::basic_istream >&, char*)@@GLIBCXX_3.4 обратите внимание на 38 строчку: 00000000000011e4 T checkup(char*, int*) Тобишь ваша функция принимает char * а не char[5] или char[20] хотя это и разные типы (но char[n] автоматом приводится к char *). Таким образом компилятор не видит никакой разницы между определением и реализацией этих функций (вы можете попробовать реализовать две функции принимаещией char[5] и char[20] и компилятор ругнется на то, что функции не могут иметь одинаковых сигнатур при том же названии)

воскресенье, 1 декабря 2019 г.

Сортировка массивов русских символов и строк с участием буквы Ё

#c_sharp #строки #сортировка #char


Простой пример кода, попробуем отсортировать массив русских символов:

var a = new char[]
{
    'д',
    'е',
    'ё',
    'ж'
};
var b = a.OrderBy(x => x).ToList();
Console.WriteLine(string.Concat(b));


Выдаст этот простой код неожиданное на первый взгляд дежё, но тут c# как раз верен
стандартам, потому что код буквы ё больше, чем коды всех остальных букв русского алфавита.

Попробуем отсортировать массив строк, одна из которых содержит букву ё:

var a = new string[]
{
    "жар",
    "дом",
    "ели",
    "ёлка",
};
var b = a.OrderBy(x => x).ToList();
Console.WriteLine(string.Join(" ", b));


Получаем ожидаемое дом ели ёлка жар. Вроде бы строки сортируются ожидаемым образом. 

Попробуем ели заменить на ель. Получим прекрасное дом ёлка ель жар. Очевидно, при
сортировке строк е и ё считаются одним символом, во втором случае ёлка становится перед
ель потому что к идёт раньше ь.

Моё наивное понимание предполагаемого алгоритма сортировки массива строк подсказывает,
что он должен использовать тот же алгоритм сравнения кодов символов, что и сортировка
массива символов. Этого, очевидно, не происходит. Ожидаемой модификацией алгоритма
был бы учёт того, что ё в русском алфавите находится всё-таки не там, где она находится
в unicode. А на самом деле имеем реализацию, где е и ё - это один символ.

Меня интересует несколько вопросов. Где конкретно определён алгоритм сортировки строк?
Мои путешествия по ReferenceSource увели меня куда-то в цппшные недра GitHub'а CLR,
не уверен, что двигался верно. Почему было принято решение принимать е и ё за один
символ, а не осуществлять честную сортировку? Это чьё-то волевое решение или это всё-таки
определено в какой-то из спецификаций?

Понимаю, что не все вопросы подразумевают наличие чёткого ответа у обычных участников
сообщества, но ссылаюсь сюда.

А может быть, что я вообще всё интерпретировал неверно, поправьте в таком случае.

Спасибо.
    


Ответы

Ответ 1



Здесь причиной вашего удивления является не странность алгоритмов BCL, а имплементация стандарта Unicode. Документация стандарта Unicode Unicode® Technical Standard #10 / Unicode Collation Algorithm гласит (перевод мой): 1.1 Многоуровневое сравнение Сортировка, требуемая человеческими языками, сложна. Чтобы правильно её имплементировать, используется многоуровневый алгоритм сравнения. При сравнении двух слов, самым важным являются базовые буквы, например, отличие между И и Е. Акценты обычно игнорируются, если базовые буквы не совпадают. Различие в регистре (прописные/строчные) также обычно игнорируются, если базовые буквы или их акценты различны. Что делать с пунктуацией, зависит от обстоятельств. В некоторых ситуациях, точка или запятая считается как бы отдельной базовой буквой. В других ситуациях пунктуация игнорируется, если и так есть отличия в базовых буквах, акцентах или регистре. В случае равенства иногда проводится финальное сравнение: если других различий нет, сравниваются (нормализованные) code point'ы. Таким образом, сравнение слов проводится следующим образом: Отбрасываются различия регистра и акценты. Если сравнение установило, какое из слов больше, конец алгоритма. Возвращаются назад акценты, проводится повторное сравнение. Если сравнение установило, какое из слов больше, конец алгоритма. Возвращается назад регистр, проводится повторное сравнение. Если сравнение установило, какое из слов больше, конец алгоритма. и. т. д. Для русского языка по «принципу кроссворда» Ё считается акцентированным вариантом Е, а Й — акцентированным вариантом И. Такой выбор стандарт для русского языка в Unicode, надеюсь, был согласован с лингвистами. Как это поменять в .NET — как заставить считать Ё отдельной буквой, расположенной между Е и Ж, я сходу не скажу. (Но смотрите соседний ответ.) Кстати, имплементация того или иного стандарта Unicode — фича не языка, а системы. BCL просит систему сравнить строки, чтобы не дублировать имплементацию Unicode. Это значит, что одна и та же программа, будучи проинсталлированной на Windows 7 и Windows 10, может вести себя по-разному по отношению к сортировке. Уточнение Для русской локали Й считается отдельной от И буквой, в то время как для английской Й считается акцентированным вариантом И. Буква Ё и там, и там считается акцентированным вариантом Е. Пример: var strings = new[] { "Иа", "Йокогама", "Италия", "Ель", "Ёлочка" }; var en = CultureInfo.GetCultureInfo("en-US"); var ru = CultureInfo.GetCultureInfo("ru-RU"); var orderEn = strings.OrderBy(s => s, StringComparer.Create(en, false)); Console.WriteLine("en-US: " + string.Join(" ", orderEn)); var orderRu = strings.OrderBy(s => s, StringComparer.Create(ru, false)); Console.WriteLine("ru-RU: " + string.Join(" ", orderRu)); выдаёт такой результат: en-US: Ёлочка Ель Иа Йокогама Италия ru-RU: Ёлочка Ель Иа Италия Йокогама Мораль этой истории: при сортировке строк всегда указывайте локаль!

Ответ 2



В дополнение к ответу @VladD: Как это поменять в .NET — как заставить считать Ё отдельной буквой, расположенной между Е и Ж, я навскидку не знаю. Как вариант, можно реализовать свой IComparer, например так: class MyStringComparer : IComparer { int IComparer.Compare(string x, string y) { int result = CompareAlgorithm(x.ToLowerInvariant(), y.ToLowerInvariant()); if (result != 0) return result; return CompareAlgorithm(x, y); } static readonly string symbols = " абвгдеёжзийклмнопрстуфхцчшщъыьэюяАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ.,:"; int CompareAlgorithm(string x, string y) { int i = 0, j = 0; while(x.Length > i && y.Length > j) { int indexX = symbols.IndexOf(x[i]); int indexY = symbols.IndexOf(y[i]); if (indexX == -1) { ++i; continue; } if (indexY == -1) { ++j; continue; } if (indexX < indexY) return -1; if (indexX > indexY) return 1; ++i; ++j; } if (x.Length <= i && y.Length <= j) return 0; if (x.Length <= i) return -1; else return 1; } } Сравнение строк выполняется посимвольно, символы, участвующие при сравнении указываются в symbols, в желаемом порядке возрастания, символы, отсутствующие в symbols игнорируются. В самом методе Compare сначала сравниваем без учета регистра и если выдается одинаковый результат, сравниваем с учетом регистра. Это поведение можно изменить. Если хотите всегда сравнивать с учетом регистра, оставьте метод в таком виде: int IComparer.Compare(string x, string y) { return CompareAlgorithm(x, y); } Следующий код: var a = new string[] { "жар", "дом", "ель", "ёлка", "домовой", "д.ом", "д-ом", "ДОм" }; var b = a.OrderBy(x => x, new MyStringComparer()).ToList(); Console.WriteLine(string.Join(" ", b)); Выдает такой результат: д-ом дом ДОм домовой д.ом ель ёлка жар

Ответ 3



Сортировка по умолчанию в Windows в целом и в .NET в частности всегда вызывает удивление. Я задавал похожий вопрос на en.so. Цитата из документации (чуть подправленная мной для читаемости): Платформа .NET Framework использует три разных способа сортировки: по словам, по строкам и по порядковому номеру. Соответственно, при сортировке нужно всегда указывать желаемый способ.

четверг, 11 июля 2019 г.

Как прочитать с консоли строку неизвестной заранее длины не используя string?

Я прекрасно понимаю, что можно использовать класс string и просто написать cin>> str; а затем получить количество элементов. Я даже видел веселое решение при помощи malloc и realloc функций из C. но все же, интересно, есть ведь какой-то способ при помощи средств C++, а конкретно их потоков ввода и стандартных строк в виде массива символов прочитать из консоли строку, не имея понятия о ее размере, получить впоследствии ее размер и положить в динамически выделенный массив такого размера?? Вопрос не к спеху и не для какого-то задания, все чисто ради интереса, так что ваши ответы, типа "не парься и пользуйся тем, что дают" также приветствуются )


Ответ

cin >> str; запишет только до первого разделителя: http://ideone.com/Y5CJxD.
Если под строкой понимается последовательность, которая заканчивается переводом строки ('
'), то вам: std::getline() нужен: http://www.cplusplus.com/reference/string/string/getline/
А так, std::string это и так стандартное средство в C++. Если же хочется извратиться, то нужно читать блоками, проверять - есть ли перевод строки и агрегировать код, но лишнее нужно как-то хранить между вызовами. Либо, что проще, но медленнее - читать посимвольно, проверяя конец строки, например так:
istream& getline(istream& is, char *&buffer) { std::istream::sentry s(is); if (s) { std::istreambuf_iterator it(is); std::istreambuf_iterator end;
size_t size = 64; size_t grow = 64; size_t len = 0;
buffer = new char[size];
while (it != end && *it != '
') { if (len == (size - 1)) { buffer = buffer_realloc(buffer, size, size + grow); size += grow; }
buffer[len++] = *it++; }
buffer[len] = '\0'; }
return is; }
Здесь я не привожу функцию buffer_realloc() - она простая. Использовать как-то так:
char *line = nullptr; while (getline(std::cin, line)) { if (line) { cout << line << endl; delete[] line; } }
Либо вот вариант, где использует вектор:
istream& getline(istream& is, vector &buffer) { std::istream::sentry s(is); if (s) { std::istreambuf_iterator it(is); std::istreambuf_iterator end;
const size_t grow = 64; buffer.reserve(grow);
while (it != end && *it != '
') { if (buffer.size() == buffer.capacity() - 1) { buffer.reserve(buffer.size() + grow); }
buffer.push_back(*it++); } buffer.push_back('\0'); }
return is; }
Использовать как-то так:
vector line; while (getline(std::cin, line)) { if (!line.empty()) { cout << line.data() << endl; line.clear(); } }
Тут реаллокация памяти ложится на плечи vector'а. От вас только почистить его (а можно и в гетлайн всунуть). Плюс бонусом сразу длинна строки за O(1): line.size() - 1
Вот законченные примеры:
http://ideone.com/Huj4eQ http://ideone.com/CxNfg4
Но повторюсь: std::string стандартны для С++. Я слабо представляю условия, где есть потоки, но нет строк :)