एक स्ट्रिंग में प्रत्येक शब्द का पहला अक्षर प्राप्त करें


81

मुझे दिए गए स्ट्रिंग के लिए प्रत्येक शब्द का पहला अक्षर कैसे मिलेगा?

$string = "Community College District";
$result = "CCD";

मुझे जावास्क्रिप्ट विधि मिली लेकिन यह निश्चित नहीं था कि इसे php में कैसे बदला जाए।


1
क्या आप जानना चाहते हैं कि आपके स्ट्रिंग के पहले अक्षर को कैसे प्राप्त किया जाए, जैसे कि आपका प्रश्न कैसे किया जाता है या आपके उदाहरण के अनुसार प्रत्येक शब्द का पहला अक्षर कैसे प्राप्त करें? यदि पूर्व: $ परिणाम = $ स्ट्रिंग [0]।
बेनामी

आप सकारात्मक हैं कि प्रत्येक शब्द एक ही स्थान से अलग है? What__about__this__sentence?याWhat about.This sentence?
माइक बी

खुलकर PHP में अपनी स्क्रिप्ट विकसित करें।
लायन

वे कौन से पात्र हैं जो विभाजक के रूप में योग्य होंगे? अंतरिक्ष, पानी का छींटा, अंडरस्कोर, आदि?
असली सपने

1
सफेद रिक्त स्थान पर स्ट्रिंग का विस्फोट करें फिर परिणाम सरणी के माध्यम से लूप करें और क्योंकि प्रत्येक एक स्ट्रिंग है जिसे आप पहले वर्ण प्राप्त करने के लिए $ स्ट्रिंग [0] का उपयोग कर सकते हैं।
slash197

जवाबों:


136

explode()रिक्त स्थान पर, तब आप []परिणाम को स्ट्रिंग के रूप में एक्सेस करने के लिए नोटेशन का उपयोग करते हैं:

$words = explode(" ", "Community College District");
$acronym = "";

foreach ($words as $w) {
  $acronym .= $w[0];
}

यदि आपके पास एक उम्मीद है कि कई रिक्त स्थान अलग-अलग शब्द हो सकते हैं, तो इसके बजाय स्विच करें preg_split()

$words = preg_split("/\s+/", "Community College District");

या यदि -,_उदाहरण के लिए व्हाट्सएप सीमांत शब्दों ( ) के अलावा अन्य वर्ण भी उपयोग करें preg_split():

// Delimit by multiple spaces, hyphen, underscore, comma
$words = preg_split("/[\s,_-]+/", "Community College District");

14
बात करने के लिए:preg_match_all("/[A-Z]/", ucwords(strtolower($string)), $matches);
dmmd

46

इसे पूरा करने का सबसे अच्छा तरीका नियमित अभिव्यक्ति है।

एक तार्किक तरीके से आप जो चाहते हैं उसे तोड़ देते हैं: आप चाहते हैं कि स्ट्रिंग से हर वर्ण एक शब्द की शुरुआत में हो। उन पात्रों की पहचान करने का सबसे अच्छा तरीका उन पात्रों की तलाश करना है जो सफेद स्थान से पहले हैं।

इसलिए हम उस अंतरिक्ष चरित्र के लिए एक खोजबीन शुरू करते हैं , उसके बाद किसी भी पात्र को:

/(?<=\s)./

यह किसी भी वर्ण को एक स्थान से पहले मिलेगा। लेकिन - स्ट्रिंग में पहला चरित्र स्ट्रिंग में एक चरित्र है जिसे आप निकालना चाहते हैं। और क्योंकि यह स्ट्रिंग में पहला चरित्र है, इसलिए इसे एक स्थान से पहले नहीं रखा जा सकता है। इसलिए हम किसी स्थान से पहले या स्ट्रिंग के पहले चरित्र से मेल खाना चाहते हैं , इसलिए हम एक प्रारंभिक विषय जोड़ते हैं :

/(?<=\s|^)./

