Как я могу сравнить строки в C с помощью оператора switch?

85

В C есть switchконструкция, которая позволяет выполнять различные условные ветви кода на основе тестового целочисленного значения, например,

Как можно получить такое же поведение (т.е. избежать так называемого « if- elseлестница») для значения строки, то есть char *?

Никлас
источник
Что вы имеете в виду под «включением»?
kennytm
Пожалуйста, переформулируйте вопрос, чтобы прояснить, что вы на самом деле пытаетесь сделать (или просите).
Шимон Тот
9
OP, вероятно, хочет использовать строку в качестве параметра оператора switch. Насколько я знаю, это невозможно.
dandan78

Ответы:

104

Если вы имеете в виду, как написать что-то подобное:

Тогда каноническим решением в C является использование лестницы if-else:

Барт ван Инген Шенау
источник
1
На самом деле проблема в том, что у меня уже есть переключатель на int, и в особом случае у меня есть значения «B1» и «B2», которые я хочу использовать в том же переключателе. Единственный способ - как-то преобразовать значения "B1" и "B2" и использовать их как int !!?
Никлас
2
@Niklas: Это важная информация для вашего вопроса. Можете ли вы обновить свой вопрос и объяснить (если возможно, с помощью некоторого (псевдо) кода), что вы пытаетесь сделать?
Барт ван Инген Шенау
5
@Niklas: Вы должны прояснить свой вопрос: как вообще «B1» и «B2» могут быть частным случаем int?
Эдгар Боне
1
#define A 1 #define B 2 #define C S1 #define D S2, и эти значения я хочу использовать в своем коммутаторе. Так просто :-)
Niklas
5
@Niklas: Определения - это не строки. Если определение предназначено для числа, вы можете использовать его прямо в своем переключателе следующим образом switch (something) { case A: /*...*/ break; case B: /*...*/ break; }.
Барт ван Инген Шенау,
46

Если у вас много дел и вы не хотите писать много strcmp()звонков, вы можете сделать что-то вроде:

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

Эдгар Боне
источник
9
«убедитесь, что ваша хеш-функция не имеет конфликтов внутри набора возможных значений строки». - Существует ли такая хеш-функция для алфавита [a-zA-Z0-9_]? Любой пример?
Арун,
8
@ArunSaha: Очевидно, не для произвольных комбинаций таких символов.
Эдгар Боне,
3
Если вы используете строковые ключи фиксированной длины, вы можете преобразовать каждый из них в уникальные целые числа; столкновения невозможны.
Инженер
@ArcaneEngineer Эм ... разве это не та проблема, которую пытается решить вопрос? Как, учитывая только строку, выбрать целое число? "используйте переключатель или лестницу if / else" Или, может быть, вы имеете в виду что-то очень короткое, например, 4 символа?
ebyrob 02
@ebyrob Я имел в виду все, что сравнимо в быстрой операции, например, 2 64-битных uint, биты которых обрабатываются как 8 однобайтовых ASCII char. Я реализовал это некоторое время назад для ключевых сравнений в хэш-таблице на C. Таким образом, вы устраняете необходимость в хешировании или ведрах. Проблема возникает тогда, когда вам нужно превышать 64 бита; затем вы оплачиваете стоимость условных операторов, перебирая каждый набор из 8 charсекунд в полной строке. Если вы не развернете цикл, если вы знаете максимальный размер ключей. Это прекрасный баланс.
Инженер
40

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

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

плинтус
источник
7
Намного удобнее использовать перечисление вместо набора #defines для ключей, но в остальном это лучшее, что вы можете сделать.
Крейг Рингер,
инкремент неправильный. lookuptable + i * sizeof (t_symstruct) не равно lookuptable [i].
asdf
@asdf Вот как работает арифметика указателей в c. Sizeof неявно.
ijustlovemath
20

Я предпочитаю сделать это с помощью хеш-функции (заимствованной отсюда ). Это позволяет вам использовать эффективность оператора switch даже при работе с char *:

