Используя C, преобразуйте динамично выделенный международный массив в разделенную от запятой строку максимально чисто

Если Ваше все еще наличие проблемы, проверьте использование enctype в теге form

номер:

<form encoding="multipart/form-data" action="/upload">

Да:

<form enctype="multipart/form-data" action="/upload">
7
задан Michael Ekstrand 17 November 2009 в 00:49
поделиться

13 ответов

Код теперь протестирован и строится под gcc.

В отличие от других ответов, не требует C99.

Настоящая проблема здесь заключается в том, что не зная длину строки, которую вы будет нуждаться. Получить число так же просто, как sprintf ("% u", * num) , используя num для обхода вашего массива int s, но сколько места тебе понадобится? Чтобы избежать переполнения буфера, вы должны отслеживать множество целых чисел.

size_t join_integers(const unsigned int *num, size_t num_len, char *buf, size_t buf_len) {
    size_t i;
    unsigned int written = 0;

    for(i = 0; i < num_len; i++) {
        written += snprintf(buf + written, buf_len - written, (i != 0 ? ", %u" : "%u"),
            *(num + i));
        if(written == buf_len)
            break;
    }

    return written;
}

Обратите внимание, что я отслеживаю, какой объем буфера я использовал, и использую snprintf , чтобы не переполнять конец. snprintf будет прикреплять к \ 0 , но поскольку я использую buf + написанный , я начну с \ 0 предыдущий snprintf .

Используется:

int main() {
    size_t foo;
    char buf[512];

    unsigned int numbers[] = { 10, 20, 30, 40, 1024 };

    foo = join_integers(numbers, 5, buf, 512);
    printf("returned %u\n", foo);
    printf("numbers: %s\n", buf);
}

Выходы:

returned 20
numbers: 10, 20, 30, 40, 1024

Принудительное срабатывание ограничения вместо превышения:

char buf[15];    
foo = join_integers(numbers, 5, buf, 14);
buf[14] = '\0';

Ожидаемые результаты:

returned 14
numbers: 10, 20, 30, 4
9
ответ дан 6 December 2019 в 10:51
поделиться

Фактически вы можете использовать такую ​​библиотеку, как Glib , которая содержит такие функции, как

gchar * g_strjoin (const gchar * separator, ...);

Объединяет несколько строк в одну длинную строку с дополнительным разделителем, вставленным между каждой из них. Возвращенная строка должна быть освобождена с помощью g_free ().

(вам все равно нужно использовать g_snprintf () , возможно, с g_printf_string_upper_bound () , чтобы освободить место)

2
ответ дан 6 December 2019 в 10:51
поделиться
char buf [11 * sizeof (my_list)];
for (int n = 0, int j = 0;  j < sizeof (my_list) / sizeof (my_list [0]);  ++j)
    n += sprintf (&buf [n], "%s%u",   (j > 0) ? "," : "",  my_list [j]);
1
ответ дан 6 December 2019 в 10:51
поделиться
unsigned *a; /* your input a[N] */
unsigned i,N;
char *b,*m;
b=m=malloc(1+N*11); /* for each of N numbers: 10 digits plus comma (plus end of string) */
for (i=0;i<N;++i)
  b+=sprintf(b,"%u,",a[i]);
if (N>0) b[-1]=0; /* delete last trailing comma */
/* now use m */
free(m);

красиво, правда? :)

1
ответ дан 6 December 2019 в 10:51
поделиться

Что насчет этого?

char *join_int_list(const unsigned int *list, size_t n_items)
{
     enum { SIZEOF_INT_AS_STR = sizeof("4294967295,")-1 };
     char *space = malloc(SIZEOF_INT_AS_STR * n_items);
     if (space != 0)
     {
         size_t i;
         char *pad = "";
         char *dst = space;
         char *end = space + SIZEOF_INT_AS_STR * n_items;
         for (i = 0; i < n_items; i++)
         {
              snprintf(dst, end - dst, "%s%u", pad, list[i]);
              pad = ",";
              dst += strlen(dst);
         }
         space = realloc(space, dst - space + 1);
     }
     return(space);
}

