Простая манипуляция строкой C - PullRequest
3 голосов
/ 07 августа 2010

Я пытаюсь выполнить очень простую обработку строк в C (например, с помощью имени файла, отрубить расширение файла, изменить имя файла и затем добавить его обратно) - Я довольно ржавый на C и получаю ошибки сегментации

char* fname;
char* fname_base;
char* outdir;
char* new_fname;
.....
fname = argv[1];
outdir = argv[2];
fname_len = strlen(fname);
strncpy(fname_base, fname, (fname_len-4)); // weird characters at the end of the truncation?
strcpy(new_fname, outdir); // getting a segmentation on this I think

strcat(new_fname, "/");
strcat(new_fname, fname_base);
strcat(new_fname, "_test");
strcat(new_fname, ".jpg");
printf("string=%s",new_fname);  

Любые предложения или указатели приветствуются.

Большое спасибо и извинения за такой основной вопрос

Ответы [ 8 ]

3 голосов
/ 07 августа 2010

Вам необходимо выделить память для new_fname и fname_base. Вот как бы вы это сделали для new_fname:

new_fname = (char*)malloc((strlen(outdir)+1)*sizeof(char));

В strlen(outdir)+1 часть +1 предназначена для выделения памяти для терминатора NULL CHARACTER '\0'.

2 голосов
/ 07 августа 2010

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

strncpy(fname_base, fname, (fname_len-4));

Вы предполагаете, что хотите отрубить последние 4 символа (. ???). Если расширение файла отсутствует или это не 3 символа, это не будет делать то, что вы хотите. Следующее должно дать вам представление о том, что может понадобиться (я предполагаю, что последний «.» Указывает расширение файла). Обратите внимание, что мой 'C' очень ржавый (предупреждение!)

char *s;
s = (char *) strrchr (fname, '.');
if (s == 0)
{
    strcpy (fname_base, fname);
}
else
{
    strncpy (fname_base, fname, strlen(fname)-strlen(s));
    fname_base[strlen(fname)-strlen(s)] = 0;
}
1 голос
/ 07 августа 2010

В следующем коде я не называю malloc.

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

/*  Change this to '\\' if you are doing this on MS-windows or something like it. */
#define DIR_SYM '/'
#define EXT_SYM '.'
#define NEW_EXT "jpg"


int main(int argc, char * argv[] ) {
   char * fname;
   char * outdir;

   if (argc < 3) {
       fprintf(stderr, "I want more command line arguments\n");
       return 1;
   }
   fname = argv[1];
   outdir = argv[2];

   char * fname_base_begin = strrchr(fname, DIR_SYM); /* last occurrence of DIR_SYM */
   if (!fname_base_begin) {
       fname_base_begin = fname; // No directory symbol means that there's nothing
                                 // to chop off of the front.
   }

   char * fname_base_end = strrchr(fname_base_begin, EXT_SYM);
   /* NOTE: No need to search for EXT_SYM in part of the fname that we have cut off
    * the front and then have to deal with finding the last EXT_SYM before the last
    * DIR_SYM */
   if (!fname_base_end) {
       fprintf(stderr, "I don't know what you want to do when there is no extension\n");
       return 1;
   }

   *fname_base_end = '\0'; /* Makes this an end of string instead of EXT_SYM */
   /* NOTE:  In this code I actually changed the string passed in with the previous
    * line.  This is often not what you want to do, but in this case it should be ok.
    */

   // This line should get you the results I think you were trying for in your example
   printf("string=%s%c%s_test%c%s\n", outdir, DIR_SYM, fname_base_begin, EXT_SYM, NEW_EXT);

   // This line should just append _test before the extension, but leave the extension
   // as it was before.
   printf("string=%s%c%s_test%c%s\n", outdir, DIR_SYM, fname_base_begin, EXT_SYM, fname_base_end+1);

   return 0;
} 

Мне удалось избежать выделения памяти для сборки строки, потому что я позволил printf на самом деле беспокоиться о ее построении и воспользовался тем, что знал, что исходная строка fname не понадобится в будущем. *