अब हम करीब आ रहे हैं। लेकिन क्या होगा यदि स्ट्रिंग में कई रिक्त स्थान के ब्लॉक हैं? क्या होगा यदि इसमें विराम चिह्नों के बाद एक स्थान शामिल हो? हम शायद उनमें से किसी से भी मेल नहीं खाना चाहते हैं, मोटे तौर पर हम शायद सिर्फ अक्षरों से मेल खाना चाहते हैं। हम चरित्र वर्ग के साथ ऐसा कर सकते हैं [a-zA-Z]। और हम i संशोधक का उपयोग करके अभिव्यक्ति के मामले को असंवेदनशील बना सकते हैं ।

तो हम साथ समाप्त करते हैं:

/(?<=\s|^)[a-z]/i

लेकिन हम वास्तव में PHP में इसका उपयोग कैसे करते हैं? वैसे हम स्ट्रिंग के भीतर नियमित अभिव्यक्ति की सभी घटनाओं का मिलान करना चाहते हैं, इसलिए हम इसका उपयोग करते हैं (आपने अनुमान लगाया) preg_match_all():

$string = "Progress in Veterinary Science";

$expr = '/(?<=\s|^)[a-z]/i';
preg_match_all($expr, $string, $matches);

अब हमारे पास सभी पात्र हैं जिन्हें हम निकालना चाहते थे। आपके द्वारा दिखाए जाने वाले परिणाम स्ट्रिंग का निर्माण करने के लिए, हमें उन्हें फिर से एक साथ मिलाने की आवश्यकता है :

$result = implode('', $matches[0]);

... और हमें यह सुनिश्चित करने की आवश्यकता है कि वे सभी ऊपरी मामले हैं :

$result = strtoupper($result);

और यह वास्तव में यह सब वहाँ है।

इसे काम करते हुए देखें


1
यदि आप चाहते थे कि आप (?<=\b)इसके स्थान पर भी उपयोग कर सकते हैं (?<=\s|^), तो यह आपको हाइफ़न, पूर्ण विराम आदि द्वारा अलग-अलग शब्दों के प्रारंभिक अक्षरों पर कब्जा करने की अनुमति देगा, (मूल रूप से "गैर शब्द" वर्ण, जो मेल नहीं खाते \ w या \ W), लेकिन उन चीजों को कैप्चर करना भी बंद कर सकते हैं जो आप नहीं चाहते हैं
लेह

आपके समाधान से बहुत मदद मिली! धन्यवाद !
यत्रकरन

1
इसका उत्तर निश्चित रूप से होना चाहिए। अत्यंत विस्तृत और पूरी तरह से काम करता है, धन्यवाद!
स्टीव ब्यूमन

इससे मुझे मदद मिली, लेकिन $ स्ट्रिंग के मामले के बारे में क्या = "वेटरनरी साइंस (ब्राउनर काउंटी) में प्रगति"; 'B' गिरा है। किसी भी विचार
केन

17

मान लें कि शब्द रिक्त स्थान से विभाजित हैं, यह एक उपयुक्त समाधान है:

$string = "Progress in Veterinary Science";

function initials($str) {
    $ret = '';
    foreach (explode(' ', $str) as $word)
        $ret .= strtoupper($word[0]);
    return $ret;
}

echo initials($string); // would output "PIVS"

मुझे लगता है कि $ शब्द [0] पदार्थ की तुलना में तेज है ($ शब्द, 0,1) इसलिए आप पदार्थ का उपयोग क्यों करते हैं ($ शब्द, 0,1)?
सर l33tname

1
मैं सिर्फ सरणियों के रूप में स्ट्रिंग्स पर बहुत भरोसा नहीं कर रहा हूं। मैं कुछ त्रुटियों को अतीत में पॉप कर चुका हूँ
कैसर्फ

संपादित करें: TL; DR: सिर्फ पुरानी आदतें
कैसरफ

2
@LeonardChallis मुझे नहीं पता, अगर चेन असरफ इस तरह की त्रुटियों से पीड़ित थे, लेकिन यदि आप मल्टी-बाइट स्ट्रिंग्स पर काम कर रहे हैं, तो substr($word,0,1)(या वास्तव में - mb_substr($word, 0, 1, 'utf-8')) का उपयोग करना पूर्ण रूप से आवश्यक है। सरल का उपयोग करना $word[0]मल्टी-बाइट चरित्र को आधा काट देगा और आपको वास्तविक प्रारंभिक अक्षर - वास्तविक पत्र के बजाय कुछ अजीब प्रतीक देगा। यदि आप इस स्थिति को एक त्रुटि के रूप में संदर्भित करते हैं, तो आपके पास अपना जवाब है! :]
trejder