Вызывающий объект обязан освободить возвращаемый указатель и проверить, не является ли он нулевым перед его использованием. 'Realloc ()' освобождает дополнительное пространство, если выделенная сумма была слишком большой, чтобы окупить ее. Этот код беспечно предполагает, что значения действительно являются 32-битными целыми числами без знака; если они могут быть больше, то перечисление требует соответствующей настройки.

Протестированный код:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

char *join_int_list(const unsigned int *list, size_t n_items)
{
    enum { SIZEOF_INT_AS_STR = sizeof("4294967295,")-1 };
    char *space = malloc(SIZEOF_INT_AS_STR * n_items);
    if (space != 0)
    {
        size_t i;
        char *pad = "";
        char *dst = space;
        char *end = space + SIZEOF_INT_AS_STR * n_items;
        for (i = 0; i < n_items; i++)
        {
            snprintf(dst, end - dst, "%s%u", pad, list[i]);
            pad = ",";
            dst += strlen(dst);
        }
        space = realloc(space, dst - space + 1);
    }
    return(space);
}

int main(void)
{
    static unsigned int array[]= { 1, 2, 3, 49, 4294967295U, 0, 332233 };
    char *str = join_int_list(array, sizeof(array)/sizeof(array[0]));
    printf("join: %s\n", str);
    free(str);
    return(0);
}

Проверено с помощью valgrind - вроде нормально.


Обсуждение преобразования INT_MAX или UINT_MAX в строку:

Вы можете использовать sizeof ("," STRINGIZE (INT_MAX)) вместо жесткого кодирования. Макрос строкового преобразования - это обычный инструмент cpp, который можно определить как #define STRINGIZE_ (v) #v и #define STRINGIZE (v) STRINGIZE_ (v). - Р. Пейт

@R Pate: хорошая идея - да, вы могли бы сделать это довольно эффективно. На самом деле, здесь есть две интересные идеи: использование конкатенации строк с помощью sizeof () (скобки необходимы для ясности, но конкатенация строк происходит достаточно рано, чтобы компилятор не беспокоился) и использование операции преобразования строк в INT_MAX . - Джонатан Леффлер

Использование операции преобразования строки в INT_MAX не является хорошей идеей - это просто должно быть «постоянное выражение», а не обязательно последовательность цифр. Его можно определить как ((1 << 32) -1) или даже что-то странное, например __int_max, если компилятор позволяет вам использовать его везде, где вы можете использовать постоянное выражение. - caf

И @caf прав. Рассмотрим этот код:

#include <limits.h>
#include <stdio.h>

#undef INT_MAX
#define INT_MAX (INT_MIN-1 - 100 + 100)

#define QUOTER(x)   #x
#define STRINGIZER(x)   QUOTER(x)

enum { SIZEOF_INT_AS_STR = sizeof("4294967295,")-1 };
enum { SIZEOF_INT_AS_STR_1 = sizeof(STRINGIZER(INT_MAX) ",")-1 };

int main(void)
{
    printf("size = %d = %d\n", SIZEOF_INT_AS_STR, SIZEOF_INT_AS_STR_1);
    printf("INT_MAX  = %d\n", INT_MAX);
    printf("UINT_MAX = %u\n", UINT_MAX);
    return(0);
}

Он даже не компилируется на MacOS X 10.5.8 с GCC 4.0. 1 - потому что идентификатор INT_MAX не определен. Предварительная версия кода, которая не печатала INT_MAX или UINT_MAX , работала; он показал, что значение SIZEOF_INT_AS_STR_1 было 31 - значит, @caf был правильным. Добавление двойной проверки значений INT_MAX и UINT_MAX затем не удалось скомпилировать, что меня удивило. Взгляд на вывод gcc -E показывает, почему:

enum { SIZEOF_INT_AS_STR = sizeof("4294967295,")-1 };
enum { SIZEOF_INT_AS_STR_1 = sizeof("((-INT_MAX - 1)-1 - 100 + 100)" ",")-1 };

int main(void)
{
 printf("size = %d = %d\n", SIZEOF_INT_AS_STR, SIZEOF_INT_AS_STR_1);
 printf("INT_MAX  = %d\n", ((-INT_MAX - 1)-1 - 100 + 100));
 printf("UINT_MAX = %u\n", (((-INT_MAX - 1)-1 - 100 + 100) * 2U + 1U));
 return(0);
}

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

Расширение INT_MAX оказывается в терминах INT_MIN и INT_MIN , в свою очередь,

2
ответ дан 6 December 2019 в 10:51
поделиться

Ребята, вам платят по телефону? : -)


f () объявлен с параметром char * для целей прототипирования, просто измените char -> int . Я интерпретировал вопрос как требующий вывода строки, а не просто кода для записи в файл.

#define PRINT(s, l, x, i) snprintf((s), (l), "%s%d", (i) ? ",":"", (x)[i]);

char *f(size_t len, char *x) {
  size_t  i, j = 0, k;

  for(i = 0; i < len; ++i)
      j += PRINT(NULL, 0, x, i);
  char *result = malloc(k = ++j);
  for(*result = i = j = 0; i < len; ++i)
      j += PRINT(result + j, k - j, x, i);
  return result;
}

Вот тестовая среда:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// put f() here

int main(int ac, char **av) {
    for(int i = 1; i < ac; ++i) { 
        printf("%s\n", f(strlen(av[i]), av[i]));
    }
    return 0;
}
1
ответ дан 6 December 2019 в 10:51
поделиться

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

:)

#include <stdio.h>

char* toStr(int arr[], unsigned int arrSize, char buff[])
{
    if (arr && arrSize && buff)
    {
        int* currInt = arr;
        char* currStr = buff;
        while (currInt < (arr + arrSize))
        {
            currStr += sprintf(currStr, "%d,", *currInt++);
        }
        *--currStr = '\0';
    }
    return buff;
}

int main()
{
    int arr[] = {1234, 421, -125, 15251, 15251, 52};
    char buff[1000];

    printf("Arr is:%s\n", toStr(arr, 6, buff));    
}

Предполагается, что buff достаточно велик, выделите его (длина наибольшего int + 2) * arrSize). Вдохновил мой memcpy :)

Edit Я понял, что раньше у меня был мозговой перд, и я мог просто увеличить на возвращаемое значение sprintf вместо сохранения temp. По-видимому, другие ответы также делают это, отредактировал мой ответ, чтобы удалить 2 ненужные строки.

Edit2 Похоже, непослушный победил меня! Его ответ практически идентичен моему и был отправлен ранее. Я смиренно предлагаю поставить ему +1.

1
ответ дан 6 December 2019 в 10:51
поделиться

Предположим, что когда вы упоминаете «для ведения журнала», вы имеете в виду запись в файл журнала, тогда ваше решение может выглядеть примерно так (псевдокодировано):

for (int x in array) {
    fprintf(log, "%d", x);
    if (! last element)
        fputc(log, ',');
}
0
ответ дан 6 December 2019 в 10:51
поделиться

Если вы хотите поместить его в файл, ответ Стивена Шланскера подойдет.

Если вы хотите поместить его в строку, все усложняется. Вы можете использовать sprintf , но вы должны быть осторожны, чтобы у вас не закончилось место в вашей строке. Если у вас есть совместимый с C99 snprintf (Linux, BSD, не Windows), следующий (непроверенный, некомпилированный) код должен работать:

char *buf = malloc(1024); /* start with 1024 chars */
size_t len = 1024;
int pos = 0;
int rv;
int i;
for (i = 0; i < n; i++) {
    rv = snprintf(buf+pos, len - pos, "%s%d", i = 0 ? "" : ",", my_list[i]);
    if (rv < len - pos) {
        /* it fit */
        pos += rv;
    } else {
        len *= 2;
        buf = realloc(buf, len);
        if (!buf) abort();
        i--; /* decrement i to repeat the last iteration of the loop */
    }
}
return buf;

Вызывающий должен затем освободить buf .

0
ответ дан 6 December 2019 в 10:51
поделиться

Лично для простоты и, вероятно, скорости, я бы выделил большой буфер , с пространством для массива размером «4 294 967 295» и «,» для каждого элемента. Однако при создании списка это неэффективно по пространству!

Затем я бы вставил туда целые числа и добавил "," ко всем элементам

Наконец, я перераспределил указатель, чтобы у него было не больше места, чем обязательный. (size = strlen)

sprintf: В случае успеха возвращается общее количество записанных символов. Это количество не включает дополнительный нулевой символ, автоматически добавляемый в конец строки.

Таким образом вы отслеживаете, где strcpy находится в строке. :)

Надеюсь, это поможет! : )

И если вы хотите просто распечатать их, посмотрите вместо них другие ответы. (цикл for и printf)

0
ответ дан 6 December 2019 в 10:51
поделиться

К сожалению, всегда будет три случая:

  • Пустой список (без запятых, без элементов)
  • Один элемент (без запятых, один элемент)
  • Два или более элементов ( n-1 запятых, n элементов)

Метод join скрывает эту сложность для вас, поэтому он такой красивый.

В C я бы сделал:

for (i = 0; i < len; i++)
{
    if (i > 0)   /* You do need this separate check, unfortunately. */
        output(",");
    output(item[i]);
}

Где output , однако, вы добавляете к строке. Это может быть просто strcat в предварительно выделенном буфере или printf для некоторого потока (например, поток памяти, о котором я узнал сегодня в Создание потока FILE * что приводит к строке : -).

Если вас раздражает выполнение этой проверки каждый раз для всех i> = 1, вы можете сделать:

if (i > 0)
{
    output(item[0]);
    for (i = 1; i < len; i++)
    {
        output(",");
        output(item[i]);
    }
}
0
ответ дан 6 December 2019 в 10:51
поделиться
#include <stdio.h>
#include <stdlib.h>

/* My approach is to count the length of the string required. And do a single alloc.
     Sure you can allocate more, but I don't know for how long this data will be retained.
*/ 

#define LEN(a) (sizeof a / sizeof *a)

int main(void) {

    unsigned a[] = {1, 23, 45, 523, 544};
    int i, str_len=0, t_written=0;
    char tmp[11]; /* enough to fit the biggest unsigned int */

    for(i = 0; i < LEN(a); i++) 
        str_len += sprintf(tmp, "%d", a[i]);

    /* total: we need LEN(a) - 1 more for the ',' and + 1 for '\0' */
    str_len += LEN(a);
    char *str = malloc(str_len); 
    if (!str) 
        return 0;

    if (LEN(a) > 1) {
        t_written += sprintf(str+t_written, "%d", a[0]);
        for(i = 1; i < LEN(a); i++)
            t_written += sprintf(str+t_written, ",%d", a[i]);
    } else if (LEN(a) == 1) 
        t_written += sprintf(str+t_written, "%d", a[0]);

    printf("%s\n", str);

    free(str);
    return 0;
}
1
ответ дан 6 December 2019 в 10:51
поделиться
void join(int arr[], int len, char* sep, char* result){
    if(len==0){
        *result='\0';
    } else {
        itoa(arr[0],result,10);
        if(len > 1){
            strcat(result,sep);
            join(arr+1,len-1,sep,result+strlen(result));
        }
    }
}
0
ответ дан 6 December 2019 в 10:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: