0
void split(char array[]) {
    int length, length2, half; 
    char first_half[BUF], sec_half[BUF];

    length = strlen(array);
    length2 = length / 2;
    length -= length2;

    strncpy(first_half, array, length - 1);
    strncpy(sec_half, (array + length), length2 - 1);

    printf("%s\n", first_half);
    printf("%s\n", sec_half);
}

If input is "People", it outputs: Pe0z plcy etc... Just wondering what's the problem here.

My question is not the same as the question mentioned. I am not trying to insert a word in the middle of another word, I am trying to split a word in half and put the two halves in two strings.

chqrlie
  • 131,814
  • 10
  • 121
  • 189

5 Answers5

2

OP's trouble is that first_half[BUF], sec_half[BUF] are not certainly null character terminated - a short coming of strncpy().

strncpy( first_half, array, length);
// printf( "%s\n", first_half );
// alternative limited width print, even if `first_half` lacks a null character.
printf( "%.*s\n", length, first_half );

trying to split a wordstring in half and put the two halves in two strings.

Below is a variable length array (VLA) approach.

I recommend memcpy() as that is often fastest for long strings and lest error prone than strncpy() @Blastfurnace. YMMV.

void split(const char array[]) {
  size_t a_length = strlen(array);
  size_t l_length = a_length / 2;
  size_t r_length = a_length - l_length;
  char l_half[l_length + 1], r_half[r_length + 1];  // + 1 for null character

  memcpy(l_half, array, l_length);
  l_half[l_length] = '\0';
  memcpy(r_half, array + l_length, r_length + 1); // +1 for null character

  printf("<%s>\n", l_half);
  printf("<%s>\n", r_half);
}

size_t, an unsigned type, is the right sized integer type to use for array indexing. int maybe insufficient. Note that strlen() returns type size_t.


If all that is wanted is to print the 2 halves, not much code needed:

void split_print(const char array[]) {
  int half = strlen(array) / 2;
  printf("%.*s\n", half, array);
  printf("%s\n", array + half);
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

Since I see that there is a concern about performance, I'd say get rid of strncpy altogether.

fist_half[l_length] = '\0';
while(l_length--)
  first_half[l_length] = array[l_length];

The same method can be applied to the right side with some index modification. This way you can get rid of overhead and HEADACES of strncpy.

If you use pointers the code can be even further simplified.

m0h4mm4d
  • 400
  • 4
  • 12
1

One other issue not yet considered is "How do I handle words with an odd number of characters?". It's easy to split 1234 into 12 and 34, but how do you split 123? You must decide how to test for odd/even and decide which half gets the extra character (generally the first half, but it's up to you). You should also consider splitting strings of any size (and validate against processing a NULL pointer or empty-string passed to your function).

To handle checking odd/even length, all you need to do is check the 1st-bit of length on little-endian systems, or for complete portability just use modulo 2. If the 1st-bit (on little-endian) or the result of len % 2 is 0 it's even, otherwise the length is odd. (if odd, just add +1 character to the first half).

If your compiler supports Variable Length Array C99 extensions, the problem becomes simply a matter of declaring the two VLAs and letting the compiler handle the work, e.g.

void split_vla (char *s)
{
    if (!s || !*s) {  /* validate parameter not NULL or empty-string */
        fprintf (stderr, "error: s NULL or empty-string.\n");
        return;
    }

    size_t l = strlen (s), 
        n = (l % 2 == 0) ? l / 2 : l / 2 + 1;
    char s1[n + 1], s2[n + 1];  /* declare VLAs */

    memcpy (s1, s, n);          /* memcpy chars to each half */
    memcpy (s2, s + n, l - n);

    s1[n] = 0;                  /* affirmatively nul-terminate */
    s2[l - n] = 0;

    printf ("s  : %s\ns1 : %s\ns2 : %s\n", s, s1, s2);
}

If it doesn't, you can always dynamically allocate storage for each of the halfs (and if you choose calloc over malloc, then your storage is initialized to all zeros (providing a nul-byte at the end of each half), e.g.

void split_dyn (char *s)
{
    if (!s || !*s) {  /* validate parameter not NULL or empty-string */
        fprintf (stderr, "error: s NULL or empty-string.\n");
        return;
    }

    size_t l = strlen (s),
        n = (l % 2 == 0) ? l / 2 : l / 2 + 1;
    char *s1, *s2;

    /* allocate storage for s1 & s2 and validate allocation */
    if (!(s1 = calloc (n + 1, 1)) || !(s2 = calloc (n + 1, 1))) {
        fprintf (stderr, "error: virtual memory exhausted.\n");
        exit (EXIT_FAILURE);
    }

    memcpy (s1, s, n);          /* memcpy chars to each half */
    memcpy (s2, s + n, l - n);

    printf ("s  : %s\ns1 : %s\ns2 : %s\n", s, s1, s2);

    free (s1);    /* don't forget to free the memory you allocate */
    free (s2);
}