शब्दों को नज़रअंदाज़ करने का कोई तरीका या तरीका (जैसे, के, की, एक ...) और "PIV" के बजाय "PVS" के रूप में आउटपुट प्राप्त करें
फेनिल शाह

9

बहुत सारे explodeजवाब हैं। मुझे लगता है कि strtokफ़ंक्शन का उपयोग करना बहुत अधिक सुरुचिपूर्ण और स्मृति-कुशल समाधान है:

function createAcronym($string) {
    $output = null;
    $token  = strtok($string, ' ');
    while ($token !== false) {
        $output .= $token[0];
        $token = strtok(' ');
    }
    return $output;
}
$string = 'Progress in Veterinary Science';
echo createAcronym($string, false);

यहां एक अधिक मजबूत और उपयोगी फ़ंक्शन है, जो UTF8 वर्णों का समर्थन करता है और केवल कैपिटल शब्दों का उपयोग करने का विकल्प है:

function createAcronym($string, $onlyCapitals = false) {
    $output = null;
    $token  = strtok($string, ' ');
    while ($token !== false) {
        $character = mb_substr($token, 0, 1);
        if ($onlyCapitals and mb_strtoupper($character) !== $character) {
            $token = strtok(' ');
            continue;
        }
        $output .= $character;
        $token = strtok(' ');
    }
    return $output;
}
$string = 'Leiðari í Kliniskum Útbúgvingum';
echo createAcronym($string);

मैं असहमत हूं, विस्फोट के तरीकों की तुलना में आपका कोड बड़े पैमाने पर है।
डेल

3
@ डेल वेल, जो हमें मेरे कोड के बारे में आपके बारे में अधिक बताता है - सौंदर्यशास्त्र कोड का आकलन करने का एक खराब तरीका है। explodeइस समस्या को हल करने के लिए उपयोग करना एक भोला समाधान कहा जाएगा । यह बबल-सॉर्ट एल्गोरिथ्म का उपयोग करने जैसा है क्योंकि इसे लागू करना आसान है।
सेवर्री एम। ऑलसेन

@MAssiveAmountsOfCode मैं असहमत हूं कि कोड की 13 लाइनों में कुछ ऐसा क्यों करें, जो 1 में प्राप्त किया जा सकता है foreach(explode(' ', $string) as $word) echo $word[0];? एक नज़र में समझने में आसान, और एक समय नुक़सान नहीं।
डेल

इसके अलावा, एक अंतरिक्ष द्वारा, एक अंतरिक्ष द्वारा अलग किए गए शब्दों की एक स्ट्रिंग को विभाजित करने के बारे में भोले क्या है? मुझे लगता है कि आपकी टिप्पणी हमें बताती है कि आप एक आकर्षक कोडर हैं जो कोड समीक्षाओं के लिए खुला नहीं है।
डेल

3
@ डेल का मतलब मैं आपका अपमान नहीं कर सकता था, न ही अपमानजनक प्रकट कर सकता था। यह भोला है क्योंकि एक स्ट्रिंग को विस्फोट करना एक सरणी बनाता है, जहां किसी की आवश्यकता नहीं है। स्ट्रिंग को टोकन करना अधिक सुरुचिपूर्ण है क्योंकि आप मूल स्ट्रिंग के माध्यम से कदम रख रहे हैं, जिसके लिए कम मेमोरी की आवश्यकता होती है। मैं यह नहीं कह रहा हूं कि उपयोग explodeकरना गलत है (यह काम पूरा हो जाता है), लेकिन यह समस्या को हल करने का एक और अधिक सुंदर कहना है। और मैं "सुरुचिपूर्ण" शब्द का सौंदर्यपूर्ण तरीके से उपयोग नहीं कर रहा हूं, मैं इसे तकनीकी तरीके से उपयोग कर रहा हूं।
सेवरी एम। ऑलसेन

8

माइकल बर्कोव्स्की (और अन्य) का उत्तर, एक पंक्ति में सरल और बहु-बाइट वर्णों पर सही तरीके से काम करना (यानी संक्षिप्त नाम बनाना / गैर-लैटिन स्ट्रिंग से आरंभ करना):

