2014-02-18 2 views
1

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

/* The normalize procedure normalizes a character array of size len 
    according to the following rules: 
    1) turn all upper case letters into lower case ones 
    2) turn any white-space character into a space character and, 
     shrink any n>1 consecutive whitespace characters to exactly 1 whitespace 

    When the procedure returns, the character array buf contains the newly 
    normalized string and the return value is the new length of the normalized string. 

*/ 
int 
normalize(unsigned char *buf, /* The character array contains the string to be normalized*/ 
        int len  /* the size of the original character array */) 
{ 
    /* use a for loop to cycle through each character and the built in c functions to analyze it */ 
    int i; 

if(isspace(buf[0])){ 
    buf[0] = ""; 
} 
if(isspace(buf[len-1])){ 
    buf[len-1] = ""; 
} 

    for(i = 0;i < len;i++){ 
     if(isupper(buf[i])) { 
      buf[i]=tolower(buf[i]); 
     } 
     if(isspace(buf[i])) { 
      buf[i]=" "; 
     } 
     if(isspace(buf[i]) && isspace(buf[i+1])){ 
      buf[i]=""; 
     } 
    } 

    return strlen(*buf); 


} 

Как вернуть длину массива символов в конце? Также правильно ли моя процедура делает то, что я хочу?

EDIT: Я внесла некоторые исправления в свою программу на основе комментариев. Правильно ли сейчас?

/* The normalize procedure normalizes a character array of size len 
    according to the following rules: 
    1) turn all upper case letters into lower case ones 
    2) turn any white-space character into a space character and, 
     shrink any n>1 consecutive whitespace characters to exactly 1 whitespace 

    When the procedure returns, the character array buf contains the newly 
    normalized string and the return value is the new length of the normalized string. 

*/ 
int 
normalize(unsigned char *buf, /* The character array contains the string to be normalized*/ 
        int len  /* the size of the original character array */) 
{ 
    /* use a for loop to cycle through each character and the built in c funstions to analyze it */ 
    int i = 0; 
    int j = 0; 

    if(isspace(buf[0])){ 
     //buf[0] = ""; 
     i++; 
    } 
    if(isspace(buf[len-1])){ 
     //buf[len-1] = ""; 
     i++; 
    } 
    for(i;i < len;i++){ 
     if(isupper(buf[i])) { 
      buf[j]=tolower(buf[i]); 
      j++; 
     } 
     if(isspace(buf[i])) { 
      buf[j]=' '; 
      j++; 
     } 
     if(isspace(buf[i]) && isspace(buf[i+1])){ 
      //buf[i]=""; 
      i++; 
     } 
    } 

    return strlen(buf); 


} 
+0

Используйте '» «'вместо'»«'' вернуть STRLEN (* ЬиЕ) 'должен быть' обратный STRLEN (BUF) ' – Machtl

+0

В C, строка заканчивается ' '\ 0''. Использование 'len' подразумевает' buf' - массив 'unsigned char', не обязательно _string_. Выбери один. – chux

+0

'if (isspace (buf [len-1])) {i ++; } 'является проблемой – chux

ответ

1

Канонический способ сделать что-то вроде этого - использовать два индекса: один для чтения и один для записи. Как это:

int normalizeString(char* buf, int len) { 
    int readPosition, writePosition; 
    bool hadWhitespace = false; 
    for(readPosition = writePosition = 0; readPosition < len; readPosition++) { 
     if(isspace(buf[readPosition]) { 
      if(!hadWhitespace) buf[writePosition++] = ' '; 
      hadWhitespace = true; 
     } else if(...) { 
      ... 
     } 
    } 
    return writePosition; 
} 

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

int normalizeString(char* string) { //No length is passed, it is implicit in the null byte. 
    char* in = string, *out = string; 
    bool hadWhitespace = false; 
    for(; *in; in++) { //loop until the zero byte is encountered 
     if(isspace(*in) { 
      if(!hadWhitespace) *out++ = ' '; 
      hadWhitespace = true; 
     } else if(...) { 
      ... 
     } 
    } 
    *out = 0; //add a new zero byte 
    return out - string; //use pointer arithmetic to retrieve the new length 
} 

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

+0

Я отредактировал свое начальное сообщение, чтобы включить обновленный код. Он эффективен сейчас? Также кто-то указал, что buf - это массив неподписанного символа, а не строка, это что-то изменит? – Lesha

+0

Нет, это не так, вы все еще неадекватно используете 'strlen' для вывода размера строки. Даже если параметр 'len' включает в себя завершающий нулевой байт, который затем копируется в правильную позицию по вашему циклу, возвращающий результат' strlen' изменяет семантику размера, поскольку она не включает завершающий нулевой байт. Из этого следует смешение и безумие. Либо вы забываете о завершении нулевого значения, и, следовательно, забываете использовать какие-либо стандартные функции обработки строк или правильно обрабатываете строку с нулевыми байтами и забываете о параметрах длины строки. – cmaster

+0

Было указано, что 'buf' на самом деле является массивом символов, а не строкой. Я понимаю, потому что на самом деле строка 'buf' не имеет этого завершающего нулевого байта. Я делаю неправильное предположение здесь? И означает ли это, что я не могу использовать 'strlen'? – Lesha

1

Обозначения как:

buf[i]=" "; 
buf[i]=""; 

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

Предупреждение: непроверенный код.

int i, j; 
for (i = 0, j = 0; i < len; i++) 
{ 
    if (isupper(buf[i])) 
     buf[j++] = tolower(buf[i]); 
    else if (isspace(buf[i]) 
    { 
     buf[j++] = ' '; 
     while (i+1 < len && isspace(buf[i+1])) 
      i++; 
    } 
    else 
     buf[j++] = buf[i]; 
} 
buf[j] = '\0'; // Null terminate 

Вы заменить произвольное пустое пространство с гладким пространством с помощью:

buf[i] = ' '; 

Вы возвращаетесь:

return strlen(buf); 

или с указанным кодом:

return j; 
+0

'buf [j] = '\ 0'' может написать 1 мимо памяти' buf'. OP неоднозначно относится к этому вызову 'len'« размер исходного массива символов ». – chux

+0

Я так не думаю, если вход является строкой с нулевой длиной указанной длины (так что 'buf [len] == '\ 0'). Если это не строка с нулевым завершением, то нулевое завершение неверно - период. Но это C, а не Java, а нулевое прекращение - это норма, а длина, как описано, также является нормой. –

+0

Согласен, учитывая, что «вводится строка с нулевым завершением заданной длины». ОП использовали «размер» и «неподписанный символ» и «массив», а также функцию с параметрами, включая размер/длину, которые намекали на это байтовый массив, а не на строку С. OP также использовал «струну» для мутной воды. Поэтому я опубликовал ОП для разъяснения. – chux

1
if(isspace(buf[i])) { 
    buf[i]=" "; 
} 

Это должно быть buf[i] = ' ', а не buf[i] = " ". Вы не можете назначить строку символу.

if(isspace(buf[i]) && isspace(buf[i+1])){ 
    buf[i]=""; 
} 

У этой проблемы есть две проблемы. Во-первых, вы не проверяете, i < len - 1, так что buf[i + 1] может быть в конце строки. Другое дело, что buf[i] = "" не будет делать то, что вы хотите вообще. Чтобы удалить символ из строки, вам нужно использовать memmove, чтобы переместить оставшееся содержимое строки влево.

return strlen(*buf); 

Это будет return strlen(buf). *buf - это символ, а не строка.

+0

Не могли бы вы рассказать о том, как memmove можно использовать в этом случае. Я посмотрел, но я не понимаю, как использовать его для моей цели. – Lesha

0

вы должны написать:

return strlen(buf) 

вместо:

return strlen(*buf) 

Причина:

buf имеет тип CHAR * - это адрес из полукокса где-то в памяти (тот, который находится в начале строки). Строка имеет нулевой конец (или, по крайней мере, должен быть), и поэтому функция strlen знает, когда прекратить подсчет символов.

*buf будет ссылаться на указатель, в результате чего на char - не то, что strlen ожидает.

+1

Две строки кода идентичны – Phonon

1

Несколько ошибок в коде:

  1. Вы не можете назначить buf[i] со строкой, например, "" или " ", так как тип buf[i] является char и тип струны char*.

  2. Вы читаете от buf и записывали в buf используя индекс i. Это создает проблему, так как вы хотите исключить последовательные пробелы. Поэтому вы должны использовать один индекс для чтения и другой индекс для записи.

  3. В C/C++, родной строкой является массив символов, который заканчивается с 0. Таким образом, в сущности, вы можете просто перебирать buf, пока не прочтете 0 (вам не нужно использовать переменную len в все). Кроме того, так как вы «усечения» входной строки, вы должны установить новый последний символ 0.

Здесь есть один опциональное решение для задачи под рукой:

int normalize(char* buf) 
{ 
    char c; 
    int i = 0; 
    int j = 0; 
    while (buf[i] != 0) 
    { 
     c = buf[i++]; 
     if (isspace(c)) 
     { 
      j++; 
      while (isspace(c)) 
       c = buf[i++]; 
     } 
     if (isupper(c)) 
      buf[j] = tolower(c); 
     j++; 
    } 
    buf[j] = 0; 
    return j; 
} 
0

Не сильно отличается от других, но предполагает, что это массив из unsigned char, а не строка C.

tolower() само по себе не требуется тест isupper().

int normalize(unsigned char *buf, int len) { 
    int i = 0; 
    int j = 0; 
    int previous_is_space = 0; 
    while (i < len) { 
    if (isspace(buf[i])) { 
     if (!previous_is_space) { 
     buf[j++] = ' '; 
     } 
     previous_is_space = 1; 
    } else { 
     buf[j++] = tolower(buf[i]); 
     previous_is_space = 0; 
    } 
    i++; 
    } 
    return j; 
} 

@OP:
Per отправленного коде подразумевает ведущие и ведомые пробелы должны либо быть уменьшен до 1 charили устранить все начальные и конечные пробелы.
Приведенный выше ответ просто сжимает передние и конечные пробелы до 1 ' '. Для устранения отставая и ведущие пробелы:

int i = 0; 
int j = 0; 
while (len > 0 && isspace(buf[len-1])) len--; 
while (i < len && isspace(buf[i])) i++; 
int previous_is_space = 0; 
while (i < len) { ... 
Смежные вопросы