Так много сложных методов ... Просто найдите все группы с названием «Названия» и присоединитесь к нижнему варианту с обложкой с подчеркиванием.
>>> import re
>>> def camel_to_snake(string):
... groups = re.findall('([A-z0-9][a-z]*)', string)
... return '_'.join([i.lower() for i in groups])
...
>>> camel_to_snake('ABCPingPongByTheWay2KWhereIsOurBorderlands3???')
'a_b_c_ping_pong_by_the_way_2_k_where_is_our_borderlands_3'
Если вы не хотите, чтобы цифры, такие как первый символ группы или отдельная группа - вы можете использовать маску ([A-z][a-z0-9]*)
.
Вы можете определить новую метку , которая освободит ресурсы, а затем вы можете ПОЛУЧИТЬ это всякий раз, когда ваш код не работает.
char* function()
{
char* retval = NULL;
char* mem = get_memory(100); // first allocation
if (!mem)
goto out_error;
struct binder* b = get_binder('regular binder'); // second allocation
if (!b)
goto out_error_mem;
struct file* f = mk_file(); // third allocation
if (!f)
goto out_error_b;
/* ... Normal code path ... */
retval = good_value;
out_error_b:
free_binder(b);
out_error_mem:
free(mem);
out_error:
return retval;
}
Управление ошибками с помощью GOTO уже обсуждалось здесь: Допустимое использование goto для управления ошибками в C?
Хотя я восхищаюсь вашим подходом к защитному кодированию, и это хорошо. И каждый программист C должен иметь такой менталитет, он может применяться и к другим языкам ...
Я должен сказать, что это единственная полезная вещь в GOTO, несмотря на то, что пуристы скажут иначе, это было бы эквивалентно окончательному блоку, но есть одна особая ошибка, которую я могу видеть там ...
код karlphillip почти завершен, но .... предположим, что функция была выполнена следующим образом
char* function() {
struct file* f = mk_file(); // third allocation
if (!f) goto release_resources;
// DO WHATEVER YOU HAVE TO DO....
return some_ptr;
release_resources:
free(mem);
free_binder(b);
return NULL;
}
Будьте осторожны !!! Это будет зависеть от дизайна и назначения функции, которую вы считаете нужной, откладывая в сторону ... если бы вы вернулись из этой функции, вы могли бы в конечном итоге провалиться через метку release_resources
.... которая могла бы При возникновении незначительной ошибки все ссылки на указатели в куче исчезают и могут привести к возврату мусора ... поэтому убедитесь, что если у вас есть выделенная память и верните ее обратно, используйте ключевое слово return
непосредственно перед меткой, в противном случае память может исчезнуть ... или создать утечку памяти ...
Если ваши структуры данных являются сложными / вложенными, одного перехода может быть недостаточно, и в этом случае я предлагаю что-то вроде:
mystruct = malloc(sizeof *mystruct);
if (!mystruct) goto fail1;
mystruct->data = malloc(100);
if (!mystruct->data) goto fail2;
foo = malloc(sizeof *foo);
if (!foo) goto fail2;
...
return mystruct;
fail2:
free(mystruct->data);
fail1:
free(mystruct);
Пример из реального мира будет более сложным и может включать несколько уровней Вложение структуры, связанные списки и т. д. Обратите внимание, что здесь нельзя вызвать free(mystruct->data);
(поскольку разыменование элемента из mystruct
недопустимо), если первый malloc
не удался.
Вы также можете использовать противоположный подход и проверить на успех:
struct binder* b = get_binder('regular binder'); // second allocation
if(b) {
struct ... *c = ...
if(c) {
...
}
free(b);
}
Я знаю, что меня линчуют за это, но у меня был друг, который сказал, что они использовали для этого goto
.
Затем он сказал мне, что в большинстве случаев этого недостаточно, и теперь он использовал setjmp ()
/ longjmp ()
. По сути, он заново изобрел исключения C ++, но с гораздо меньшей элегантностью.
При этом, поскольку goto
может работать, вы можете преобразовать его во что-то, что не использует goto
, но отступы быстро выйдут из-под контроля:
char* function() {
char* result = NULL;
char* mem = get_memory(100);
if(mem) {
struct binder* b = get_binder('regular binder');
if(b) {
struct file* f = mk_file();
if (f) {
// ...
}
free(b);
}
free(mem);
}
return result;
}
Кстати, разброс объявлений локальных переменных по блоку, подобный этому, не является стандартом C.
Теперь, если вы понимаете, что free (NULL);
определяется стандартом C как do- ничего, вы можете упростить вложение:
char* function() {
char* result = NULL;
char* mem = get_memory(100);
struct binder* b = get_binder('regular binder');
struct file* f = mk_file();
if (mem && b && f) {
// ...
}
free(f);
free(b);
free(mem);
return result;
}
Если вы хотите сделать это без goto
, вот подход, который хорошо масштабируется:
char *function(char *param)
{
int status = 0; // valid is 0, invalid is 1
char *result = NULL;
char *mem = NULL:
struct binder* b = NULL;
struct file* f = NULL:
// check function parameter(s) for validity
if (param == NULL)
{
status = 1;
}
if (status == 0)
{
mem = get_memory(100); // first allocation
if (!mem)
{
status = 1;
}
}
if (status == 0)
{
b = get_binder('regular binder'); // second allocation
if (!b)
{
status = 1;
}
}
if (status == 0)
{
f = mk_file(); // third allocation
if (!f)
{
status = 1;
}
}
if (status == 0)
{
// do some useful work
// assign value to result
}
// cleanup in reverse order
free(f);
free(b);
free(mem);
return result;
}