foreach(explode(' ', $words) as $word) $acronym .= mb_substr($word, 0, 1, 'utf-8');

का उपयोग करते हुए mb_substr($word, 0, 1, 'utf-8'), के बजाय $word[0], जरूरी होने के लिए यदि आप जब UTF-8 एन्कोडेड तार का उपयोग कर गैर-लैटिन, मल्टी-बाइट तार और वर्ण, यानी पर काम कर रहे है।



5

ऐशे ही

preg_match_all('#(?<=\s|\b)\pL#u', $String, $Result);
echo '<pre>' . print_r($Result, 1) . '</pre>';

अच्छा लगा। मुझे अपने कोड में पहले अक्षर से समस्या हुई। पहला अक्षर किस वर्ण को दर्शाता है? <=?
नारेक

1
के लिए +1 \pL। आप हालांकि थोड़ा स्पष्टीकरण जोड़ सकते हैं? मैं सिर्फ उसे एक देने से ;-) बल्कि मछली के लिए एक आदमी को पढ़ाने के लिए पसंद करते हैं
DaveRandom


@DaveRandom यहां इस श्लोक के बारे में डेटेल
विंस्टन

@Winston मैं जानता हूँ (हालांकि मैं अपने जवाब में KISS दृष्टिकोण लिया), मैं ओ पी के लिए और अधिक मतलब ;-) लेकिन धन्यवाद वैसे भी :-)
DaveRandom

5

जैसा कि दूसरों द्वारा समझाया गया है, शास्त्रीय तरीका आपकी प्रारंभिक स्ट्रिंग के प्रत्येक शब्द पर ध्यान देने में शामिल है, शब्द को इसके पहले अक्षर को कम करता है, और उन पहले अक्षरों को एक साथ जोड़ देता है।

यहाँ एक सहायक विधि है जो विभिन्न चरणों को जोड़ती है।

/**
 * @return string
 */
function getInitials($string = null) {
    return array_reduce(
        explode(' ', $string),
        function ($initials, $word) {
            return sprintf('%s%s', $initials, substr($word, 0, 1));
        },
        ''
    );
}

NB: यदि दी गई स्ट्रिंग खाली है तो यह एक रिक्त स्ट्रिंग लौटाएगा।

getInitials('Community College District')

स्ट्रिंग 'सीसीडी' (लंबाई = 3)

getInitials()

स्ट्रिंग '' (लंबाई = 0)

getInitials('Lorem ipsum dolor sic amet')

स्ट्रिंग 'लिडा' (लंबाई = 5)

बेशक, आप कॉलबैक फ़ंक्शन के लिए फ़िल्टर जोड़ सकते हैं array_reduce(), जैसे कि strtoupper()यदि आप उदाहरण के लिए केवल अपरकेस शुरुआती पसंद करते हैं।


3
$str = 'I am a String!';
echo implode('', array_map(function($v) { return $v[0]; }, explode(' ', $str)));

// would output IaaS

3

कुछ मैंने पकाया है।

/**
 * Return the first letter of each word in uppercase - if it's too long.
 *
 * @param string $str
 * @param int $max
 * @param string $acronym
 * @return string
 */
function str_acronym($str, $max = 12, $acronym = '')
{
    if (strlen($str) <= $max) return $str;

    $words = explode(' ', $str);

    foreach ($words as $word)
    {
        $acronym .= strtoupper(substr($word, 0, 1));
    }

    return $acronym;
}

2
function acronym( $string = '' ) {
    $words = explode(' ', $string);
    if ( ! $words ) {
        return false;
    }
    $result = '';
    foreach ( $words as $word ) $result .= $word[0];
    return strtoupper( $result );
}

2

इसके लिए str_word_count फ़ंक्शन का उपयोग क्यों नहीं किया जा रहा है ?

  1. एक सरणी में एक पंक्ति के रूप में प्रत्येक शब्द मिलता है
  2. कम करना पहले अक्षर है कि सरणी

    $ संक्षिप्त = array_reduce (str_word_count ("कम्युनिटी कॉलेज डिस्ट्रिक्ट", 1), फ़ंक्शन ($ res, $ w) {$ $ res। $ w [0];});


1

मुझे लगता है कि आपको विस्फोट करना होगा और उन्हें फिर से शामिल करना होगा ....।

