Apa cara terbaik (termurah) untuk memasukkan string kompleks CamelCase?

Saya memiliki banyak frasa masuk waktu nyata yang perlu diubah menjadi alpha only - CamelCase berdasarkan kata dan titik pisah.

Itulah yang saya temukan sejauh ini, tetapi apakah ada cara yang lebih murah dan cepat untuk melakukan tugas itu?

function FoxJourneyLikeACamelsHump(string $string): string {
  $string = preg_replace("/[^[:alpha:][:space:]]/u", ' ', $string);
  $string = ucwords($string);
  $camelCase = preg_replace('/\s+/', '', $string);
  return $camelCase;
}

// $expected = "ThQuCkBrWnFXJumpsVRThLZyDG";
$string = " Th3 qu!ck br0wn f0x jumps 0v3r th3 l@zy d0g. ";
$is = FoxJourneyLikeACamelsHump($string);

Hasil:

Kalimat: 100000000
Total waktu: 40.844197034836 seconds
rata-rata: 0.000000408


person mkungla    schedule 08.04.2017    source sumber
comment
Tolong berhenti menggunakan terlalu banyak huruf tebal.   -  person Daedalus    schedule 08.04.2017
comment
@Daedalus, apa masalahmu dengan huruf tebal di tempat yang tepat?   -  person mkungla    schedule 08.04.2017
comment
cukup adil, bukan niat saya untuk membuat jengkel siapa pun. Berpikir untuk memberi perhatian pada kendala yang saya hadapi tanpa ada yang perlu membaca terlalu banyak, tapi akan mengingatnya lain kali.   -  person mkungla    schedule 08.04.2017
comment
Anda melakukan 0,2 miliar penggantian berbasis regex dalam waktu sekitar 41 detik - itu tidak cukup?   -  person Robin Mackenzie    schedule 08.04.2017
comment
Anda tidak mengatakan mengapa kinerja saat ini menjadi masalah: Anda mungkin perlu lebih membingkai situasi untuk mengontekstualisasikannya. Kita mungkin melihat bagian masalah yang salah. Seperti yang disinggung oleh @RobinMackenzie... mungkin ini merupakan kasus optimasi prematur bagi saya. Apakah Anda sebenarnya mempunyai masalah terkait bisnis yang ingin Anda selesaikan? yaitu: hal ini memakan waktu terlalu lama, dan akibatnya kita kehilangan uang. Saat itulah seseorang mungkin perlu memulai optimasi mikro. Bukan berarti Anda tidak memiliki kasus yang sah; tapi kamu tidak menjelaskannya.   -  person Adam Cameron    schedule 08.04.2017
comment
Karena Anda ingin menangani string unicode, Anda tidak dapat menggunakan fungsi seperti ucwords atau ucfirst yang tidak mengetahui unicode.   -  person Casimir et Hippolyte    schedule 08.04.2017


Jawaban (4)


Kode Anda cukup efisien. Anda masih dapat meningkatkannya dengan beberapa penyesuaian:

  • Berikan pembatas ke ucwords sehingga tidak perlu mencari \t, \n, dll, yang tidak akan ada dalam string Anda setelah langkah pertama. Rata-rata hal ini memberikan peningkatan 1%;
  • Anda dapat melakukan langkah terakhir dengan penggantian non-regex pada spasi. Ini memberikan peningkatan hingga 20%.

Kode:

function FoxJourneyLikeACamelsHump(string $string): string {
    $string = preg_replace("/[^[:alpha:][:space:]]/u", ' ', $string);
    $string = ucwords($string, ' ');
    $camelCase = str_replace(' ', '', $string);
    return $camelCase;
}

Lihat pengaturan waktu untuk versi asli dan versi yang lebih baik di rextester.com.

Catatan: Saat Anda menggunakan ucwords, kode Anda tidak dapat digunakan dengan andal untuk string unicode secara umum. Untuk menutupinya, Anda perlu menggunakan fungsi seperti mb_convert_case:

$string = mb_convert_case($string,  MB_CASE_TITLE);

...tapi ini berdampak pada kinerja.

person trincot    schedule 08.04.2017

Dibandingkan dengan 3 alternatif, saya yakin metode Anda adalah yang tercepat. Berikut hasil dari 100.000 iterasi:

array(4) {
  ["Test1"]=>
  float(0.23144102096558)
  ["Test2"]=>
  float(0.41140103340149)
  ["Test3"]=>
  float(0.31215810775757)
  ["Test4"]=>
  float(0.98423790931702)
}

Dimana Test1 milikmu, Test2 dan Test3 milikku, dan Test4 dari jawaban @RizwanMTuman (dengan perbaikan).

Saya pikir menggunakan preg_split dapat memberi Anda kesempatan untuk mengoptimalkan. Dalam fungsi ini, hanya 1 regex yang digunakan dan mengembalikan array yang hanya berisi item alfa yang kemudian Anda terapkan ucfirst ke:

function FoxJourneyLikeACamelsHump_2(string $string): string {
    return implode('', array_map(function($word) {
        return ucfirst($word);
    }, preg_split("/[^[:alpha:]]/", $string, null, PREG_SPLIT_NO_EMPTY)));
}

Ini dapat lebih dioptimalkan dengan menggunakan foreach daripada array_map (lihat di sini):

function FoxJourneyLikeACamelsHump_3(string $string): string {
    $validItems = preg_split("/[^[:alpha:]]/u", $string, null, PREG_SPLIT_NO_EMPTY);
    $result = '';
    foreach($validItems as $item) {
        $result .= ucfirst($item);
    }
    return $result;
}

Hal ini membuat saya berspekulasi bahwa 2 regex dan 1 ucwords lebih cepat dari 1 regex dan beberapa ucfirsts.

Skrip tes lengkap:

<?php

// yours
function FoxJourneyLikeACamelsHump_1(string $string): string {
  $string = preg_replace("/[^[:alpha:][:space:]]/u", ' ', $string);
  $string = ucwords($string);
  $camelCase = preg_replace('/\s+/', '', $string);
  return $camelCase;
}

// mine v1
function FoxJourneyLikeACamelsHump_2(string $string): string {
    return implode('', array_map(function($word) {
        return ucfirst($word);
    }, preg_split("/[^[:alpha:]]/", $string, null, PREG_SPLIT_NO_EMPTY)));
}

// mine v2
function FoxJourneyLikeACamelsHump_3(string $string): string {
    $validItems = preg_split("/[^[:alpha:]]/u", $string, null, PREG_SPLIT_NO_EMPTY);
    $result = '';
    foreach($validItems as $item) {
        $result .= ucfirst($item);
    }
    return $result;
}

// Rizwan with a fix
function FoxJourneyLikeACamelsHump_4(string $string): string {
    $re = '/(?:\b|\d+)([a-z])|[\d+ +!.@]/';
    $result = preg_replace_callback($re,function ($matches) {
        return (isset($matches[1]) ? strtoupper($matches[1]) : '');
    },$string);
    return $result;
}


// $expected = "ThQuCkBrWnFXJumpsVRThLZyDG";
$test1 = 0;
$test2 = 0;
$test3 = 0;
$test4 = 0;

$loops = 100000;

$time_start = microtime(true);
for($i=0; $i<$loops; $i++) {
    $string = " Th3 qu!ck br0wn f0x jumps 0v3r th3 l@zy d0g. ";
    $is = FoxJourneyLikeACamelsHump_1($string);
    if($loops==1) echo $is."\n";
}
$time_end = microtime(true);
$test1 = $time_end - $time_start;

$time_start = microtime(true);
for($i=0; $i<$loops; $i++) {
    $string = " Th3 qu!ck br0wn f0x jumps 0v3r th3 l@zy d0g. ";
    $is = FoxJourneyLikeACamelsHump_2($string);
    if($loops==1) echo $is."\n";
}
$time_end = microtime(true);
$test2 = $time_end - $time_start;

$time_start = microtime(true);
for($i=0; $i<$loops; $i++) {
    $string = " Th3 qu!ck br0wn f0x jumps 0v3r th3 l@zy d0g. ";
    $is = FoxJourneyLikeACamelsHump_3($string);
    if($loops==1) echo $is."\n";
}
$time_end = microtime(true);
$test3 = $time_end - $time_start;

