Символы и строки

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

Символьный тип char

Любой текст состоит из символов. Символ - это некоторый значок, изображение. Один и тот же символ можно записать по-разному, например, два человека по-разному напишут от руки букву “A”, и даже в компьютерном представлении одна и та же буква будет выглядеть по-разному, если ее отображать разными шрифтами, при этом это будет все равно один и тот же символ. Верно и другое: разные символы могут быть записаны одинаково, например, вот две разные буквы, одна - латинского алфавита, другая - русского: “A” и “А”. Несмотря на то, что они выглядят одинаково, удобней считать их разными символами.

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

Первоначально договорились под кодирование одного символа отвести один байт, то есть 8 бит информации. Таким образом можно было закодировать 256 различных значений, то есть в записи текста можно использовать 256 различных символов. Этого достаточно, чтобы отобразить все символы латинского алфавита, цифры, знаки препинания и некоторые другие символы. Стандарт, указывающий, какие числовые коды соответствуют каким основным символам, называется ASCII. В таблицу ASCII включены символы с кодами от 0 до 127, то есть ASCII - это семибитный код. Вот так выглядит таблица ASCII:

Char  Dec  Oct  Hex | Char  Dec  Oct  Hex | Char  Dec  Oct  Hex | Char Dec  Oct   Hex
-------------------------------------------------------------------------------------
(nul)   0 0000 0x00 | (sp)   32 0040 0x20 | @      64 0100 0x40 | `      96 0140 0x60
(soh)   1 0001 0x01 | !      33 0041 0x21 | A      65 0101 0x41 | a      97 0141 0x61
(stx)   2 0002 0x02 | "      34 0042 0x22 | B      66 0102 0x42 | b      98 0142 0x62
(etx)   3 0003 0x03 | #      35 0043 0x23 | C      67 0103 0x43 | c      99 0143 0x63
(eot)   4 0004 0x04 | $      36 0044 0x24 | D      68 0104 0x44 | d     100 0144 0x64
(enq)   5 0005 0x05 | %      37 0045 0x25 | E      69 0105 0x45 | e     101 0145 0x65
(ack)   6 0006 0x06 | &      38 0046 0x26 | F      70 0106 0x46 | f     102 0146 0x66
(bel)   7 0007 0x07 | '      39 0047 0x27 | G      71 0107 0x47 | g     103 0147 0x67
(bs)    8 0010 0x08 | (      40 0050 0x28 | H      72 0110 0x48 | h     104 0150 0x68
(ht)    9 0011 0x09 | )      41 0051 0x29 | I      73 0111 0x49 | i     105 0151 0x69
(nl)   10 0012 0x0a | *      42 0052 0x2a | J      74 0112 0x4a | j     106 0152 0x6a
(vt)   11 0013 0x0b | +      43 0053 0x2b | K      75 0113 0x4b | k     107 0153 0x6b
(np)   12 0014 0x0c | ,      44 0054 0x2c | L      76 0114 0x4c | l     108 0154 0x6c
(cr)   13 0015 0x0d | -      45 0055 0x2d | M      77 0115 0x4d | m     109 0155 0x6d
(so)   14 0016 0x0e | .      46 0056 0x2e | N      78 0116 0x4e | n     110 0156 0x6e
(si)   15 0017 0x0f | /      47 0057 0x2f | O      79 0117 0x4f | o     111 0157 0x6f
(dle)  16 0020 0x10 | 0      48 0060 0x30 | P      80 0120 0x50 | p     112 0160 0x70
(dc1)  17 0021 0x11 | 1      49 0061 0x31 | Q      81 0121 0x51 | q     113 0161 0x71
(dc2)  18 0022 0x12 | 2      50 0062 0x32 | R      82 0122 0x52 | r     114 0162 0x72
(dc3)  19 0023 0x13 | 3      51 0063 0x33 | S      83 0123 0x53 | s     115 0163 0x73
(dc4)  20 0024 0x14 | 4      52 0064 0x34 | T      84 0124 0x54 | t     116 0164 0x74
(nak)  21 0025 0x15 | 5      53 0065 0x35 | U      85 0125 0x55 | u     117 0165 0x75
(syn)  22 0026 0x16 | 6      54 0066 0x36 | V      86 0126 0x56 | v     118 0166 0x76
(etb)  23 0027 0x17 | 7      55 0067 0x37 | W      87 0127 0x57 | w     119 0167 0x77
(can)  24 0030 0x18 | 8      56 0070 0x38 | X      88 0130 0x58 | x     120 0170 0x78
(em)   25 0031 0x19 | 9      57 0071 0x39 | Y      89 0131 0x59 | y     121 0171 0x79
(sub)  26 0032 0x1a | :      58 0072 0x3a | Z      90 0132 0x5a | z     122 0172 0x7a
(esc)  27 0033 0x1b | ;      59 0073 0x3b | [      91 0133 0x5b | {     123 0173 0x7b
(fs)   28 0034 0x1c | <      60 0074 0x3c | \      92 0134 0x5c | |     124 0174 0x7c
(gs)   29 0035 0x1d | =      61 0075 0x3d | ]      93 0135 0x5d | }     125 0175 0x7d
(rs)   30 0036 0x1e | >      62 0076 0x3e | ^      94 0136 0x5e | ~     126 0176 0x7e
(us)   31 0037 0x1f | ?      63 0077 0x3f | _      95 0137 0x5f | (del) 127 0177 0x7f

При этом символы с кодами, меньшими 32 - это специальные управляющие символы, которые не отображаются на экране. Например, для того, чтобы обозначить конец строки в системе Linux используется один символ с кодом 10, а в системе Windows - два подряд идущих символа с кодами 13 и 10, символы с кодами 48-57 соответствуют начертанию арабских цифр (обратите внимание, символ с кодом 0 - это вовсе не символ, отображающийся на экране, как “0”), символы с кодами 65-90 - заглавные буквы буквы латинского алфавита, а если к их кодам прибавить 32, то получатся строчные буквы латинского алфавита. В промежутках между указанными диапазонами находятся знаки препинания, математические операции и прочие символы.

Но в ASCII-таблицы нет русских букв! А также нет букв сотен других национальных алфавитов. Первоначально для отображения букв национальных алфавитов использовали вторую половину возможного значения байта, то есть символы с кодами от 128 до 255. Это приводило к множеству проблем, например, поскольку 128 значений явно недостаточно для того, чтобы отобразить символы всех национальных алфавитов (даже недостаточно для того, чтобы отобразить символы одного алфавита, например, китайской письменности. Поэтому в настоящее время для кодирования символов используется стандарт Unicode, последняя версия 5.2 которого (октябрь, 2009) включает 107361 различный символ. Естественно, для кодирования Unicode-символов недостаточно одного байта на символ, поэтому используются многобайтовые кодировки (для представления одного символа необходимо несколько байт).

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

В языке C++ для хранения однобайтового символа используется тип данных char. Переменную типа char можно рассматривать двояко: как целое число, занимающее 1 байт и способное принимать значения от -128 до 127 (тип signed char, есть также беззнаковая модификация unsigned char, принимающая значения от 0 до 255) и как один символ текста. Само по себе определение char может оказаться как знаковым, так и беззнаковым, в зависимости от операционной системы и компилятора. Поэтому использовать тип char не рекомендуется, лучше явно указывать будет ли он знаковым (signed) или беззнаковым (unsigned).

Как и целые числа, данные типа char можно складывать, вычитать, умножать и даже делить. Но если операции умножения и деления, как правило, бессмысленны, то сложение и вычитание вполне осмысленно. Например, если к символу 'A' прибавить 1, то получится символ 'B', а если вычесть 1, то получится символ '@'. То есть в следующем фрагменте кода на экран будет выведена буква B.

char c = 'A';
c = c + 1;
cout << c << endl;

В этом примере видно, что переменным типа char можно присваивать значения, равные ASCII кодам символов, если эти символы заключать в кавычки. То есть запись 'A' будет соответствовать символу A, или ASCII коду 65.

Также в этом примере видно, что при выводе на экран переменной типа char мы увидим изображение этого символа. Как же узнать значение ASCII-кода символа? Его не нужно узнавать, сам символ - это и есть ASCII-код. А как его вывести на экран? Очень просто - нужно преобразовать значение величины типа char к значению типа int. Например, вот так:

cout << (int) c << endl;

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

Аналогично, при считывании переменной типа char через поток cout, из потока ввода считывается один символ, переменная получает значение, равное его ASCII-коду. Например, если написать программу, содержающую строчку

char c;
cin >> c;

запустить ее, ввести символ A (безо всяких кавычек!), то в переменную c будет записано значение 65 - ASCII-код символа A.

Переменным типа char можно и явно присваивать числовые значения. Например, можно сделать так:

#include <iostream>
using namespace std;
int main()
{
         unsigned char c = 'A';
         cout << c << " " << (int) c << endl;
         c = 126;      // char можно присвоить и числовое значение
         cout << c << " " << (int) c << endl;
         return 0;
}

Эта программа выведет две строки: “A 65” и “~ 126”, то есть символы с ASCII-кодами 65 (A) и 126 (~) и сами ASCII-коды.

Организовать последовательное посимвольное считывание всего входного потока можно при помощи цикла while:

#include <iostream>
using namespace std;
int main()
{
    char c;
    while (cin >> c) // Цикл пока считывание успешно
    {
        // Делаем необходимые действия,
        // обрабатывая символ c
    }
    return 0;
}

В этом примере программа будет посимвольно считывать входной поток (по умолчанию — ввод с клавиатуры), пока не встретит признак конца файла. Для того, чтобы сообщить программе о завершении входного потока при вводе с клавиатуры необходимо нажать клавиши Ctrl-d в системе Linux и Ctrl-z в системе Windows.

Эта программа при считывании данных будет игнорировать символы–разделители: пробелы, символы новой строки и табуляции. Если нужно, чтобы в переменную c считывались все символы, в том числе и разделители, то необходимо для потока ввода cin установить манипулятор noskipws при помощи инструкции:

    cin >> noskipws;

Строки в языке C++

Текстовая строка - это последовательность символов. Поскольку символы в строке пронумерованы, то естественным представлением для строки был бы массив символов. Так строки и представлялись в языке C - строкой считался массив символов, а для обозначения конца строки использовался символ с ASCII-кодом 0, что позволяло хранить строки переменной длины (то есть в массиве char[n] можно было хранить строки любой длины, не превосходящей n-1. Такой способ хранения строк порождал ряд неудобств: любая строка была ограничена по длине размером массива, а чтобы вычислить длину строки необходимо было пройти по всей строке до появления нулевого символа, то есть определение длины строки требует количество операций, пропорциональное этой длине.

В языке C++ для представления строк существует более совершенный тип данных string, в основе которого лежит такой же массив символов, завершающийся нулевым символом, но содержащий еще ряд дополнительных возможностей. Для работы со строками языка C++ необходимо в начале программы подключить описание типа string, которое находится в одноименном файле:

#include <string>

Переменная для хранения строковых данных объявляется так:

string S;

Присвоить строковой переменной некоторое константное значение можно так:

S = "Hello, world!";

С записью строк в тексте программы в кавычках мы уже встречались, когда выводили текст в поток cout. Обратите внимание - константы типа char записываются в одинарных кавычках, а строки - в двойных кавычках. В частности, 'A' - это символ, а "A" - это строка, состоящая из одного символа. Поэтому переменной типа char нельзя присвоить значение "A", поскольку они имеют несовместимые типы данных.

По сути, переменная типа string является массивом символов и с каждым символом этой строки можно работать по-отдельности, обращаясь к ним по индексу, как к элементам массива. Например:

cout << S[0]; // Будет выведен символ H
S[0] = 'h'; // Изменили первый символ в строке
cout << S; // Будет выведено hello, world!

Для определения длины строки есть метод size(), применяемый к строке. Он возвращает целое число - количество символов в строке. Его можно использовать так:

string S;
cin >> S;
cout << "Вы ввели слово " << S << " в котором " << S.size() << " символов" << endl;

Для начала нам понадобится две операции над строками: сложение двух строк и изменение размера строки.

Основная операция над строками - сложение: например, при сложении строк "Hello, " и "world!" получится строка "Hello, world!". Такая операция над строками называется конкатенацией.

Вот пример использования конкатенации строк:

string S, S1, S2;            // Объявление трех строк
cout << "Who are you? ";
cin >> S1;                   // Считали строку S1
S2 = "Hello, "               // Присвоили строке значение
S = S2 + S1;                 // Использование конкатенации
cout << S << endl;           // Вывод строки на экран
cout << S.size();          // Длина строки S

Другая операция - изменение размера строки. Для этого существует метод resize, который применяется к строке. У метода resize есть две формы записи: с одним и с двумя параметрами. Если он вызывается с одним параметром, то этот параметр задает новую длину строки. Например, так:

string S = "abcdefg"
S.resize(3);
cout << S << endl; // Будет выведено abc

Второй параметр метода resize задает символ, которым будут заполнены символы в строке, если размер строки увеличивается в результате изменения размера. Например:

string S = "abc"
S.resize(6, 'd');
cout << S << endl; // Будет выведено abcddd

При считывании строк из входного потока считываются все символы, кроме символов–разделителей (пробелов, табуляций и новых строк), которые являются границами между строками. Например, если при выполнении следующей программы

     string S1, S2, S3; // объявили 3 строки
     cin>>S1>>S2>>S3;

ввести текст ‘Мама мыла раму’ (с произвольным количеством пробелов между словами), то в массив S1 будет записана строка "Мама", в S2 "мыла", в S3 "раму".

Таким образом, организовать считывание всего файла по словам, можно следующим образом:

     string s;
     while (cin >> s) // Цикл пока считывание успешно
     {             // Делаем необходимые действия
     }

Если нужно считать строку со всеми пробелами, то необходимо использовать функцию getline следующим образом:

     string S;
     getline(cin, S);

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

string s;
while (getline(cin, S)) // Цикл пока считывание успешно
{                        // Делаем необходимые действия
}

Упражнения

A: ASCII-код символа

Считайте со стандартного ввода символ и выведите его ASCII-код. Решите эту задачу с использованием только одной переменной типа char.

Программа получает на вход один символ с ASCII кодом от 33 до 126.

Ввод Вывод
A
65

B: Символ с данным ASCII-кодом

Считайте со стандартного ввода целое число и выведите ASCII-символ с таким кодом. Решите эту задачу с использованием только одной переменной типа int.

Программа получает на вход число от 33 до 126.

Ввод Вывод
65
A

C: Таблица ASCII

Выведите все символы ASCII с кодами от 33 до 126 и их коды в следующем виде:

Вывод
! 33
" 34
# 35
...
} 125
~ 126

D: Символы в заданном интервале

Выведите подряд, без пробелов, все символы, лежащие в таблице ASCII между двумя заданными символами.

Программа получает на вход один символ с ASCII-кодом от 33 до 126, являющийся начальным символом интервала и число от 33 до 126, являющееся ASCII-кодом символа, завершающего интервал.

Ввод Вывод
A 68
ABCD
0 57
0123456789

E: is_digit

Для данного символа, считанного со стандартного ввода, проверьте, является ли он цифрой. Программа должна вывести слово YES, если символ является цифрой, или слово NO.

Решение оформите в виде функции bool is_digit(char c). В решении нельзя использовать циклы. В решении нельзя использовать константы с неочевидным значением типа 48 или 57.

Ввод Вывод
0
YES
A
NO

F: to_upper

Напишите функцию char to_upper(char c), которая переводит символ в верхний регистр, то есть для строчной буквы латинского алфавита возвращает сооветствующую заглавную букву латинского алфавита, а для остальных символов возвращает тот же символ.

Считайте один символ со стандартного ввода и переведите его в верхний регистр. В решении нельзя использовать циклы. В решении нельзя использовать константы с неочевидным значением.

Ввод Вывод
f
F
F
F
4
4

G: Сменить регистр символа

Напишите функцию char case_change(char c), меняющую регистр символа, то есть переводящую заглавные буквы в строчные, а строчные — в заглавные, остальные символы не меняющие.

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

Ввод Вывод
f
F
F
f
4
4

H: Нижний регистр

Дана строка, возможно, содержащая пробелы. Считайте эту строку и переведите все символы этой строки в нижний регистр. Решение оформите в виде функции void to_lower (string & S), получающей в качестве параметра строку по ссылке и изменяющая символы этой строки.

Для перевода одного символа в нижний регистр напишите отдельную функцию.

Ввод Вывод
Hello, world!
hello, world!

I: Проверить строки на равенство

Даны две строки (возможно, с пробелами). Проверьте, равны ли они. Если строки равны, выведите слово YES, если строки не равны, выведите слово NO.

Решение оформите в виде функции bool is_equal(const string &S1, const string & S2).

После того, как вы решите эту задачу, вам разрешается использовать оператор == для сравнения строк.

Ввод Вывод
Hi
HI
NO
Bye
Bye
YES

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

Даны две строки, не содержащие пробелов. Выведите конкатенацию этих строк.

Решение оформите в виде функции string concat(const string & s1, const string & s2), получающей в качестве параметров две строки и возвращающей новую строку — их конкатенацию.

После того, как вы решите эту задачу, вам разрешается использовать оператор + для конкатенации строк.

Ввод Вывод
abcd
xyz
abcdxyz

K: Substr

Дана строка и параметры \(i\), \(k\). Напишите функцию, которая возвращает подстроку данной строки, начиная с символа с индексом \(i\) и длиной \(k\).

Решение оформите в виде функции string substr(const string & s, int i, int k).

Если \(i<0\), или \(i\ge s.size()\) или \(k<0\), то функция должна возвращать пустую строку.

Если \(i+k\ge s.size()\), то возвращается строка длиной \(s.size() - i\), то есть до конца строки.

Ввод Вывод
abcdefghijk
2 4
cdef
abcdefghijk
6 10000
ghijk

L: Erase

Дана строка и параметры \(i\), \(k\). Напишите функцию, которая удаляет из данной строки подстроку начиная с символа с индексом \(i\) и длиной \(k\).

Решение оформите в виде функции void erase(string & s, int i, int k), которая модифицирует данную строку, не возвращая значения.

Если \(i<0\), или \(i\ge s.size()\) или \(k<0\), то функция не модифицирует исходную строку.

Если \(i+k\ge s.size()\), то удаляются все символы начиная с \(i\)-го до конца строки.

Программа не должна использовать дополнительные вспомогательные строки.

Ввод Вывод
abcdefghijk
2 4
abghijk
abcdefghijk
6 10000
abcdef

M: Insert

Дана строка \(s\), параметр \(i\), другая строка \(t\). Напишите функцию, которая вставляет в данную строку \(s\) начиная с индекса \(i\) строку \(t\).

Решение оформите в виде функции void insert(string & s, int i, const string & t), которая модифицирует данную строку, не возвращая значения.

Если \(i<0\) или \(i\gt s.size()\), функция не модифицирует исходную строку.

Программа не должна использовать дополнительные вспомогательные строки.

Ввод Вывод
abcde
2
xyz
abxyzcde

N: Лексикографический порядок

Даны две строки. Если первая строка меньше второй в лексикографическом порядке, выведите слово less. Если первая строка больше — выведите слово greater. Если строки равны — выведите слово equal.

Решение оформите в виде функции... Операциями сравнения для строк в этой задаче пользоваться нельзя.

Ввод Вывод
banan
baobab
less

O: Извлечь цифры

Дана строка, возможно, содержащая пробелы. Извлеките из этой строки все символы, являющиеся цифрами и составьте из них новую строку. Решение оформите в виде функции string extract_digits (const string & S), получающей на вход исходную строку S и возвращающую новую строку, содержащую только цифры данной строки.

Указание. Заведите строку Answer, пройдите по всем символам данной строки, при обнаружении цифры добавляйте ее в конец строки Answer, увеличивая ее размер на 1. По завершении цикла верните значение Answer.

Ввод Вывод
2+2=4
224

P: Палиндром

Дано слово, состоящее только из заглавных и строчных латинских букв. Проверьте, верно ли что это слово читается одинаково как справа налево, так и слева направо (то есть является палиндромом), если считать заглавные и строчные буквы не различающимися. Выведите слово YES, если слово является палиндромом и словов NO, если не является.

Решение оформите в виде функции bool is_palindrome (const string & S). При решении этой задачи нельзя пользоваться вспомогательными массивами или строками.

Ввод Вывод
Radar
YES
YES
NO

Q: Значение выражения - 1

Дана строка, состоящая из n цифр, между которыми стоит n-1 знак операции, каждый из которых может быть либо +, либо -. Вычислите значение данного выражения.

Решение оформите в виде функции int evaluate(const string & S).

Ввод Вывод
1+2-3
0

R: StrToInt

Дана строка, содержащее запись в виде символов целого числа от 0 до 109-1. Определите значение этого числа в виде переменной int. Решение задачи оформите в виде функции int str_to_int(const string & S).

Функция main должна быть такой:

int main()
{
    string S;
    cin >> S;
    cout << StrToInt(S) << endl;
    return 0;
}

Ввод Вывод
179
179

S: IntToStr

Дана целое число от -109+1 до 109-1. Запишите это число в строку, то есть выполните преобразование, обратное предыдущей задаче (но только допускаются отрицательные числа).

Решение задачи оформите в виде функции string int_to_str(int n).

Функция main должна быть такой:

int main()
{
    int n;
    cin >> n;
    cout << IntToStr(n) << endl;
    return 0;
}

Ввод Вывод
-179
-179

T: Шифр Цезаря

В шифре Цезаря каждый символ заменяется на другой символ, третий по счету в алфавите после данного, с цикличность. То есть символ A заменяется на D, символ B - на E, символ C - на F, ..., символ Z на C. Аналогично строчные буквы заменяются на строчные буквы. Все остальные символы не меняются.

Дана строка, зашифруйте ее при помощи шифра Цезаря. Решение оформите в виде функции void caesar_cipher(string & S).

Указание: сделайте функцию char caesar_cipher(char c), шифрующую один данный символ.

Ввод Вывод
In a hole in the ground there lived a hobbit.
Lq d kroh lq wkh jurxqg wkhuh olyhg d kreelw.

U: Количество слов

Дана строка, возможно, содержащая пробелы. Определите количество слов в этой строке. Слово - это несколько подряд идущих букв (как заглавных, так и строчных).

Решение оформите в виде функции int count_words(const string & S). При решении этой задачи нельзя пользоваться дополнительными строками и массивами.

Ввод Вывод
Yesterday, all my troubles seemed so far away
8

V: Самое длинное слово

Дана строка. Найдите в этой строке самое длинное слово и выведите его. Если в строке несколько слов одинаковой максимальной длины, выведите первое из них. Решение оформите в виде функции string longest_word(const string & S).

Ввод Вывод
In a hole in the ground there lived a hobbit.
ground

W: Слова с прописной буквы

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

Решение оформите в виде функции void capitalization(string & S).

Ввод Вывод
In a hole in the ground there lived a hobbit.
In A Hole In The Ground There Lived A Hobbit.

X: Значение выражения - 2

Дано выражение одно из следующих видов: “A+B”, “A-B” или “A*B”, где A и B - целые числа от 0 до 109. Определите значение этого выражения.

Решение оформите в виде функции eval(const string & S).

Ввод Вывод
2*2
4
100-101
-1

Y: Поиск подстроки

Даны две строки, возможно, содержащие пробелы. Выведите слово YES, если первая строка является подстрокой второй строки или слово NO в противном случае.

Решение оформите в виде функции bool is_substring(const string & Pattern, const string & Source).

Ввод Вывод
hole in the ground
In a hole in the ground there lived a hobbit.
YES
hole on the ground
In a hole in the ground there lived a hobbit.
NO

Z: Значение выражения - 3

Дана строка, содержащая одно или более целых чисел от 0 до 109, разделенных знаками “+” или “-”. Вычислите значение этого выражения.

Решение оформите в виде функции eval(const string & S).

Ввод Вывод
21+7-10
18