<?php
$string  = "Progress in Veterinary Science";
$pieces = explode(" ", $string);
$str="";
foreach($pieces as $piece)
{
    $str.=$piece[0];
}    
echo $str; /// it will result into  "PiVS"
?>

1

Prateeks नींव का उपयोग, यहाँ स्पष्टीकरण के साथ एक सरल उदाहरण है

//  initialize variables
$string = 'Capitalize Each First Word In A String';
$myCapitalizedString = '';

//  here's the code
$strs=explode(" ",$string);    
foreach($strs as $str) {
  $myCapitalizedString .= $str[0]; 
}

//  output
echo $myCapitalizedString;  // prints 'CEFWIAS'

यह इस साइट पर पोस्ट किया गया मेरा पहला समाधान है। HTH!
रोब स्टॉकी

1

यदि इनपुट स्ट्रिंग में दो अक्षरों के बीच अधिक स्थान हैं तो यह प्रयास करें।

function first_letter($str)
{
    $arr2 = array_filter(array_map('trim',explode(' ', $str)));
    $result='';
    foreach($arr2 as $v)
    {
        $result.=$v[0];
    }
    return $result;
}

$str="    Let's   try   with    more   spaces       for  fun .   ";

echo first_letter($str);

डेमो 1

एक ही कोड का विकल्प

function first_letter($str)
{
    return implode('', array_map(function($v) { return $v[0]; },array_filter(array_map('trim',explode(' ', $str)))));;
}

$str="    Let's   try   with    more   spaces       for  fun .   ";

echo first_letter($str);

डेमो 2


1

यहां एक फ़ंक्शन है जो आपको एक नाम के शुरुआती अक्षर से मिलता है और यदि प्रारंभिक केवल 1 अक्षर हैं तो यह पहले नाम के पहले 2 अक्षर लौटाता है।

function getNameInitials($name) {

    preg_match_all('#(?<=\s|\b)\pL#u', $name, $res);
    $initials = implode('', $res[0]);

    if (strlen($initials) < 2) {
        $initials = strtoupper(substr($name, 0, 2));
    }

    return strtoupper($initials);
}


0

कुछ इस तरह करना चाहिए ट्रिक:

$string = 'Some words in a string';
$words = explode(' ', $string); // array of word
foreach($words as $word){
    echo $word[0]; // first letter
}

0

इस मामले के लिए कि आप इसे बड़े स्ट्रिंग्स पर कर रहे हैं (या सीधे फ़ाइल से भी) explode()ऐसा करने का सबसे अच्छा तरीका नहीं है। कल्पना करें कि यदि स्मृति में स्ट्रिंग 2MB बड़े को विभाजित करना है तो कितनी मेमोरी बर्बाद हो जाएगी।

थोड़े अधिक कोडिंग और (मानकर PHP >= 5.0) आप आसानी से PHP के Iteratorवर्ग को लागू कर सकते हैं जो वास्तव में ऐसा करेगा। यह अजगर और लंबी कहानी संक्षेप में जनरेटर के करीब होगा, यहाँ कोड है:

/**
 * Class for CONTINOUS reading of words from string.
*/
class WordsIterator implements Iterator {
    private $pos = 0;
    private $str = '';
    private $index = 0;
    private $current = null;

    // Regexp explained:
    // ([^\\w]*?) - Eat everything non-word before actual word characters
    //              Mostly used only if string beings with non-word char
    // ([\\w]+)   - Word
    // ([^\\w]+?|$) - Trailing thrash
    private $re = '~([^\\w]*?)([\\w]+)([^\\w]+?|$)~imsS';

    // Primary initialize string
    public function __construct($str) {
        $this->str = $str;
    }

    // Restart indexing
    function rewind() {
        $this->pos = 0;
        $this->index = 0;
        $this->current = null;
    }

    // Fetches current word
    function current() {
        return $this->current;
    }

    // Return id of word you are currently at (you can use offset too)
    function key() {
        return $this->index;
    }

