วิธีที่ดีที่สุด (ถูกที่สุด) สำหรับสตริงอินพุตที่ซับซ้อนของ CamelCase คืออะไร

ฉันมีวลีขาเข้าแบบเรียลไทม์จำนวนมากซึ่งจำเป็นต้องแปลงเป็น alpha only - CamelCase ตามคำและจุดแยก

นั่นคือสิ่งที่ฉันได้คิดมาจนถึงตอนนี้ แต่มีวิธีที่ถูกกว่าและเร็วกว่าในการทำงานนั้นหรือไม่?

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);

ผลลัพธ์:

ประโยค: 100000000
เวลาทั้งหมด: 40.844197034836 seconds
เฉลี่ย: 0.000000408


person mkungla    schedule 08.04.2017    source แหล่งที่มา
comment
กรุณาหยุดใช้ตัวหนามาก   -  person Daedalus    schedule 08.04.2017
comment
@Daedalus คุณมีปัญหาอะไรกับตัวหนาในตำแหน่งที่ถูกต้อง?   -  person mkungla    schedule 08.04.2017
comment
ยุติธรรมพอสมควร ไม่ได้มีเจตนาจะทำให้ใครระคายเคือง คิดว่าจะดึงความสนใจไปที่อุปสรรคที่ฉันกำลังเผชิญโดยไม่มีใครต้องอ่านมากนัก แต่จะจำไว้ครั้งต่อไป   -  person mkungla    schedule 08.04.2017
comment
คุณทำการแทนที่โดยใช้ regex 0.2 พันล้านครั้งในเวลาประมาณ 41 วินาที ซึ่งยังดีไม่พอใช่ไหม   -  person Robin Mackenzie    schedule 08.04.2017
comment
คุณไม่ได้บอกว่าเหตุใดประสิทธิภาพปัจจุบันจึงเป็นปัญหา: คุณอาจต้องวางกรอบสถานการณ์ให้มากขึ้นอีกเล็กน้อยเพื่อให้เข้าใจบริบท เราอาจมองส่วนที่ผิดของปัญหา ดังที่ @RobinMackenzie พาดพิงถึง... นี่อาจเป็นกรณีของการเพิ่มประสิทธิภาพก่อนเวลาอันควรสำหรับฉัน คุณมีปัญหาเกี่ยวกับธุรกิจที่คุณพยายามแก้ไขจริงๆ หรือไม่? เช่น สิ่งนี้ใช้เวลานานเกินไป และส่งผลให้เราสูญเสียเงิน นั่นคือเวลาที่ใครคนหนึ่งอาจต้องเริ่มการเพิ่มประสิทธิภาพแบบไมโคร ไม่ได้บอกว่าคุณไม่มีมีคดีที่ชอบด้วยกฎหมาย แต่คุณไม่อธิบายมัน   -  person Adam Cameron    schedule 08.04.2017
comment
เนื่องจากคุณต้องการจัดการกับสตริง Unicode คุณจึงไม่สามารถใช้ฟังก์ชันเช่น ucwords หรือ ucfirst ที่ไม่รับรู้ Unicode ได้   -  person Casimir et Hippolyte    schedule 08.04.2017


คำตอบ (4)


รหัสของคุณค่อนข้างมีประสิทธิภาพ คุณยังคงสามารถปรับปรุงได้ด้วยการปรับแต่งเล็กน้อย:

  • ระบุตัวคั่นเป็น ucwords เพื่อจะได้ไม่ต้องค้นหา \t, \n ฯลฯ ซึ่งจะไม่อยู่ในสตริงของคุณหลังจากขั้นตอนแรก โดยเฉลี่ยจะดีขึ้น 1%;
  • คุณดำเนินการขั้นตอนสุดท้ายได้ด้วยการแทนที่ที่ไม่ใช่ Regex บนช่องว่าง สิ่งนี้ช่วยให้ปรับปรุงได้มากถึง 20%

รหัส:

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

ดูกำหนดเวลาสำหรับเวอร์ชันดั้งเดิมและเวอร์ชันปรับปรุงได้ที่ rextester.com

หมายเหตุ: เมื่อคุณใช้ ucwords โค้ดของคุณจะไม่สามารถนำมาใช้กับสตริง Unicode โดยทั่วไปได้อย่างน่าเชื่อถือ เพื่อครอบคลุมสิ่งนั้น คุณจะต้องใช้ฟังก์ชันเช่น mb_convert_case:

$string = mb_convert_case($string,  MB_CASE_TITLE);

... แต่สิ่งนี้มีผลกระทบต่อประสิทธิภาพ

person trincot    schedule 08.04.2017

เมื่อเทียบกับ 3 ทางเลือก ฉันเชื่อว่าวิธีการของคุณเร็วที่สุด ผลลัพธ์จากการทำซ้ำ 100,000 ครั้งมีดังนี้

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

โดยที่ Test1 เป็นของคุณ Test2 และ Test3 เป็นของฉัน และ Test4 มาจากคำตอบของ @RizwanMTuman (พร้อมการแก้ไข)

ฉันคิดว่าการใช้ preg_split อาจให้โอกาสคุณในการเพิ่มประสิทธิภาพ ในฟังก์ชันนี้ จะใช้ regex เพียง 1 รายการและส่งกลับอาร์เรย์ของรายการอัลฟ่าเท่านั้น จากนั้นคุณจะใช้ ucfirst กับ:

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

สามารถปรับให้เหมาะสมเพิ่มเติมได้โดยใช้ foreach แทน array_map (ดู ที่นี่):

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;
}

สิ่งนี้ทำให้ฉันคาดเดาว่า 2 regexes และ 1 ucwords เร็วกว่า 1 regex และหลาย ucfirsts

สคริปต์ทดสอบฉบับเต็ม:

<?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

คุณสามารถลองใช้ regex นี้:

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

UPDTAE ( เรียกใช้ที่นี่ )

แนวคิดข้างต้นคือการแสดงให้คุณเห็นว่าสิ่งนี้ควรทำงานอย่างไรใน regex:

ต่อไปนี้เป็นการใช้งาน PHP ของ regex ข้างต้น คุณสามารถเปรียบเทียบกับของคุณได้เนื่องจากจะทำให้การดำเนินการสามารถทำได้โดยการแทนที่ครั้งเดียว:

<?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;

?>

การสาธิต Regex

person Rizwan M.Tuman    schedule 08.04.2017
comment
OP กำลังถามเกี่ยวกับความเร็ว นอกจากนี้ \U ไม่ใช่ regex101.com เฉพาะเจาะจงใช่หรือไม่ - person vallentin; 08.04.2017
comment
การลงคะแนนเสียงและการตั้งค่าสถานะเนื่องจากไม่ตอบคำถาม ภาษา regex ที่ใช้ใน PHP ไม่ถูกต้อง และไม่ได้ทดสอบใน PHP อย่างชัดเจน - person Adam Cameron; 08.04.2017
comment
@AdamCameron ฉันได้อัปเดตคำตอบแล้ว .. คุณไม่คิดว่าจะเร็วเกินไปที่จะตั้งค่าสถานะคำตอบซึ่งไม่ได้ให้แนวคิดทั่วไปในการแก้ปัญหาแก่คุณในการใช้งาน php หรือไม่ - person Rizwan M.Tuman; 08.04.2017
comment
คุณได้รับข้อผิดพลาด PHP Notice: Undefined offset: 1 มากมายรวมถึงผลลัพธ์ที่ถูกต้องด้วย คุณอาจลอง return (isset($matches[1]) ? strtoupper($matches[1]) : ''); - person Robin Mackenzie; 08.04.2017
comment
@RobinMackenzie คุณพูดถูก .. อัปเดตแล้ว ... ขอบคุณเพื่อน - person Rizwan M.Tuman; 08.04.2017
comment
@rizwan: ฉันตั้งค่าสถานะว่าไม่ใช่คำตอบ แต่อาจมีการแก้ไขบางอย่าง ดูเหมือนคุณจะเห็นด้วยจริงๆ ไม่แน่ใจว่าปัญหาคืออะไร การอัปเดตของคุณเป็นไปด้วยดีแล้ว และฉันจะลบคะแนนโหวตของฉันออก - person Adam Cameron; 08.04.2017
comment
@Adam Cameron ผ่อนคลายเพื่อน ฉันไม่ได้จริงจังขนาดนั้น :) .. แต่ฉันไม่เคยได้รับธงเลยไม่ค่อยชัดเจนเกี่ยวกับการติดธง;) - person Rizwan M.Tuman; 08.04.2017

ก่อนที่จะคิดที่จะปรับปรุงประสิทธิภาพของโค้ด คุณต้องสร้างโค้ดที่ใช้งานได้ก่อน จริงๆ แล้วคุณกำลังพยายามสร้างโค้ดที่จัดการสตริงที่เข้ารหัส utf8 (เนื่องจากคุณเพิ่มตัวแก้ไข u ให้กับรูปแบบของคุณ) แต่ด้วยสตริง: liberté égalité fraternité รหัสของคุณส่งคืน Liberté égalité Fraternité แทนที่จะเป็น Liberté Égalité Fraternité เพราะ ucwords (หรือ ucfirst) ไม่สามารถจัดการกับ อักขระหลายไบต์.

หลังจากลองวิธีการต่างๆ (ด้วย preg_split และ preg_replace_callback) ดูเหมือนว่า preg_match_all เวอร์ชันนี้จะเร็วที่สุด:

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]);
}

แน่นอนว่ามันช้ากว่าโค้ดเริ่มต้นของคุณ แต่เราไม่สามารถเปรียบเทียบโค้ดต่างๆ เหล่านี้ได้จริงๆ เนื่องจากโค้ดของคุณใช้งานไม่ได้

person Casimir et Hippolyte    schedule 09.04.2017