$time_start = microtime(true);
for($i=0; $i<$loops; $i++) {
    $string = " Th3 qu!ck br0wn f0x jumps 0v3r th3 l@zy d0g. ";
    $is = FoxJourneyLikeACamelsHump_4($string);
    if($loops==1) echo $is."\n";
}
$time_end = microtime(true);
$test4 = $time_end - $time_start;

var_dump(array('Test1'=>$test1, 'Test2'=>$test2, 'Test3'=>$test3, 'Test4'=>$test4));
person Robin Mackenzie    schedule 08.04.2017

Anda dapat mencoba ekspresi reguler ini:

(?:\b|\d+)([a-z])|[\d+ +!.@]

UPDTAE ( Jalankan di sini )

Ide di atas adalah untuk menunjukkan kepada Anda bagaimana hal tersebut seharusnya bekerja di regex:

Berikut ini adalah implementasi php dari regex di atas. Anda dapat membandingkannya dengan milik Anda karena ini memungkinkan operasi dilakukan dengan operasi penggantian tunggal:

<?php

$re = '/(?:\b|\d+)([a-z])|[\d+ +!.@]/';
$str = 'Th3 qu!ck br0wn f0x jumps 0v3r th3 l@zy d0g. ';
$subst=strtoupper('\\1');

$result = preg_replace_callback($re,function ($matches) {
return (isset($matches[1]) ? strtoupper($matches[1]) : '');
    },$str);

echo $result;

?>

Demo Regex

person Rizwan M.Tuman    schedule 08.04.2017
comment
OP bertanya sehubungan dengan kecepatan. Juga bukankah \U merupakan regex101.com hal yang spesifik? - person vallentin; 08.04.2017
comment
Downvoting & menandai karena tidak menjawab pertanyaan. Dialek regex yang digunakan tidak valid di PHP, dan jelas tidak diuji di PHP. - person Adam Cameron; 08.04.2017
comment
@AdamCameron Saya telah memperbarui jawabannya.. bukankah menurut Anda terlalu dini untuk menandai jawaban yang tidak memberi Anda implementasi php, bukan ide umum tentang solusinya? - person Rizwan M.Tuman; 08.04.2017
comment
Anda mendapatkan banyak kesalahan PHP Notice: Undefined offset: 1 serta hasil yang benar dengan itu. Anda dapat mencoba return (isset($matches[1]) ? strtoupper($matches[1]) : ''); - person Robin Mackenzie; 08.04.2017
comment
@RobinMackenzie Anda benar sekali .. diperbarui ... terima kasih sobat - person Rizwan M.Tuman; 08.04.2017
comment
@rizwan: Saya menandainya sebagai bukan jawaban, tetapi mungkin ada beberapa revisi. Anda tampaknya benar-benar setuju. Tidak yakin apa masalahnya. Pembaruan Anda sekarang bagus, dan saya akan menghapus suara negatif saya. - person Adam Cameron; 08.04.2017
comment
@Adam Cameron, santai sobat, saya juga tidak seserius itu :) ..tapi saya tidak pernah mendapat bendera jadi kurang jelas tentang penandaannya ;) - person Rizwan M.Tuman; 08.04.2017

Sebelum berpikir untuk meningkatkan kinerja suatu kode, Anda perlu terlebih dahulu membuat kode yang berfungsi. Sebenarnya Anda mencoba membuat kode yang menangani string berkode utf8 (karena Anda menambahkan pengubah u ke pola Anda); tetapi dengan string: liberté égalité fraternité kode Anda mengembalikan Liberté égalité Fraternité bukannya Liberté Égalité Fraternité karena ucwords (atau ucfirst) tidak dapat menangani karakter multibyte.

Setelah mencoba pendekatan yang berbeda (dengan preg_split dan preg_replace_callback), tampaknya versi preg_match_all ini adalah yang tercepat:

function FoxJourneyLikeACamelsHumpUPMA(string $string): string {
    preg_match_all('~\pL+~u', $string, $m);
    foreach ($m[0] as &$v) {
        $v = mb_strtoupper(mb_substr($v, 0, 1)) . mb_strtolower(mb_substr($v, 1));
    }
    return implode('', $m[0]);
}

Jelas, ini lebih lambat dari kode awal Anda, tetapi kami tidak dapat membandingkan kode-kode yang berbeda ini karena kode Anda tidak berfungsi.

person Casimir et Hippolyte    schedule 09.04.2017