    // Here's where the magic is done
    function next() {
        if( $this->pos < 0){
            return;
        }

        $match = array();
        ++$this->index;

        // If we can't find any another piece that matches... Set pos to -1
        // and stop function
        if( !preg_match( $this->re, $this->str, $match, 0, $this->pos)){
            $this->current = null;
            $this->pos = -1;
            return;
        }

        // Skip what we have read now
        $this->current = $match[2];
        $this->pos += strlen( $match[1]) + strlen( $match[2]) + strlen($match[3]);

        // We're trying to iterate past string
        if( $this->pos >= strlen($this->str)){
            $this->pos = -1;
        }

    }

    // Okay, we're done? :)
    function valid() {
        return ($this->pos > -1);
    }
}

और अगर आप इसे थोड़ा और अधिक चुनौतीपूर्ण स्ट्रिंग पर उपयोग करेंगे:

$a = new WordsIterator("Progress in Veterinary Science. And, make it !more! interesting!\nWith new line.");
foreach( $a as $i){
    echo $i;
    echo "\n";
}

क्या आपको अपेक्षित परिणाम मिलेगा:

Progress
in
Veterinary
Science
And
make
it
more
interesting
With
new
line

तो आप आसानी से $i[0]पहले अक्षर को लाने के लिए उपयोग कर सकते हैं। आप शायद यह देख सकते हैं कि यह पूरे स्ट्रिंग को मेमोरी में विभाजित करने से अधिक प्रभावी उपाय है (हमेशा जितना संभव हो केवल कम मेमोरी का उपयोग करें)। फ़ाइलों के निरंतर पढ़ने आदि के साथ काम करने के लिए आप इस समाधान को आसानी से संशोधित कर सकते हैं।


0
<?php $arr = explode(" ",$String);

foreach($arr as $s)
{
   echo substr($s,0,1);
}

?>

सबसे पहले मैं रिक्त स्थान द्वारा स्ट्रिंग का विस्फोट करता हूं, फिर मैं पहले चार का स्थान लेता हूं।

http://php.net/substr

http://php.net/explode


0

इसे इस्तेमाल करे

function initials($string) {
        if(!(empty($string))) {
            if(strpos($string, " ")) {
                $string = explode(" ", $string);
                $count = count($string);
                $new_string = '';
                for($i = 0; $i < $count; $i++) {
                $first_letter = substr(ucwords($string[$i]), 0, 1);
                $new_string .= $first_letter;
            }
            return $new_string;
            } else {
                $first_letter = substr(ucwords($string), 0, 1);
                $string = $first_letter;
                return $string;
            }
        } else {
            return "empty string!";
        }
    }
    echo initials('Thomas Edison');

0

मुझे स्ट्रिंग एक्सट्रैक्शन के किसी भी अन्य तरीके पर रेग एक्सप्रेशन पसंद है, लेकिन अगर आप रेग एक्स से अपरिचित हैं तो सुनें explode()पीएचपी का उपयोग करने वाला एक तरीका है:

$string = "David Beckham";
$string_split = explode(" ", $string);
$inititals = $string_split[0][0] . $string_split[1][0];
echo $inititals;

जाहिर है कि उपरोक्त कोड केवल दो शब्दों वाले नाम पर काम करेगा।


0

यह उत्तर https://stackoverflow.com/a/33080232/1046909 पर मल्टीबाइट स्ट्रिंग्स समर्थन के साथ है:

if (!function_exists('str_acronym')) {
    function str_acronym(string $str, int $min = -1, string $prefix = null): string
    {
        if (mb_strlen($str) <= $min) {
            return $str;
        };

        $words = explode(' ', $str);

        $acronym = strval($prefix);

        foreach ($words as $word) {
            if ($word = trim($word)) {
                $acronym .= mb_strtoupper(mb_substr($word, 0, 1));
            }
        }

        return $acronym;
    }
}

0

आप उस फ़ंक्शन का उपयोग @Michael Berkowski के स्वीकृत उत्तर के आधार पर कर सकते हैं

function buildAcronym($string, $length = 1) {
    $words = explode(" ", $string);
    $acronym = "";
    $length = (self::is_empty($string) || $length <= 0 ? 1 : $length);

    foreach ($words as $i => $w) {
        $i += 1;
        if($i <= $length) {
            $acronym .= $w[0];
        }
    }

    return $acronym;
}

$ लंबाई पैरामीटर यह निर्धारित करता है कि आप कितने चार्ट प्रदर्शित करना चाहते हैं

उपयोग:

$acronym = buildAcronym("Hello World", 2);
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.