• C сравнение строк char. Функции обработки строк в Cи

    Теги: Си строки. Char array.

    Строки в си. Введение.

    Э то вводная статья по строкам в си. Более подробное описание и примеры будут, когда мы научимся работать с памятью и указателями. В компьютере все значения хранятся в виде чисел. И строки тоже, там нет никаких символов и букв. Срока представляет собой массив чисел. Каждое число соответствует определённому символу, который берётся из таблицы кодировки. При выводе на экран символ отображается определённым образом.
    Для хранения строк используются массивы типа char. Ещё раз повторюсь – тип char – числовой, он хранит один байт данных. Но в соответствии с таблицей кодировки каждое из этих чисел связано с символом. И в обратную сторону – каждый символ определяется своим порядковым номером в таблице кодировки.
    Например

    #include #include void main() { char c = "A"; int i = 65; printf("display as char %c\n", c); printf("display as int %d\n", c); printf("display as char %c\n", i); printf("display as char %d\n", i); getch(); }

    Мы создали две переменные, одна типа char , другая int . Литера "A" имеет числовое значение 65. Это именно литера, а не строка, поэтому окружена одинарными кавычками. Мы можем вывести её на печать как букву

    Printf("display as char %c\n", c);

    Тогда будет выведено
    A
    Если вывести её как число, то будет
    65
    Точно также можно поступить и с числом 65, которое хранится в переменной типа int .
    Спецсимволы также имеют свой номер

    #include #include void main() { printf("%c", "\a"); printf("%d", "\a"); printf("%c", 7); getch(); }

    Здесь будет сначала "выведен" звуковой сигнал, затем его числовое значение, затем опять звуковой сигнал.
    Строка в си – это массив типа char , последний элемент которого хранит терминальный символ "\0". Числовое значение этого символа 0, поэтому можно говорить, что массив оканчивается нулём.
    Например

    #include #include void main() { char word; word = "A"; word = "B"; word = "C"; word = "\0"; //word = 0; эквивалентно printf("%s", word); getch(); }

    Для вывода использовался ключ %s. При этом строка выводится до первого терминального символа, потому что функция printf не знает размер массива word.
    Если в этом примере не поставить

    Word = "\0";

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

    #include #include void main() { char word = "ABC"; char text = {"H", "E", "L", "L", "O"}; printf("%s\n", word); printf("%s", text); getch(); }

    В данном случае всё корректно. Строка "ABC" заканчивается нулём, и ею мы инициализируем массив word. Строка text инициализируется побуквенно, все оставшиеся символы, как следует из главы про массивы, заполняются нулями.

    Чтение строк

    Д ля того, чтобы запросить у пользователя строку, необходимо создать буфер. Размер буфера должен быть выбран заранее, так, чтобы введённое слово в нём поместилось. При считывании строк есть опасность того, что пользователь введёт данных больше, чем позволяет буфер. Эти данные будут считаны и помещены в память, и затрут собой чужие значения. Таким образом можно провести атаку, записав нужные байты, в которых, к примеру, стоит переход на участок кода с вредоносной программой, или логгирование данных.

    #include #include void main() { char buffer; scanf("%19s", buffer); printf("%s", buffer); getch(); }

    В данном случае количество введённых символов ограничено 19, а размер буфера на 1 больше, так как необходимо хранить терминальный символ. Напишем простую программу, которая запрашивает у пользователя строку и возвращает её длину.

    #include #include void main() { char buffer; unsigned len = 0; scanf("%127s", buffer); while (buffer != "\0") { len++; } printf("length(%s) == %d", buffer, len); getch(); }

    Так как числовое значение символа "\0" равно нулю, то можно записать

    While (buffer != 0) { len++; }

    Или, ещё короче

    While (buffer) { len++; }

    Теперь напишем программу, которая запрашивает у пользователя два слова и сравнивает их

    #include #include /* Результатом сравнения будет число 0 если слова равны 1 если первое слово больше второго в лексикографическом порядке -1 если второе слово больше */ void main() { char firstWord; //Первое слово char secondWord; //Второе слово unsigned i; //Счётчик int cmpResult = 0; //Результат сравнения scanf("%127s", firstWord); scanf("%127s", secondWord); for (i = 0; i < 128; i++) { if (firstWord[i] > secondWord[i]) { //Больше даже если второе слово уже закончилось, потому что //тогда оно заканчивается нулём cmpResult = 1; break; } else if (firstWord[i] < secondWord[i]) { cmpResult = -1; break; } } printf("%d", cmpResult); getch(); }

    Так как каждая буква имеет числовое значение, то их можно сравнивать между собой как числа. Кроме того, обычно (но не всегда!) буквы в таблицах кодировок расположены по алфавиту. Поэтому сортировка по числовому значению также будет и сортировкой по алфавиту.

    Программист говорит:

    Здравствуйте! Я прочел вашу статью. Мне было очень грустно и одновременно смешно. Особенно убивает эта ваша фраза: «Так как переменную типа char часто используют как массив, то определяют количество возможных значений». 😆 😆 😆
    Я не смеюсь над вами. Создание сайта это действительно подвиг. Я лишь хочу поддержать вас советом и указать несколько ошибок.

    1. Значение переменной типа char присваивается так:

    Вот здесь:

    Char a = *"A";

    Происходит разадресация указателя на массив и в результате возвращается значение первого элемента массива т.е. ‘A’

    2. Обнуление происходит так:

    Char a = NULL;
    char b = {};

    //А так очищается строка в теле программы

    "" -- этот символ называется ноль-терминатор. Он ставится в конце строки. Вы сами того не зная заполнили этим символом массив s1 из вашей статьи. А ведь можно было присвоить этот символ только нулевому элементу массива.

    3. Смело пользуитесь терминологией.
    Знак = это операция присваивания.
    Знак * операция разадресации.
    Я имею в виду вот этот фрагмент статьи: "Настолько всё оказалось просто, перед знаком = нужно было поставить знак * и нужно было объявить номер элемента (ноль соответствует первому)"

    Поймите меня правильно, статья в нынешнем виде не может существовать. Не поленитесь, перепишите ее.
    На вас лежит большая ответственность! Я серьезно. Страницы вашего сайта попали в первую страницу выдачи Яндекса. Много людей уже начали повторять за вами ошибки.

    Удачи! Вы справитесь!

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

    Я буду рад исправить и другие ошибки. поправить неточности, если они выскочат. Я ценю вашу помощь. спасибо.Я это давно знаю, просто трудно перечитывать 200 статей постоянно, чтобы что-то исправить. А некоторые грубые типы так пишут, что даже зная, что лучше исправить, исправлять совсем не охота.
    С вашим char b = {}; Это не обнуление совсем. проверили бы хотя б.
    если говорить о нулевом символе "" ; Я хорошо знал, когда заполнял им строку и цель была в том, чтобы показать настоящее очищение, а не видимое на глаз, ибо в строку входит мусор, который иногда мешает. Вы бы с терминами сами поаккуратнее, "символ нуль-терминации" или просто "нулевой символ", не терминатор))) А символ-терминатор звучит просто круто.

    Статью я модернизирую, но я не буду переходить в чужой стиль. Если я посчитаю, что новичку понятнее так, а не как хочется, то я оставлю именно так. Вы тоже поймите меня правильно. Слово "знак" слабому еще новичку намного проще понять и запомнить чем определение и название каждого знака. В этом нет совсем ошибки, знак он и есть - знак. Меньше акцента на одно дает больше акцента на другое.

    Я буду рад исправить и другие ошибки. поправить неточности, если они выскочат. Я ценю вашу помощь. спасибо.

    Здравствуйте еще раз!
    Хочу пояснить. Термином "ноль-терминатор" (terminator с англ. ограничитель) пользовался мой преподаватель в ВУЗе. Видимо это old school!
    Что касается обнуления строк.
    char b = {}; Это действительно обнуление. Весь массив заполнен нулями. Не верите -- проверьте!
    Если рассматривать строку в её естественном, бытовом смысле, то "пустой" будет та строка в которой нет ни одного символа. Поэтому в 99.9% случаев достаточно поставить в начало нулевой символ. Обычно обработка строки идет до первого нулевого символа а какие символы идут за ним уже не важно. Я понимаю что вы хотели обнулить строку. Просто решил предложить проверенный временем классический вариант.

    :
    Когда "Обычно обработка строки идет до первого нулевого символа а какие символы идут за ним уже не важно" - да, строка обнуляется
    Если рассматривать "реальное обнуление всех ячеек строки (о котором писал я)" - нет, не обнуление и, даже, первый символ не нулевой. Я этим вариантом проверял. MinGW(CodeBlock) - весь массив отдает символ "a"
    не думаю, что это повод для споров.

    Неслучайно тему про строки я поместил в раздел "Массивы". Так как строка это, по сути, массив символов. Вот пример:

    char str = "Это просто строка";

    Эту же строчку для большего понимания можно записать вот так:

    char str = {"Э","т","о"," ","п","р","о","с","т","о","","с","т","р","о","к","а"};

    Т.е. все тот же массив, только состоящий уже из символов. Поэтому работать с ним можно, так же как и с целочисленными массивами.

    А теперь давайте попробуем работать со строками в c . На вводных уроках мы разбирали, что символы относятся к целочисленным типам, т.е. каждый символ имеет свое числовое значение. Вот пример и его решение:

    1. требуется перевести введенное слово в верхний регистр:
    2. #include
      #include

      Int main()
      {
      char str = "sergey";

      str[i] -= 32;
      }
      for (int i=0; str[i] != "\0";i++){
      printf ("%c", str[i]);
      }
      getch();

      Return 0;
      }

      для получения кода числа просто воспользуйтесь в функции printf спецификатором %d. Да, и еще один важный момент: окончанием любой строки является нуль-терминатор, который обозначается специальным символом - "\0".

    Еще одним способом указания строки является объявление ее через char*. Вот пример:

    char *str = "provod";

    Т.е. создается указатель на строку, который располагается где-то в памяти.

    А вот как можно вводить строки через, нам уже родной, опертаор scanf:

    char str; scanf("%s", str);

    Тут две тонкости:

    1. знак взятия адреса тут не нужен, так как имя массива, как мы уже знаем, и является адресом
    2. Длина вводимой строки не должна превышать 15 символов, так как последним обязательно должен быть нуль-терминатор. Причем компилятор сам заполнит этот символ после последнего введенного вашего символа.

    Так как язык Си является языком структурным, то существуют уже встроенные функции для работы со строками и с символами. Для обработки строк вам понадобится подключить файл: ctype.h. Файл содержит функции определения регистра, формата символов. В принципе, все, что вам может понадобится узнать о символе, можно выполнить с помощью функций файла ctype.h

    Иногда вам может понадобиться перевести строку в другой тип данных. Для перевода строк в другие типы существует библиотека stdlib. Вот ее функции:

    1. int atoi (char *str)
    2. long atol (char *str)
    3. double atof (char *str)

    Иногда эти функции очень помогают, например, когда вам надо извлечь из строки год или цифровое значение. Работа со строками в c (си) является очень важной темой, поэтому постарайтесь вникнуть в этот урок.

    Строки в C++

    Строка - последовательность (массив) символов. Если в выражении встречается одиночный символ, он должен быть заключен в одинарные кавычки . При использовании в выражениях строка заключается в двойные кавычки. Признаком конца строки является нулевой символ \0 . В C++ строки можно описать с помощью символов (массив элементов типа char ), в котором следует предусмотреть место для хранения признака конца строки.

    Например, описание строки из 25 символов должно выглядеть так:

    Можно описать и массив строк:

    Определен массив из 3 строк по 25 байт в каждой.

    Для работы с указателями можно использовать (char * ). Адрес первого символа будет начальным значением указателя.

    Рассмотрим пример объявления и вывода строк.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    #include «stdafx.h»
    #include
    using namespace std;
    int main()
    {
    setlocale(LC_ALL,«Rus» ) ;
    //описываем 3 строки, s3- указатель
    char s2[ 20 ] , * s3, s4[ 30 ] ;
    cout << «s2=» ; cin >> s2; //ввод строки s2
    cout << «s2=» << s2<< endl;
    //запись в s3 адреса строки, где хранится s4. Теперь в переменных
    //(указателях) s3 и s4 хранится значение одного и того же адреса
    s3= s4;
    cout << «s3=» ; cin >> s3; //ввод строки s3
    //вывод на экран строк s3 и s4, хотя в результате присваивния s3=s4;
    //теперь s3 и s4 — это одно и тоже
    cout << «s3=» << s3<< endl;
    cout << «s4=» << s4<< endl;
    system («pause» ) ;
    return 0 ;
    }

    Результат работы программы:

    Но следует отметить, что если пользователь введет в одну переменную слова разделенные пробелом, то программа будет работать иначе:

    Все дело в том, что функция cin вводит строки до встретившегося пробела. Более универсальной функцией является getline .

    cin.getline(char *s, int n);

    Предназначена для ввода с клавиатуры строки s с пробелами, в строке не должно быть более n символов. Следовательно, для корректного ввода строк, содержащих пробел, необходимо в нашей программе заменить cin>>s на cin.getline(s, 80) .

    Операции над строками

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

    Для преобразования числа в строку можно воспользоваться функцией sprintf из библиотеки stdio.h .

    Некоторые функции работы со строками:

    Прототип функции Описание функции
    size_t strlen(const char *s) вычисляет длину строки s в байтах.
    char *strcat(char *dest, const char *scr) присоединяет строку src в конец строки dest, полученная срока возвращается в качестве результата
    char *strcpy(char *dest, const char *scr) копирует строку scr в место памяти, на которое указывает dest
    char strncat(char *dest, const char *dest, size_t maxlen) присоединяет строку maxlen символов строки src в конец строки dest
    char *strncpy(char *dest, const char *scr, size_t maxlen) копирует maxlen символов строки src в место памяти, на которое указывает dest
    int ctrcmp(const char *s1, const char *s2) сравнивает две строки в лексикографическом порядке с учетом различия прописных и строчных букв, функция возвращает 0, если строки совпадают, возвращает - 1, если s1 располагается в упорядоченном по алфавиту порядке раньше, чем s2, и 1 - в противоположном случае.
    int strncmp(const char *s1, const char *s2, size_t maxlen) сравнивает maxlen символов двух строк в лексикографическом порядке, функция возвращает 0, если строки совпадают, возвращает - 1, если s1 располагается в упорядоченном по алфавиту порядке раньше, чем s2, и 1 - в противоположном случае.
    double atof(const char *s) преобразует строку в вещественное число, в случае неудачного преобразования возвращается число 0
    long atol(const char *s) преобразует строку в длинное целое число, в случае неудачного преобразования возвращается 0
    char *strchr(const char *s, int c); возвращает указатель на первое вхождение символа c в строку, на которую указывает s . Если символ c не найден, возвращается NULL
    char *strupr(char *s) преобразует символы строки, на которую указывает s, в символы верхнего регистра, после чего возвращает ее

    Тип данных string

    Кроме работы со строками, как с массивом символов, в C++ существует специальный тип данных string . Для ввода переменных этого типа можно использовать cin , или специальную функцию getline .

    getline(cin, s);

    Здесь s - имя вводимой переменной типа string .

    При описании переменной этого типа можно сразу присвоить значение этой переменной.

    string var(s);

    Здесь var - имя переменной, s - строковая константа. В результате этого оператора создается переменная var типа string , и в нее записывается значение строковой константы s . Например,

    string v(«Hello»);

    Создается строка v , в которую записывается значение Hello .

    Доступ к i-му элементу строки s типа string осуществляется стандартным образом s[i] . Над строками типа string определенны следующие операции:

    • присваивания, например s1=s2;
    • объединения строк (s1+=s2 или s1=s1+s2) - добавляет к строке s1 строку s2, результат храниться в строке s1, пример объединения строк:
    • сравнения строк на основе лексикографического порядка: s1=s2, s1!=s2, s1s2, s1<=s2, s1>=s2 - результатом будет логическое значение;

    При обработке строк типа string можно использовать следующие функции:

    • s.substr(pos, length) - возвращает подстроку из строки s , начиная с номера pos длинной length символов;
    • s.empty() - возвращает значение true, если строка s пуста, false - в противном случае;
    • s.insert(pos, s1) - вставляет строку s1 в строку s , начиная с позиции pos ;
    • s.remove(pos, length) - удаляет из строки s подстроку length длинной pos символов;
    • s.find(s1, pos) - возвращает номер первого вхождения строки s1 в строку s , поиск начинается с номера pos , параметр pos может отсутствовать, в этом случае поиск идет с начала строки;
    • s.findfirst(s1, pos) - возвращает номер первого вхождения любого символа из строки s1 в строку s , поиск начинается с номера pos , который может отсутствовать.

    Русский язык для строк

    Думаю вы уже заметили, что при выводе русских букв, в консоли появляются «левые» символы. Для того чтобы избежать этого недоразумения, необходимо воспользоваться сторонней функцией CharToOemA . Подключаем библиотеку windows.h , она нужна для того, чтобы наша функция могла преобразовать строки в другую кодировку. Также, нам понадобиться дополнительный символьный массив. Исходный код программы будет выглядеть вот так:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

    #include «stdafx.h»
    #include
    #include
    using namespace std;
    int main()
    { setlocale(LC_ALL,«Rus» ) ;
    char s[ 255 ] = { » Меня надо преобразовать « } ;
    char * pre= new char [ 255 ] ;
    CharToOemA(s, pre) ; //преобразовываем
    cout << s;
    delete pre;
    system («pause>>void» ) ;
    return 0 ;
    }

    Способ только что описанный достаточно не удобен. Но существует более простой вариант решения «русской» проблемы. Как видите, в программе используется функция setlocale(), вместо этого удобнее вписать в главную функцию следующую конструкцию.

    Последнее обновление: 31.10.2015

    Конкатенация

    Конкатенация строк или объединение может производиться как с помощью операции + , так и с помощью метода Concat:

    String s1 = "hello"; string s2 = "world"; string s3 = s1 + " " + s2; // результат: строка "hello world" string s4 = String.Concat(s3, "!!!"); // результат: строка "hello world!!!" Console.WriteLine(s4);

    Метод Concat является статическим методом класса String, принимающим в качестве параметров две строки. Также имеются другие версии метода, принимающие другое количество параметров.

    Для объединения строк также может использоваться метод Join:

    String s5 = "apple"; string s6 = "a day"; string s7 = "keeps"; string s8 = "a doctor"; string s9 = "away"; string values = new string { s5, s6, s7, s8, s9 }; String s10 = String.Join(" ", values); // результат: строка "apple a day keeps a doctor away"

    Метод Join также является статическим. Использованная выше версия метода получает два параметра: строку-разделитель (в данном случае пробел) и массив строк, которые будут соединяться и разделяться разделителем.

    Сравнение строк

    Для сравнения строк применяется статический метод Compare:

    String s1 = "hello"; string s2 = "world"; int result = String.Compare(s1, s2); if (result<0) { Console.WriteLine("Строка s1 перед строкой s2"); } else if (result > 0) { Console.WriteLine("Строка s1 стоит после строки s2"); } else { Console.WriteLine("Строки s1 и s2 идентичны"); } // результатом будет "Строка s1 перед строкой s2"

    Данная версия метода Compare принимает две строки и возвращает число. Если первая строка по алфавиту стоит выше второй, то возвращается число меньше нуля. В противном случае возвращается число больше нуля. И третий случай - если строки равны, то возвращается число 0.

    В данном случае так как символ h по алфавиту стоит выше символа w, то и первая строка будет стоять выше.

    Поиск в строке

    С помощью метода IndexOf мы можем определить индекс первого вхождения отдельного символа или подстроки в строке:

    String s1 = "hello world"; char ch = "o"; int indexOfChar = s1.IndexOf(ch); // равно 4 Console.WriteLine(indexOfChar); string subString = "wor"; int indexOfSubstring = s1.IndexOf(subString); // равно 6 Console.WriteLine(indexOfSubstring);

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

    Еще одна группа методов позволяет узнать начинается или заканчивается ли строка на определенную подстроку. Для этого предназначены методы StartsWith и EndsWith . Например, у нас есть задача удалить из папки все файлы с расширением exe:

    String path = @"C:\SomeDir"; string files = Directory.GetFiles(path); for (int i = 0; i < files.Length; i++) { if(files[i].EndsWith(".exe")) File.Delete(files[i]); }

    Разделение строк

    С помощью функции Split мы можем разделить строку на массив подстрок. В качестве параметра функция Split принимает массив символов или строк, которые и будут служить разделителями. Например, подсчитаем количество слов в сроке, разделив ее по пробельным символам:

    String text = "И поэтому все так произошло"; string words = text.Split(new char { " " }); foreach (string s in words) { Console.WriteLine(s); }

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

    String words = text.Split(new char { " " }, StringSplitOptions.RemoveEmptyEntries);

    Второй параметр StringSplitOptions.RemoveEmptyEntries говорит, что надо удалить все пустые подстроки.

    Обрезка строки

    Для обрезки начальных или концевых символов используется функция Trim:

    String text = " hello world "; text = text.Trim(); // результат "hello world" text = text.Trim(new char { "d", "h" }); // результат "ello worl"

    Функция Trim без параметров обрезает начальные и конечные пробелы и возвращает обрезанную строку. Чтобы явным образом указать, какие начальные и конечные символы следует обрезать, мы можем передать в функцию массив этих символов.

    Эта функция имеет частичные аналоги: функция TrimStart обрезает начальные символы, а функция TrimEnd обрезает конечные символы.

    Обрезать определенную часть строки позволяет функция Substring :

    String text = "Хороший день"; // обрезаем начиная с третьего символа text = text.Substring(2); // результат "роший день" Console.WriteLine(text); // обрезаем сначала до последних двух символов text = text.Substring(0, text.Length - 2); // результат "роший де" Console.WriteLine(text);

    Функция Substring также возвращает обрезанную строку. В качестве параметра первая использованная версия применяет индекс, начиная с которого надо обрезать строку. Вторая версия применяет два параметра - индекс начала обрезки и длину вырезаемой части строки.

    Вставка

    Для вставки одной строки в другую применяется функция Insert:

    String text = "Хороший день"; string subString = "замечательный "; text = text.Insert(8, subString); Console.WriteLine(text);

    Первым параметром в функции Insert является индекс, по которому надо вставлять подстроку, а второй параметр - собственно подстрока.

    Удаление строк

    Удалить часть строки помогает метод Remove:

    String text = "Хороший день"; // индекс последнего символа int ind = text.Length - 1; // вырезаем последний символ text = text.Remove(ind); Console.WriteLine(text); // вырезаем первые два символа text = text.Remove(0, 2);

    Первая версия метода Remove принимает индекс в строке, начиная с которого надо удалить все символы. Вторая версия принимает еще один параметр - сколько символов надо удалить.

    Замена

    Чтобы заменить один символ или подстроку на другую, применяется метод Replace :

    String text = "хороший день"; text = text.Replace("хороший", "плохой"); Console.WriteLine(text); text = text.Replace("о", ""); Console.WriteLine(text);

    Во втором случае применения функции Replace строка из одного символа "о" заменяется на пустую строку, то есть фактически удаляется из текста. Подобным способом легко удалять какой-то определенный текст в строках.

    Смена регистра

    Для приведения строки к верхнему и нижнему регистру используются соответственно функции ToUpper() и ToLower() :

    String hello = "Hello world!"; Console.WriteLine(hello.ToLower()); // hello world! Console.WriteLine(hello.ToUpper()); // HELLO WORLD!