Конечно, этот подход требует, чтобы хеш-значения для всех возможных принятых char * были рассчитаны заранее. Я не думаю, что это слишком большая проблема; однако, поскольку оператор switch работает независимо от фиксированных значений. Можно создать простую программу для передачи символов * через хэш-функцию и вывода их результатов. Затем эти результаты можно определить с помощью макросов, как я сделал выше.

Мэтью Раса
источник
Добро пожаловать в Stack Overflow. То, что вы показали, красиво представлено и является хорошей идеей, но ... но это не так уж сильно отличается от некоторых других ответов - есть несколько, в которых используются незначительные варианты этой идеи. Если вы добавите новый ответ на старый стабильный вопрос, вы должны быть уверены, что у вас есть хорошая новая информация. Это в основном предупреждение; Я, конечно, не собираюсь голосовать против вас за это.
Джонатан Леффлер,
16

Я думаю, что лучший способ сделать это - отделить «узнаваемость» от функциональности:

xtofl
источник
8

Я опубликовал файл заголовка для переключения строк в C. Он содержит набор макросов, которые скрывают вызов strcmp () (или аналогичный), чтобы имитировать поведение, подобное переключателю. Я тестировал его только с GCC в Linux, но уверен, что его можно адаптировать для поддержки других сред.

РЕДАКТИРОВАТЬ: добавил код здесь, как просили

Это заголовочный файл, который вы должны включить:

И вот как вы его используете:

Андреа Каррон
источник
Я отредактировал пример, не добавив «перерыв», а подчеркнув тот факт, что вы можете его опустить
Андреа Каррон
1
это лучше! Прежде чем использовать "sscanf" для сопоставления, я выучил "regex.h", который отлично подходит для строковых случаев :)
LinconFive
Какое красивое решение, хорошая читаемость и гораздо больше функциональности по сравнению с переключателем / корпусом - Спасибо! Не забывайте "switchs_end:" после закрывающей скобки.
Ахим,
6

Есть способ выполнить поиск строки быстрее. Предположения: поскольку мы говорим об операторе switch, я могу предположить, что значения не будут меняться во время выполнения.

Идея состоит в том, чтобы использовать qsort и bsearch из C stdlib.

Я буду работать над кодом xtofl.

Дариуш
источник
6

Чтобы добавить к ответу Phimueme выше, если ваша строка всегда состоит из двух символов, вы можете построить 16-битное int из двух 8-битных символов - и включить его (чтобы избежать вложенных операторов switch / case).

Майк Бром
источник
Если действительно хотите To add to Phimueme's answer above, то смело используйте функцию комментариев. :)
Onion-Knight
3
@Onion: Вы заметите, что у Майка Брома в настоящее время нет репутации, чтобы комментировать посты, кроме его собственных, и ответов на свои вопросы. Тем не менее, @Mike "выше" скользкий в SO, потому что нет надежного порядка сортировки. Лучше дать ссылку на ответ типа «... в ответе Phimueme ...» (хотя этот ответ сейчас удален, и ссылка годна только для пользователя с репутацией 10k +).
dmckee --- котенок экс-модератора
3

Мы не можем избежать лестницы if-else, чтобы сравнить строку с другими. Даже обычный switch-case внутри также является лестницей if-else (для целых чисел). Мы могли бы захотеть смоделировать только switch-case для строки, но никогда не сможем заменить лестницу if-else. Лучший из алгоритмов сравнения строк не может избежать использования функции strcmp. Средство сравнения символа за символом, пока не будет найдено несоответствие. Поэтому использование лестницы if-else и strcmp неизбежно.

ДЕМО

А вот простейшие макросы для имитации switch-case для строк.

И вы можете использовать их как

Вывод:

Ниже показано использование вложенного SWITCH:

Вывод:

Вот обратная строка SWITCH, где вы можете использовать переменную (а не константу) в предложении CASE:

Вывод:

Раму
источник
«Даже обычный switch-case внутренне также является лестницей if-else (для целых чисел)», что неверно. Если возможно, компилятор сгенерирует таблицу переходов, что будет намного эффективнее. См. Stackoverflow.com/a/14067661/4990392
Дада,
2

Я обычно так делаю.

EvilTeach
источник
Интересно. Отсутствует (вероятно, по выбору) защитное кодирование. И я восхищаюсь дополнительными подтяжками на всякий случай. Делает код более читаемым (хотя я предпочитаю египетские скобки для регистра).
Дариуш
1
Кстати, вы можете использовать постоянные выражения в метках case. case 'B'<<8+'1':Я думаю, это проясняет это, чем 0x4231.
Йенс
Я бы использовал макрос. #define twochar(a) (((uint16_t)a[1]<<8)|a[0])
v7d8dpo4 09
1

Вот как вы это делаете. Нет, не совсем.

пользователь14554
источник
3
Я не думаю, что это стандартная C.
Йохан Котлински
2
Сделать макрос, поддерживающий смешанный порядок байтов, или функцию, оставим читателю в качестве упражнения.
2
Это стандартный C, но не переносимый. Порядок байтов многобайтовых char является «зависимым от реализации» и не обязательно должен отражать порядок байтов машины. Я использовал это однажды и обгорел: в Solaris SPARC (big endian) GNU-C 3.4 использует другой порядок байтов, чем Sunstudio 12.
Патрик Шлютер
@tristopia Вы, конечно, правы (настолько правы, насколько это возможно после попытки сделать что-то подобное по-настоящему). Вот почему мы все должны использовать вместо этого B.
Почему вы убили свой аккаунт?
1

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

LANIDX_ * - постоянные целые числа, используемые для индексации в массивах.

Патрик Шлютер
источник
0

Предполагая небольшой порядок байтов и sizeof (char) == 1, вы могли бы это сделать (что-то вроде этого было предложено MikeBrom).

Его можно было бы обобщить на случай BE.

руслик
источник
2
Не делай этого! Это может вызвать исключение «выравнивания данных». Не гарантируется, что char * txt указывает на адрес, который соответствует требованиям выравнивания int.
harper
@R он просил об этом. @harper это не относится к x86.
Ruslik
Никлас не просил x86. И поскольку вы упомянули случай с прямым порядком байтов, вы не рассматриваете исключительно среду x86. Так что '
harper
Более того, многобайтовые символы не обязательно располагаются в машинном порядке байтов. См. Мой комментарий к ответу jbcreix.
Патрик Шлютер,
0

Указатели функций - отличный способ сделать это, например

result = switchFunction(someStringKey); //result is an optional return value

... это вызывает функцию, которую вы установили с помощью строкового ключа (одна функция на случай):

Используйте уже существующую реализацию hashmap / table / dictionary, такую ​​как khash, верните этот указатель на функцию внутри switchFunction()и выполните ее (или просто верните ее switchFunction()и выполните самостоятельно). Если реализация карты этого не хранит, просто используйте uint64_tвместо этого, которое вы приводите соответственно к указателю.

Инженер
источник
@ eri0o Если вы думали, что это прилично, почему бы не проголосовать за него? Первого отрицателя уже давно нет.
Инженер
-2

Привет, это простой и быстрый способ, если у вас есть этот случай:

[БЫСТРЫЙ режим]

[РАЗЪЯСНЕННЫЙ режим]

Например: у меня много меню, каждый выбор в 1-м меню переводит вас во 2-е меню, то же самое со 2-м меню и 3-м меню. Но параметры разные, поэтому вы знаете, что пользователь окончательно выбрал. пример:

меню 1: 1 ==> меню 2: 4 ==> меню 3: 2 (...) выбор - 142. другие случаи: 111,141,131,122 ...

Решение: сохраните первый 1-й в a, 2-й в b, 3-й в c. а = 1, б = 4, с = 2

Абдулл Бенламин
источник