Я мог бы выделить место для строки, вычислив, как долго она должна основываться на деталях, а затем использовать sprintf для формирования строки для меня.

Кроме того, если вы не хотите изменять содержимое строки fname, вы также могли бы использовать:

printf("string=%s%c%*s_test%c%s\n", outdir, DIR_SYM, (unsigned)fname_base_begin -(unsigned)fname_base_end, fname_base_begin, EXT_SYM, fname_base_end+1);

Чтобы сделать printf, используйте только часть строки.

1 голос
/ 07 августа 2010

Код уборщика:

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

const char *extra = "_test.jpg";

int main(int argc, char** argv)
{
  char *fname = strdup(argv[1]); /* duplicate, we need to truncate the dot */
  char *outdir = argv[1];
  char *dotpos;
  /* ... */
  int new_size = strlen(fname)+strlen(extra);
  char *new_fname = malloc(new_size);   
  dotpos = strchr(fname, '.');
  if(dotpos)
    *dotpos = '\0'; /* truncate at the dot */
  new_fname = malloc(new_size);
  snprintf(new_fname, new_size, "%s%s", fname, extra);
  printf("%s\n", new_fname);
  return 0;
}
1 голос
/ 07 августа 2010

вы можете объединить предложенное malloc со строковыми манипуляциями в одном выражении

if ( asprintf(&new_fname,"%s/%s_text.jpg",outdir,fname_base) >= 0 )
     // success, else failed

затем, в какой-то момент, free(new_fname), чтобы освободить память.

(обратите внимание, что это расширение GNU, которое также доступно в * BSD)

1 голос
/ 07 августа 2010

Вы используете неинициализированные указатели в качестве целей для функций, подобных strcpy: fname_base и new_fname: вам нужно выделить области памяти для работы или объявить их как массив символов, например,

char fname_base[FILENAME_MAX];
char new_fname[FILENAME_MAX]; 
1 голос
/ 07 августа 2010

Вы должны malloc fname_base и new_fname, я полагаю.

есть:

fname_base = (char *)(malloc(sizeof(char)*(fname_len+1)));
fname_base[fname_len] = 0; //to stick in the null termination

и аналогично для new_fname и outdir

0 голосов
/ 07 августа 2010

Основой любой манипуляции со строкой C является то, что вы должны записывать (и читать из, если только ... ...) память, которой вы «владеете». Объявление чего-либо является указателем (type *x) резервирует место для указателя, а не для указателя, который, конечно, не может быть известен по магии, и поэтому вы должны использовать malloc (или подобное) или обеспечить локальный буфер такими вещами, как char buf[size].

И вы всегда должны знать о переполнении буфера.

Как и предполагалось, использование sprintf (с правильно распределенным буфером назначения) или тому подобное может быть хорошей идеей. В любом случае, если вы хотите сохранить свой текущий подход strcat, я помню, что для объединения строк strcat всегда должен "обходить" текущую строку с ее начала, так что, если вам не нужен буфер (ops!) Любые проверки переполнения, добавление символов «вручную» происходит немного быстрее: в основном, когда вы закончили добавлять строку, вы знаете, где находится новый конец, и в следующей строке strcat вы можете начать с нее.

Но strcat не позволяет узнать адрес последнего добавленного символа, и использование strlen сведет на нет усилия. Таким образом, возможное решение может быть

size_t l = strlen(new_fname);
new_fname[l++] = '/';
for(i = 0; fname_base[i] != 0; i++, l++) new_fname[l] = fname_base[i];
for(i = 0; testjpgstring[i] != 0; i++, l++) new_fname[l] = testjpgstring[i];
new_fname[l] = 0; // terminate the string...

и вы можете продолжать использовать l ... (testjpgstring = "_test.jpg")

Однако, если ваша программа полна манипуляций со строками, я предлагаю использовать библиотеку для строк (для ленивости я часто использую glib)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...