Either way is fine depending on what your compiler supports. You are free to use malloc and affirmatively nul-terminate as was done in the VLA example. Also be aware that alloca gives similar functionality to what you get with VLAs, but provides a bit more flexibility in the scope for your arrays (not relevant here since the VLAs are declared with function scope but be aware of the additional method). Give both examples a try and let me know if you have further questions.

Example Use/Output

$ ./bin/str_split_half 123456789
s  : 123456789
s1 : 12345
s2 : 6789

$ ./bin/str_split_half 1234567890
s  : 1234567890
s1 : 12345
s2 : 67890

$ ./bin/str_split_half 1
s  : 1
s1 : 1
s2 :
David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Unclear why `!*s` test needed: `split_vla ("")` seems like an OK call. `n = (l % 2 == 0) ? l / 2 : l / 2 + 1;` can be simplified to `n = (l + 1)/2;` in this case. `s2[n + 1]` is OK yet 1 larger than needed with odd `l`. Still a useful post. – chux - Reinstate Monica Jul 12 '17 at 13:01
0

It is enough if you initialize the buffers with 0 as shown in split1(...)

char first_half[BUF] = {0}; // whole buffer is full of zeroes
char sec_half[BUF] = {0};

Then termination will be automatic.

The uninitialized buffer contains random characters. If buffers are not zeroed the termination is needed as shown in split2(...)

The solutions are as close as possible to the original code. They work for even, odd and empty strings.

Please note the symmetry of the solutions with memcpy and strncpy.

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

#define BUF 255

void split1(char array[], int bUseMemcpy)
{
    /* use zeroed buffers to automatically terminated the copied strings */
    int length, length2; 
    char first_half[BUF] = {0};
    char sec_half[BUF] = {0};

    length = strlen( array );
    length2 = length / 2;
    length -= length2;

    if(bUseMemcpy)
        memcpy(  first_half, array, length); 
    else    
        strncpy( first_half, array, length); 

    if(bUseMemcpy)   
        memcpy(  sec_half, (array + length), length2);
    else    
        strncpy( sec_half, (array + length), length2);

    printf( "<%s>\n", first_half );
    printf( "<%s>\n", sec_half );
}

void split2(char array[], int bUseMemcpy)
{
    /* Do not initialize the buffers
     Use 0 to terminate the buffers after the copy */
    int length, length2;
    char first_half[BUF];
    char sec_half[BUF];  

    length = strlen(array);
    length2 = length / 2;
    length -= length2;

    if(bUseMemcpy)
        memcpy(  first_half, array, length); 
    else    
        strncpy( first_half, array, length);

    first_half[length]=0; /*zero termination needed, since buffers are not initialized to 0*/

    if(bUseMemcpy)   
        memcpy(  sec_half, (array + length), length2);
    else    
        strncpy( sec_half, (array + length), length2);

    sec_half[length2]=0; // zero termination

    printf( "<%s>\n", first_half );
    printf( "<%s>\n", sec_half );
  }

int main(void) {
    split1("123456789",0);
    split1("",1);

    split2("people",0);
    split2("1",1);

    return 0;
}

Output:

<12345>
<6789>
<>
<>
<peo>
<ple>
<1>
<>
sg7
  • 6,108
  • 2
  • 32
  • 40
  • My issue with this is zeroing the entire buffer may be more work than necessary and if you reuse the buffer you can have incorrectly terminated strings from `strncpy` again. – Blastfurnace Jul 12 '17 at 02:14
  • @Blastfurnace I agree: _`strncpy` is easy to misuse. Might as well use memcpy and manually add the NUL terminator_. In the example above the buffer is not reused though. – sg7 Jul 12 '17 at 02:26
  • What happens with `"123456789"` or `"apple"`? – David C. Rankin Jul 12 '17 at 03:43
  • @David C. Rankin All cases are covered! The solutions work for even, odd and empty strings. Thanks for asking. – sg7 Jul 12 '17 at 13:15
0

There is a simple way to output the string split on 2 separate lines: use printf with the %.*s conversion format. It prints a initial portion of the string argument:

void split(const char *array) {
    int half = strlen(array) / 2;
    printf("%.*s\n%s\n", half, array, array + half);
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189