Как сделать конец строки в с
А если "C конечно", то последний символ строки - это символ с значением '\0' (или просто числовым значением 0), который в длину строки на входит.
Да? А я то голову ломал.
Я знаю это. Я спрашиваю как сделать проверку последний это символ или нет, потому что не выходит проверить на '\0' или '0'.
Я спрашиваю как сделать проверку последний это символ или нет, потому что не выходит проверить на '\0' или '0'.
Если очередной символ == 0, то он гарантировано последний в строке.
Как это может быть, что не "выходит".
А вообще то, для разделения строки на токены (слова) есть стандартные функции, такие как strtok() или strsep().
Поэтому, когда вы читаете свой файл, вы должны искать '\n' персонаж, и вы сможете узнать, что принадлежит к какой линии.
ПРОЦЕСС
- Извлеките первый образец из вашего файла
- Поместите это в буфер
- Разделить буфер на '\n' персонаж
- Проанализируйте каждую строку, чтобы оставить только те, которые имеют 2 номера
Другие решения
Пожалуйста, обратитесь к учебнику, например, cppreference
Из приведенного выше урока:
Краткое объяснение: getline возвращает true, пока не достигнут конец файла.
запустите эту программу и посмотрите, что происходит. это должно быть очень прямым.
Не так давно у со мной произошел довольно-таки интересный инцидент, в котором был замешан один из преподавателей одного колледжа информатики.
Разговор о программировании под Linux медленно перешел к тому, что этот человек стал утверждать, что сложность системного программирования на самом деле сильно преувеличена. Что язык Си прост как спичка, собственно как и ядро Linux (с его слов).
У меня был с собой ноутбук с Linux, на котором присутствовал джентльменский набор утилит для разработки на языке Си (gcc, vim, make, valgrind, gdb). Я уже не помню, какую цель мы тогда перед собой поставили, но через пару минут мой оппонент оказался за этим ноутбуком, полностью готовый решать задачу.
И буквально на первых же строках он допустил серьезную ошибку при аллоцировании памяти под… строку.
buffer — стековая переменная, в которую заносились данные с клавиатуры.
А что именно — читайте по катом.
Немного теории — своеобразный ЛикБез.
Если знаете — листайте до следующего хэдера.
Строка в C — это массив символов, который по-хорошему всегда должен заканчиваться '\0' — символом конца строки. Строки на стеке (статичные) объявляются вот так:
n — размер массива символов, то же, что и длина строки.
Так же на стеке можно сразу проинициализировать строку:
Помимо этого строку можно объявить указателем и выделить под нее память на куче (heap):
size — количество байт, которые мы выделяем под строку. Такие строки называются динамическими (вследствие того, что нужный размер вычисляется динамически + выделенный размер памяти можно в любой момент увеличить с помощью функции realloc() ).
В случае со стековой переменной, для определения размера массива я использовал обозначение n, в случае с переменной на куче — я использовал обозначение size. И это прекрасно отражает истинную суть отличия объявления на стеке от объявление с аллоцированием памяти на куче, ведь n как правило используется тогда, когда говорят о количестве элементов. А size — это уже совсем другая история…
Думаю. пока хватит. Идем дальше.
Нам поможет valgrind
В своей предыдущей статье я также упоминал о нем. Valgrind (раз — вики-статья, два — небольшой how-to) — очень полезная программа, которая помогает программисту отслеживать утечки памяти и ошибки контекста — как раз те вещи, которые чаще всего всплывают при работе со строками.
Давайте рассмотрим небольшой листинг, в котором реализовано что-то похожее на упомянутую мной программу, и прогоним ее через valgrind:
И, собственно, результат работы программы:
Пока ничего необычного. А теперь давайте запустим эту программу с valgrind!
==3892== All heap blocks were freed — no leaks are possible — утечек нет, и это радует. Но стоит опустить глаза чуть пониже (хотя, хочу заметить, это лишь итог, основная информация немного в другом месте):
==3892== ERROR SUMMARY: 3 errors from 2 contexts (suppressed: 0 from 0)
3 ошибки. В 2х контекстах. В такой простой программе. Как!?
Чуть выше результата исполнения программы, строки -> Hello, Habr! есть подробный отчет, что и где не понравилось нашему драгоценному valgrind. Предлагаю самостоятельно посмотреть эти строчки и сделать выводы.
Собственно, правильная версия программы будет выглядеть так:
Пропускаем через valgrind:
Отлично. Ошибок нет, +1 байт выделяемой памяти помог решить проблему.
Что интересно, в большинстве случаев и первая и вторая программа будут работать одинаково, но если память, выделенная под строку, в которую не влез символ окончания, не была занулена, то функция printf(), при выводе такой строки, выведет и весь мусор после этой строки — будет выведено все, пока на пути printf() не встанет символ окончания строки.
Однако, знаете, (strlen(str) + 1) — такое себе решение. Перед нами встают 2 проблемы:
- А если нам надо выделить память под формируемую с помощью, например, s(n)printf(..) строку? Аргументы мы не поддерживаем.
- Внешний вид. Строка с объявлением переменной выглядит просто ужасно. Некоторые ребята к malloc еще и (char *) умудряются прикручивать, будто под плюсами пишут. В программе где регулярно требуется обрабатывать строки есть смысл найти более изящное решение.
snprintf()
int snprintf(char *str, size_t size, const char *format, . ); — функция — расширение sprintf, которая форматирует строку и записывает ее по указателю, переданному в качестве первого аргумента. От sprintf() она отличается тем, что в str не будет записано байт больше, чем указано в size.
Функция имеет одну интересную особенность — она в любом случае возвращает размер формируемой строки (без учета символа конца строки). Если строка пустая, то возвращается 0.
Одна из описанных мною проблем использования strlen связана с функциями sprintf() и snprintf(). Предположим, что нам надо что-то записать в строку str. Конечная строка содержит значения других переменных. Наша запись должна быть примерно такой:
Встает вопрос: как определить, сколько памяти надо выделить под строку str?
— не прокатит. Прототип функции strlen() выглядит так:
const char *s не подразумевает, что передаваемая в s строка может быть строкой формата с переменным количеством аргументов.
Тут нам поможет то полезное свойство функции snprintf(), о котором я говорил выше. Давайте посмотрим на код следующей программы:
Запускаем программу в valgrind:
Отлично. Поддержка аргументов у нас есть. Благодаря тому, что мы в качестве второго аргумента в функцию snprintf() передаем ноль, запись по нулевому указателю никогда не приведет к Seagfault. Однако, несмотря на это функция все равно вернет необходимый под строку размер.
Но с другой стороны, нам пришлось завести дополнительную переменную, да и конструкция
выглядит еще хуже, чем в случае с strlen().
Да, возможно, для кого-то будет новостью, но макросы в си поддерживают переменное количество аргументов, и троеточие говорит препроцессору о том, что указанному аргументу макрофункции (в нашем случае это args) соответствует несколько реальных аргументов.
Проверим наше решение на практике:
Запускаем с valgrund:
Да, ошибок нет. Все корректно. И valgrind доволен, и программист наконец может пойти поспать.
Но, напоследок, скажу еще кое-что. В случае, если нам надо выделить память под какую-либо строку (даже с аргументами) есть уже полностью рабочее готовое решение.
Речь идет о функции asprintf:
В качестве первого аргумента она принимает указатель на строку (**strp) и аллоцирует память по разыменованному указателю.
Наша программа, написанная с использованием asprintf() будет выглядеть так:
И, собственно, в valgrind:
Все отлично, но, как видите, памяти всего было выделено больше, да и alloc'ов теперь три, а не два. На слабых встраиваемых системах использование это функции нежелательно.
К тому же, если мы напишем в консоли man asprintf, то увидим:
Отсюда ясно, что данная функция доступна только в исходниках GNU.
Заключение
Больше половины моих знакомых си-программистов (большинство из них — начинающие), решивших по моей просьбе задачу с выделением памяти под строки, сделали это так, что в конечном итоге это привело к ошибкам контекста. В одном случае — даже к утечке памяти (ну, забыл человек сделать free(str), с кем не бывает). Собственно говоря, это и сподвигло меня на создание сего творения, которое вы только что прочитали.
Я надеюсь, кому-то эта статья будет полезной. К чему я это все городил — никакой язык не бывает прост. Везде есть свои тонкости. И чем больше тонкостей языка вы знаете, тем лучше ваш код.
Я верю, что после прочтения этой статьи ваш код станет чуточку лучше :)
Удачи, Хабр!
Функциональность класса System.String наиболее полно раскрывается через его методы, основными из которых являются следующие:
- Compare — сравнивает две строки с учетом текущих региональных настроек (локали) пользователя
- CompareOrdinal — сравнивает две строки без учета локали
- Contains — проверяет, содержится ли заданная подстрока в строке
- Concat — объединяет строки
- CopyTo — копирует часть строки, начиная с определенного индекса, в массив
- EndsWith — проверяет совпадает ли конец строки с подстрокой
- Format — форматирует строку
- IndexOf — находит индекс первого вхождения символа или подстроки в строке
- Insert — вставляет в строку подстроку
- Join — соединяет элементы массива строк
- LastIndexOf — находит индекс последнего вхождения символа или подстроки в строке
- Replace — заменяет в строке символ или подстроку другим символом или подстрокой
- Split — разделяет одну строку на массив строк
- Substring — извлекает из строки подстроку, начиная с указанной позиции
- ToLower — переводит все символы строки в нижний регистр
- ToUpper — переводит все символы строки в верхний регистр
- Trim — удаляет начальные и конечные пробелы из строки
Рассмотрим представленные методы более подробно.
Результатом выполнения метода является целое число Int32. Это число означает следующее:
Результат Compare | Что означает |
Меньше нуля | strA предшествует strB в порядке сортировки (стока strA меньше строки strB ). |
Нуль | strA занимает ту же позицию в порядке сортировки, что и объект strB (строки равны) |
Больше нуля | strA следует за strB в порядке сортировки (стока strA больше строки strB ). |
Вернет значение 0 так как строки равны.
У класса String есть также несколько перегруженных методов Compare , позволяющих провести настройку способа сравнения строк. Например, можно сравнивать строки, игнорируя регистр:
или определить свои настройки сравнения строк, используя следующий вариант метода Compare :
Несмотря на то, что метод Compare по умолчанию предназначен для сравнения строк с учётом локали, используя перегруженные версии метода, можно сравнивать строки и без учёта региональных настроек, например, воспользовавшись вот таким способом:
Метод CompareOrdinal сравнивает два объекта String , оценивая числовые значения соответствующих объектов Char в каждой строке. Результат выполнения метода такой же, как и у предыдущего метода Compare (см. таблицу выше).
Метод Contains проверяет, входит ли заданная подстрока в строку. Этот метод не статический, поэтому использовать его необходимо только после создания объекта, то есть следующим образом:
В примере выше мы проверяем содержится ли подстрока "ока" в строке "Строка" . Очевидно, что результатом выполнения метода будет true .
Также возможно объединять и массивы строк:
Для приведенного выше примера в консоли мы получим следующую объединенную строку:
Для того, чтобы продемонстрировать работу метода, воспользуемся следующим примером:
в результате выполнения мы получим следующий вывод в консоли:
В этом примере из строки s6 копируется 4 символа, начиная с символа с индексом 2 (так как нумерация начинается с нуля, то первый копируемый символ r ) и вставляется в массив charArr , начиная с первого элемента (с индексом 0 )
Также можно воспользоваться статическим методом Copy для создания полной копии строки:
Первый метод ( EndWith ) проверяет заканчивается ли проверяемая строка подстрокой value , а второй, соответственно, проводит проверку на наличие подстроки value в начале строки. Оба метода имеют перегрузки. Пример:
Результатом будет следующая строка:
Этот метод возвращает индекс первого вхождения указанного символа или подстроки в строку. Например,
Результатом выполнения будут следующие строки в консоли:
Индекс первого символа искомой подстроки 18
Соответственно, чтобы найти последнее вхождение символа или подстроки в строку, достаточно воспользоваться методом:
Этот метод вставляет в строку, начиная с индекса startIndex подстроку value и возвращает в результате новый экземпляр строки. Например,
Например, заменим все символы o с строке на символы А :
В качестве параметров метод Split принимает один или несколько символов ( Char ), используемых в качестве разделителя, а на выходе возвращает массив строк. Рассмотрим работу метода Split на примере
Здесь в качестве разделителей мы используем два символа — пробел и запятую. Результатом выполнения кода будет следующий вывод в консоли:
Для извлечения подстрок из строки используется метод Substring :
При использовании первого варианта метода (с одним параметром) из строки извлекается подстрока, начинающаяся с индекса startIndex и, при этом, извлечение происходит до конца строки. Во втором случае подстрока извлекается также, начиная с индекса startIndex , однако длина извлекаемой подстроки ограничивается вторым параметром — length .
Пример использования метода представлен ниже:
Метод ToLower меняет регистр всех символов в строке на нижний, а метод ToUpper , напротив — меняет регистр всех символов в строке на верхний. Пример
По названию метода можно понять смысл его работы. Так, если используется метод Trim() , то обрезка строки происходит и сначала и с конца, если же используется TrimEnd() , то строка обрезается только с конца. При этом, если используется версия метода без параметров, то из строки удаляются лидирующие или замыкающие пробелы, иначе — определенные в параметрах метода символы. Например,
Читайте также: