Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 国产999在线,亚洲精品第一第二区,亚洲国产成人精品久久

          整合營(yíng)銷服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢熱線:

          誰說 Vim 不好用?送你一個(gè)五彩斑斕的編輯器

          誰說 Vim 不好用?送你一個(gè)五彩斑斕的編輯器

          創(chuàng):良許 源:良許Linux

          相信大家在使用各種各樣強(qiáng)大的 IDE 寫代碼時(shí)都會(huì)注意到,代碼中各種類型的關(guān)鍵字會(huì)用獨(dú)特的顏色標(biāo)記出來,然后形成一套語法高亮規(guī)則。這樣不僅美觀,而且方便代碼的閱讀。

          而在上古神器 Vim 中,我們通常看到的是黑底白字,沒有任何其它顏色。難道我們的 Vim 就這么枯燥乏味嗎?當(dāng)然不是,Vim 隱藏了非常多非常實(shí)用的功能與技巧,不是老司機(jī)根本就不知道。下面來詳細(xì)介紹。

          在開始之前,我們要先檢查一下系統(tǒng)中是否安裝了 Vim 編輯器。一般來說 Ubuntu 系統(tǒng)是默認(rèn)自帶的,沒有的小伙伴請(qǐng)先自行安裝。

          $ sudo apt-get install vim
          

          然后我們運(yùn)行以下命令來查看安裝好的 Vim 編輯器的版本。

          $ vim -version
          

          接下來,我們就可以為 Vim 編輯器添加配色方案。為了能看出效果,我們準(zhǔn)備了一個(gè)名為 login.sh 的 bash 文件,它的內(nèi)容如下:

          login.sh

          !/bin/bash
          echo "Type your username"
          read username
          echo "Type your password"
          read password
          if [[ ( $username=="admin" && $password=="secret" ) ]]; then
          echo "Authorized user"
          else
          echo "Unauthorized user"
          fi
          

          運(yùn)行以下命令使用 Vim 編輯器打開該文件:

          $ vim login.sh
          

          啟用/禁用語法高亮顯示

          有些發(fā)行版的 Vim 編輯器已經(jīng)幫你默認(rèn)啟用語法高亮,而有些發(fā)行版卻沒有。如果沒有默認(rèn)打開語法高亮,那么我們就需要自行打開。

          打開的方法其實(shí)也很簡(jiǎn)單。在 Vim 編輯器中打開 login.sh 文件后,按 ESC 鍵并鍵入 :syntax on ,語法高亮顯示就打開了,非常方便。

          同樣,關(guān)閉語法高亮也很簡(jiǎn)單,只需按 ESC 鍵并鍵入 :syntax off即可。

          永久啟用或禁用語法高亮顯示

          剛剛那種打開語法高亮的方法只是暫時(shí)性的,一旦當(dāng)前文件關(guān)閉,下次打開文件就需要重新打開語法高亮。

          如果想要永久啟用或禁用語法高亮顯示,就需要修改 .vimrc 文件。首先,我們使用 Vim 打開 .vimrc 文件。

          $ sudo vim ~ / .vimrc
          

          然后,在打開的文件中添加一句 syntax on 命令,代表永久啟用語法高亮顯示。最后再輸入 :wq 保存并關(guān)閉文件。

          如果想要永久禁用語法高亮,則添加 syntax off 命令即可。

          改變配色方案

          在默認(rèn)情況下,打開語法高亮后 Vim 為你應(yīng)用了一套默認(rèn)的配色方案,而實(shí)際上 Vim 還有很多套配色方案可供我們使用,我們可以自行去修改。

          安裝 Vim 后,顏色方案文件位于 /usr/share/vim/vim*/colors/中。我們可以運(yùn)行以下命令來查看 Vim 配色方案文件的列表。

          $ ls -l /usr/share/vim/vim*/colors/
          

          可以看出 Vim 為我們提供了非常多的配色方案,我們可以根據(jù)自己的需要自由選擇。假設(shè),我們有一個(gè) hello.html 的 html 文件,我們想要將它的的配色方案改為 morning 類型。

          hello.html

          <html>
          <head>
          <title> Welcome </title>
          </head>
          <body>
          <p> Hello Everybody, Welcome to our site </p>
          </body>
          </html>
          

          我們首先運(yùn)行以下命令在 Vim 中打開這個(gè) html 文件。

          $ vim hello.html
          

          按 ESC 并鍵入 :colorscheme morning ,這樣我們就更改了文件當(dāng)前的顏色方案。

          應(yīng)用新顏色方案后,效果如下圖顯示。但這種更改依然是暫時(shí)的,關(guān)閉編輯器后配色方案將被重置。

          如果想要永久設(shè)置特定的配色方案及背景,那么我們需要再次打開 .vimrc 文件并添加以下命令文本。下面配置的是夜晚配色方案,會(huì)將背景顏色設(shè)置為深色。

          color evening
          set background=dark
          

          之后再重新打開剛才的文件,效果就會(huì)變成如下圖所示。

          根據(jù)語言選擇配色方案

          Vim 編輯器支持多種語言的語法,例如 PHP,Perl,Python,awk 等。根據(jù)不同語言,它會(huì)應(yīng)用一套相應(yīng)語言的配色方案。

          比如現(xiàn)在有一個(gè)名為 average.py 的 Python 文件,我們用 Vim 編輯器打開它。通過 Vim 編輯器的默認(rèn)配色方案,文件的顯示效果如下圖所示。

          這是一個(gè) Python 文件,但如果我們想以 Perl 的語法風(fēng)格來顯示,要怎么操作?我們只需輸入 :set syntax=perl ,按 Enter 鍵就可以了,效果如下圖所示。

          :set syntax=perl
          

          自定義配色方案

          前面所介紹的那些配色方案,其實(shí)都是系統(tǒng)自帶的,我們只是選擇一下而已。如果我們想要個(gè)性化一點(diǎn),自定義一套配色方案,要怎么操作?強(qiáng)大的 Vim 也是支持你這個(gè)需求的!

          一般而言,語法需要高亮的無非就是 變量、注釋、常量 之類的,Vim 編輯器支持的語法高亮組有如下 9 個(gè):

          Group Name

          Description

          Identifier

          變量

          Statement

          關(guān)鍵字,例如 if,else,do,while等

          Comment

          注釋

          Type

          數(shù)據(jù)類型,如 int、double、string等

          PreProc

          預(yù)處理器語句,比如 #include

          Constant

          常量,如數(shù)字、帶引號(hào)的字符串、真/假等

          Special

          特殊符號(hào),如“”、“”等

          Underlined

          帶下劃線的文本

          Error

          錯(cuò)誤

          下面舉個(gè)例子。在 Vim 編輯器中打開名為 leap.py 的 Python 腳本文件。默認(rèn)情況下,該文件的語法高亮顯示如下圖所示。

          如果我們想把關(guān)鍵字變?yōu)榧t色,該怎么操作?只需輸入 :hi Statement ctermfg=red 即可。這時(shí),if 和 else 的顏色將變?yōu)榧t色。

          :hi Statement ctermfg=red
          

          當(dāng)然,對(duì)于注釋、常量、數(shù)據(jù)類型,我們也可以使用同樣的方法來改變顏色。這樣一來,你就會(huì)形成一套自己的配色方案,感覺是不是很酸爽?

          最后,我自己是一名從事了多年開發(fā)的Python老程序員,辭職目前在做自己的Python私人定制課程,今年年初我花了一個(gè)月整理了一份最適合2019年學(xué)習(xí)的Python學(xué)習(xí)干貨,可以送給每一位喜歡Python的小伙伴,想要獲取的可以關(guān)注我的頭條號(hào)并在后臺(tái)私信我:01,即可免費(fèi)獲取。

          、abbr

          abbr 全稱是 abbreviations,意思是縮寫。應(yīng)用場(chǎng)景也很簡(jiǎn)單,為一些文章中的縮寫增加注釋。

          以前在文章中對(duì)于縮寫的解釋經(jīng)常會(huì)這么做:

          DAU(Daily Active User),日活躍用戶數(shù) ......
          

          那我們用 abbr 標(biāo)簽?zāi)兀?/span>

          <abbr title="Daily Active User">
              DAU
          </abbr>
          <span>,日活躍用戶數(shù) ......</span>
          

          展示的效果如下:

          這個(gè)標(biāo)簽就可以把全稱隱藏掉,弱化信息量,讓真正不知道該縮寫的用戶主動(dòng)去獲取縮寫的具體意思,這個(gè)在 markdown 里經(jīng)常會(huì)出現(xiàn)。

          二、mark

          <mark/> 在 markdown 中也是很常用的,用于將包裹的文本高亮展示。

          <mark>高亮文本</mark>
          

          效果如下:

          如果全文統(tǒng)一高亮樣式,可以專門對(duì) mark 標(biāo)簽進(jìn)行樣式重置,這樣就不用對(duì)你用的每個(gè) div 加一個(gè) highlight 的類名了,又不語義化,又徒增文檔大小。

          三、sup、sub

          <sup/><sub/>分別表示上標(biāo)和下標(biāo),在 markdown 中出現(xiàn)得也很頻繁,比如數(shù)學(xué)公式和引用。

          <div>3<sup>[2]</sup></div>
          <div>4<sub>2</sub></div>
          

          效果如下:

          上標(biāo)和下標(biāo)的樣式原理也比較簡(jiǎn)單,主要就是利用了 vertical-aligntopsub 屬性值,然后將字號(hào)縮小,不過有現(xiàn)成的標(biāo)簽,干嘛不用呢?

          四、figure

          figure 是用于包裹其它標(biāo)簽的內(nèi)容的,然后再利用另一個(gè)標(biāo)簽 figcaption ,可以對(duì)包裹的內(nèi)容進(jìn)行一個(gè)文本描述,例如:

          <figure>
              <img src="/media/cc0-images/elephant-660-480.jpg"
                   alt="大象">
              <figcaption>這是一張大象的照片</figcaption>
          </figure>
          

          效果如下:

          那要是圖片掛了呢?

          再友好點(diǎn)處理,我們把 img 標(biāo)簽的 alt 屬性去掉。

          漂亮,終于把我一直厭煩的圖裂 icon 給干掉了,樣式還巨好看。

          當(dāng)然不止能包裹 img 標(biāo)簽,其它任何都是可以的。

          嘿嘿,給大家在本文來個(gè)實(shí)戰(zhàn),下面這個(gè)可以點(diǎn)擊,樣式也是利用了 figure 這個(gè)標(biāo)簽。

          我是figure標(biāo)簽產(chǎn)生的

          五、progress

          說到 <progress/> 這個(gè)標(biāo)簽就很有意思了,去年有段時(shí)間我做的業(yè)務(wù)里涉及到了進(jìn)度條,當(dāng)時(shí)是前同事做的,然后有一些性能問題,我就在研究如何優(yōu)化,減少進(jìn)度條改變帶來的性能問題。

          雖然最后問題是解決了,但是也有幸收到了張?chǎng)涡翊罄械脑u(píng)論,他告訴我 progress 這個(gè)標(biāo)簽就足夠了,既有語義化,又有進(jìn)度條的功能,性能還好,兼容性也很不錯(cuò)。后來經(jīng)過一番嘗試,還真是,當(dāng)時(shí)是我孤陋寡聞了,也安利給大家。

          <!-- 進(jìn)度條最大值為100,當(dāng)前進(jìn)度為60,即60% -->
          <progress max="100" value="60"/>
          

          瀏覽器自帶的樣式就已經(jīng)很好看了,效果如下:

          業(yè)務(wù)中我們也就可以通過控制 value 屬性,來改變進(jìn)度條的進(jìn)度了。

          六、area

          area 這個(gè)標(biāo)簽也非常有意思,它的作用是為圖片提供點(diǎn)擊熱區(qū),可以自己規(guī)定一張圖的哪些區(qū)域可點(diǎn)擊,且點(diǎn)擊后跳轉(zhuǎn)的鏈接,也可以設(shè)置成點(diǎn)擊下載文件,我們來舉個(gè)例子:

            <img src="example.png" width="100" height="100" alt="" usemap="#map">
          
            <map name="map">
              <area shape="rect" coords="0,0,100,50" alt="baidu" href="https://www.baidu.com">
              <area shape="rect" coords="0,50,100,100" alt="sougou" href="https://www.sogou.com/">
            </map>
          

          area 一般要搭配 map 標(biāo)簽一起使用,每個(gè) area 標(biāo)簽表示一個(gè)熱區(qū),例如上面代碼中,我們定義了兩個(gè)熱區(qū),熱區(qū)形狀都為rect(矩形),他們的熱區(qū)分別是:

          • 坐標(biāo) (0,0) 到坐標(biāo) (100,50) 的一個(gè)矩形
          • 坐標(biāo) (0,50) 到坐標(biāo) (100,100) 的一個(gè)矩形

          我們都知道,默認(rèn)的坐標(biāo)軸是這樣的:

          因此,我們劃分的兩個(gè)熱區(qū)就是:

          最后再來看一下我們的實(shí)際效果:

          i

          七、details

          details 字面意思是 "詳情",在 markdown 里也經(jīng)常用,用該標(biāo)簽包裹了的內(nèi)容默認(rèn)會(huì)被隱藏,只留下一個(gè)簡(jiǎn)述的文字,我們點(diǎn)擊以后才會(huì)展示詳細(xì)的內(nèi)容。

          <details>
            <p>我是一段被隱藏的內(nèi)容</p>
          </details>
          

          效果如下:

          這還沒有加任何一行的 js 代碼,我們點(diǎn)擊后,details 標(biāo)簽上會(huì)多一個(gè) open 的屬性,被隱藏的內(nèi)容就展示出來了。

          默認(rèn)情況下,簡(jiǎn)要文字為 "詳情",想要修改這個(gè)文字,要搭配 summary 標(biāo)簽來使用。

          <details>
            <summary>點(diǎn)擊查看更多</summary>
            <p>我是一段被隱藏的內(nèi)容</p>
          </details>
          

          就搞定了!

          八、dialog

          瀏覽器自帶彈窗方法 alertconfirmprompt,樣式固定且每個(gè)瀏覽器不同,同時(shí)還會(huì)阻塞頁面運(yùn)行,除了這個(gè)還提供了一個(gè) dialog 標(biāo)簽,它的使用方式有點(diǎn)類似于現(xiàn)在各大組件庫(kù)的 Modal 組件了,瀏覽器還為該標(biāo)簽提供了原生的 dom 方法:showModalclose,可以直接控制彈窗的展示和隱藏。

          <dialog id="dialog">
              <input type="text">
              <button id="close">ok</button>
          </dialog>
          <button id="openBtn">打開彈框</button>
          
          <script>
              const dialog=document.getElementById('dialog')
              const openBtn=document.getElementById('openBtn')
              const closeBtn=document.getElementById('close')
            
              openBtn.addEventListener('click', ()=> {
                  // 打開彈框
                  dialog.showModal()
              })
              closeBtn.addEventListener('click', ()=> {
                  // 隱藏彈框
                  dialog.close()
              })
          </script>
          

          效果如下:

          細(xì)心的你有沒有發(fā)現(xiàn),這原生的彈框還自帶背景蒙層,點(diǎn)擊是關(guān)閉不掉的,但起碼它不會(huì)阻塞頁面。

          然后我們?cè)趶棿罢故緯r(shí),也可以通過 esc 鍵來關(guān)閉彈窗。

          九、datalist

          datalist 是用于給輸入框提供可選值的一個(gè)列表標(biāo)簽,類似咱們常用的 Select 組件。

          我可以用其實(shí)現(xiàn)一個(gè) "輸入聯(lián)想" 的功能。

          <label> 輸入C開頭的英文單詞:</label>
          <input list="c_words"/>
          
          <datalist id="c_words">
            <option value="China">
            <option value="Click">
            <option value="Close">
            <option value="Const">
            <option value="Count">
          </datalist>
          

          來試一試:

          剛點(diǎn)擊時(shí)會(huì)把所有推薦的選項(xiàng)都列出來,然后根據(jù)后面輸入的內(nèi)容,會(huì)過濾掉不匹配的選項(xiàng),比如我輸入 cl,會(huì)過濾掉不是 cl 開頭的單詞,最后只剩下 ClickClose 了。

          最后我發(fā)現(xiàn),他這個(gè)下拉框有點(diǎn)好看啊?為啥這原生的 input 框默認(rèn)樣式那么丑,啥時(shí)候改改。

          十、fieldset

          fieldset 標(biāo)簽是用于分組管理 form 表單內(nèi)的元素的,若 fieldset 設(shè)置了 disabled 屬性,則被其包裹的所有表單元素都會(huì)被禁用置灰,且不會(huì)隨著表單一起提交上去,是的就成了擺設(shè)。

          什么意思呢?看個(gè)例子:

          <form action="/example">
            <fieldset disabled>
              <legend>被禁用區(qū)域</legend>
              <label>ID:</label>
              <input type="text" name="id" value="1">
              <label>郵箱:</label>
              <input type="text" name="email" value="1234567@163.com">
            </fieldset>
            <label>名字:</label>
            <input type="text" name="name">
            <button type="submit">提交</button>
          </form>
          

          這里我們把 ID郵箱 的表單包裹了起來,且設(shè)置了 disabled,只開放了一個(gè) name 的輸入控件,此時(shí)界面如下:

          可以看到除了 name 輸入框,其它的兩個(gè)輸入框都被禁用了,此時(shí)點(diǎn)提交會(huì)是什么樣子呢?

          嗯,只提交了 name 字段。

          十一、noscript

          這個(gè)標(biāo)簽是在瀏覽器不支持或禁用了 javascript 時(shí)才展示的,大多用于對(duì) js 強(qiáng)依賴的應(yīng)用,比如現(xiàn)在大部分的 SPA 頁面,一旦不支持 javascript,頁面基本上什么內(nèi)容都沒了,此時(shí)可以靠這個(gè)標(biāo)簽做友好提示。

          一般我們不需要特地去使用,大多都是在打包過程中自動(dòng)插入到 html 靜態(tài)文件里去的,例如:

          // init.js
          const root=document.getElementById('root')
          const button=document.createElement('button')
          button.innerText='點(diǎn)擊出彈窗'
          root.appendChild(button)
          
          <!-- index.html -->
          <script defer src="./init.js"></script>
          
          <noscript>
            不好意思,你的瀏覽器不支持或禁用了 JavaScript,請(qǐng)更換瀏覽器或啟用 JavaScript
          </noscript>
          
          <div id="root"></div>
          

          未禁用 javascript 時(shí),頁面是這樣的:

          禁用了 javascript 時(shí),是這樣的:

          文要點(diǎn)

          • 作為一項(xiàng)預(yù)覽特性,Java SE 13(2019 年 9 月)引入了文本塊,旨在減輕在 Java 中聲明和使用多行字符串字面量的痛苦。隨后,第二個(gè)預(yù)覽版本對(duì)它做了一些細(xì)微的改進(jìn),并計(jì)劃在 Java SE 15(2020 年 9 月)中成為 Java 語言的一個(gè)永久特性。
          • Java 程序中的字符串字面量不限于“yes”和“no”這樣的短字符串;它們經(jīng)常對(duì)應(yīng)于結(jié)構(gòu)化語言(如 HTML、SQL、XML、JSON,甚至 Java)中的整個(gè)“程序”。
          • 文本塊是可以包含多行文本的字符串字面量,使用三重引號(hào)(""")作為開始和結(jié)束分隔符。
          • 文本塊可以看作是嵌入在 Java 程序中的二維文本塊。
          • 如果能夠保留嵌入的那個(gè)程序的二維結(jié)構(gòu),又不必使用轉(zhuǎn)義字符和其他會(huì)造成干擾的語法,那就可以降低出錯(cuò)的可能,開發(fā)出可讀性更好的程序。

          在 QCon 紐約大會(huì)的演講“ Java 的未來”中,Java 語言架構(gòu)師 Brian Goetz 帶我們快速瀏覽了 Java 語言近期和未來的一些特性。在本文中,他深入探討了文本塊。

          作為一項(xiàng)預(yù)覽特性,Java SE 13(2019 年 9 月)引入了文本塊,旨在減輕在 Java 中聲明和使用多行字符串字面量的痛苦。

          隨后,第二個(gè)預(yù)覽版本對(duì)它做了一些細(xì)微的改進(jìn),并計(jì)劃在 Java SE 15(2020 年 9 月)中成為 Java 語言的一個(gè)永久特性。

          文本塊是可以包含多行文本的字符串字面量,如下所示:

          復(fù)制代碼

          String address="""                 25 Main Street                 Anytown, USA, 12345                 """;

          在下面這個(gè)簡(jiǎn)單的示例中,變量 address 是一個(gè)兩行的字符串,每行后面都有行終止符。如果沒有文本塊,我們必須這樣寫:

          復(fù)制代碼

          String address="25 Main Street\n" +                 "Anytown, USA, 12345\n";                 

          復(fù)制代碼

          String address="25 Main Street\nAnytown, USA, 12345\n";

          每個(gè) Java 開發(fā)人員都知道,這些寫法都非常麻煩。但是,更重要的是,它們更容易出錯(cuò)(很容易忘記\n 并且發(fā)現(xiàn)不了),也更難閱讀(因?yàn)檎Z言語法與字符串的內(nèi)容混在一起)。由于文本塊通常沒有轉(zhuǎn)義字符和其他語法干擾,讀者更容易看明白字符串的內(nèi)容。

          字符串字面量中最常見的轉(zhuǎn)義字符是換行符(\n),文本塊支持直接表示多行字符串而不需要換行符。除了換行符之外,另一個(gè)最常用的轉(zhuǎn)義字符是雙引號(hào)("),這個(gè)必須轉(zhuǎn)義,因?yàn)樗c字符串字面量分隔符沖突。文本塊不需要這樣,因?yàn)閱我?hào)與三引號(hào)文本塊分隔符并不沖突。

          為什么起了這樣一個(gè)名字?

          有人可能會(huì)說,這個(gè)特性應(yīng)該叫“多行字符串字面量”(很多人可能會(huì)這樣稱呼它)。但是,我們選擇了一個(gè)不同的名稱文本塊,為的是強(qiáng)調(diào):文本塊不是不相關(guān)行的集合,而是嵌入到 Java 程序中的二維文本塊。為了說明這里所說的“二維”是什么意思,我們舉一個(gè)稍微結(jié)構(gòu)化一些的例子,在這個(gè)例子中,文本塊是一個(gè) XML 片段。(同樣的考量也適用于其他“語言”的“程序”片段,如 SQL、HTML、JSON,甚至 Java,作為字面量嵌入到 Java 程序中。)

          復(fù)制代碼

          void m() {    System.out.println("""                       <person>                           <firstName>Bob</firstName>                           <lastName>Jones</lastName>                       </person>                       """);}

          作者希望這段代碼打印什么?雖然我們無法讀取他們的想法,但似乎不太可能是想讓 XML 塊縮進(jìn) 21 個(gè)空格;更可能是,這 21 個(gè)空格只是用來將文本塊與周圍的代碼對(duì)齊。另一方面,幾乎可以肯定,作者的意圖是輸出的第二行應(yīng)該比第一行多縮進(jìn)四個(gè)空格。此外,即使作者確實(shí)需要縮進(jìn) 21 個(gè)空格,當(dāng)程序修改、周圍代碼的縮進(jìn)發(fā)生變化時(shí),又會(huì)發(fā)生什么呢?我們不希望輸出的縮進(jìn)僅僅因?yàn)樵创a被重新格式化而改變,也不希望文本塊因?yàn)闆]有合理對(duì)齊而看起來“格格不入”。

          從這個(gè)例子中,我們可以看到,嵌入在程序源代碼中的多行文本塊的自然縮進(jìn)來自于文本塊各行行之間預(yù)期的相對(duì)縮進(jìn),以及文本塊與周圍代碼之間的相對(duì)縮進(jìn)。我們希望字符串字面量與代碼對(duì)齊(因?yàn)槿绻粚?duì)齊就會(huì)顯得格格不入),我們希望字符串的行可以反映出行之間的相對(duì)縮進(jìn),但是這兩個(gè)縮進(jìn)來源(我們可以稱之為附帶的和必需的)在源程序的表示中必然混雜在一起。(傳統(tǒng)的字符串字面量沒有這個(gè)問題,因?yàn)樗鼈儾荒芸缧校圆挥脼榱耸箖?nèi)容對(duì)齊而在文本中添加額外的前導(dǎo)空格。)

          解決這個(gè)問題的一種方法是使用庫(kù)方法,我們可以將它應(yīng)用于多行字符串字面量,比如 Kotlin 的 trimIndent 方法,而 Java 確實(shí)也提供了這樣一個(gè)方法: String::stripIndent 。但是,因?yàn)檫@是很常見的問題,所以 Java 更進(jìn)一步,會(huì)在編譯時(shí)自動(dòng)去除附帶的縮進(jìn)。

          為了理順附帶的和必需的縮進(jìn),我們可以想象下,在包含整個(gè)代碼段的 XML 代碼段周圍繪制一個(gè)最小的矩形,并將這個(gè)矩形里的內(nèi)容視為一個(gè)二維文本塊。這個(gè)“魔法矩形”是文本塊的內(nèi)容,它反映了文本塊行之間的相對(duì)縮進(jìn),但是忽略了它所在的程序是如何縮進(jìn)的。

          這個(gè)“魔法矩形”的類比或許可以幫助我們理解文本塊的工作方式,但是細(xì)節(jié)要更微妙一些,因?yàn)槲覀兛赡軙?huì)希望更好地控制哪些縮進(jìn)是附帶的,哪些是必需的。可以使用結(jié)尾分隔符相對(duì)于內(nèi)容的位置來平衡附帶縮進(jìn)和必需縮進(jìn)。

          詳情

          文本塊使用三引號(hào)(""")作為其開始和結(jié)束分隔符,開始分隔符所在行的其余部分必須為空白。文本塊的內(nèi)容從下一行開始,一直延續(xù)到結(jié)束分隔符。塊內(nèi)容的編譯時(shí)處理分為三個(gè)階段:

          • 標(biāo)準(zhǔn)化行終止符。用 LF(\u000A)字符替換所有行終止符。這可以防止文本塊的值受最后編輯它的平臺(tái)的換行約定所影響。(Windows 使用 CR + LF 作為結(jié)束行;Unix 系統(tǒng)只使用 LF,甚至還有其他使用中的方案。)
          • 從每行中刪除附帶的前導(dǎo)空格和所有的尾隨空格。附帶空格通過以下方式確定:計(jì)算出一組確定行,這些行是上個(gè)步驟得出的所有非空行,以及最后一行(包含結(jié)束分隔符的行),即使它是空的;計(jì)算出所有確定行的公共空格前綴;從每個(gè)確定行中刪除公共空格前綴。
          • 解釋內(nèi)容中的轉(zhuǎn)義序列。文本塊使用與字符串和字符字面量相同的轉(zhuǎn)義序列集。執(zhí)行上面這些操作意味著像\n、\t、\s 和 <eol> 這樣的轉(zhuǎn)義字符不會(huì)影響空格處理。( JEP 368 中新增了兩個(gè)轉(zhuǎn)義序列:顯式空格\s,續(xù)行標(biāo)記 <eol>。)

          在我們的 XML 示例中,第一行和最后一行中的所有空格都將被刪除,而中間兩行會(huì)縮進(jìn)四個(gè)空格,因?yàn)樵谶@個(gè)例子中有五個(gè)確定行(包括四行 XML 代碼和結(jié)束分隔符所在的行),行的縮進(jìn)都至少和第一行內(nèi)容的縮進(jìn)相同。通常情況下,這種縮進(jìn)就是我們期望的,但有時(shí)我們可能不希望去掉所有的前導(dǎo)縮進(jìn)。例如,如果我們想讓整個(gè)塊縮進(jìn) 4 個(gè)空格,那么我們可以通過將結(jié)束分隔符向左移動(dòng) 4 個(gè)空格來實(shí)現(xiàn):

          復(fù)制代碼

          void m() {    System.out.println("""                       <person>                           <firstName>Bob</firstName>                           <lastName>Jones</lastName>                       </person>                   """);}

          因?yàn)樽詈笠恍幸彩且粋€(gè)確定行,所以公共空格前綴現(xiàn)在是塊的最后一行中結(jié)束分隔符之前的空格數(shù)量,每一行都按這個(gè)數(shù)量刪除空格,整個(gè)塊縮進(jìn) 4 個(gè)空格。我們也可以通過編程方式管理縮進(jìn),通過實(shí)例方法 String::indent,它接受一個(gè)多行字符串(不管它是否來自文本塊),并按固定數(shù)量的空格縮進(jìn)每一行:

          復(fù)制代碼

          void m() {    System.out.println("""                       <person>                           <firstName>Bob</firstName>                           <lastName>Jones</lastName>                       </person>                       """.indent(4));}

          在極端情況下,如果不需要?jiǎng)h除空格,則可以將結(jié)束分隔符一直移動(dòng)到左邊界:

          復(fù)制代碼

          void m() {    System.out.println("""                       <person>                           <firstName>Bob</firstName>                           <lastName>Jones</lastName>                       </person>""");}

          或者,我們可以通過將整個(gè)文本塊移到左邊界來達(dá)到同樣的效果:

          復(fù)制代碼

          void m() {    System.out.println("""<person>    <firstName>Bob</firstName>    <lastName>Jones</lastName></person>""");}

          乍聽起來,這些規(guī)則可能有些復(fù)雜,但選用這種規(guī)則是為了平衡各種競(jìng)爭(zhēng)性問題(既希望能夠相對(duì)于周圍的程序縮進(jìn)文本塊,又不會(huì)產(chǎn)生可變數(shù)量的附帶前導(dǎo)空格),同時(shí),如果默認(rèn)的算法不是想要的結(jié)果,還可以提供一個(gè)簡(jiǎn)單的方法來調(diào)整或選擇不刪除空格。

          嵌入式表達(dá)式

          Java 的字符串不支持表達(dá)式插值,而其他一些語言則支持;文本塊也不支持。(在某種程度上,我們將來可能會(huì)考慮這個(gè)特性,不限于文本塊,而是同樣適用于字符串。)過去,參數(shù)化的字符串表達(dá)式是通過普通的字符串連接(+)構(gòu)建的;Java 5 添加了 String::format 以支持“printf”樣式的字符串格式。

          由于需要對(duì)周圍的空格進(jìn)行全面分析,當(dāng)通過字符串連接組合成文本塊時(shí),要想正確地縮進(jìn)可不是一件簡(jiǎn)單的事。但是,文本塊的處理結(jié)果是一個(gè)普通的字符串,所以我們?nèi)匀豢梢允褂?String::format 來參數(shù)化字符串表達(dá)式。此外,我們可以使用新的 String::formatted 方法,它是 String::format 的實(shí)例版本。

          復(fù)制代碼

          String person="""                <person>                    <firstName>%s</firstName>                    <lastName>%s</lastName>                </person>                """.formatted(first, last));

          遺憾的是,這個(gè)方法也不能命名為 format,因?yàn)槲覀儾荒苤剌d具有相同名稱和參數(shù)列表的靜態(tài)實(shí)例方法。

          先例與歷史

          雖然從某種意義上說,字符串字面量是一個(gè)“微不足道”的特性,但它們的使用頻率如此之高,足以讓小煩惱累積成大煩惱。因此也就不奇怪,為什么缺乏多行字符串是近年來對(duì) Java 最常見的抱怨之一,而許多其他語言都有多種形式的字符串字面量來支持不同的用例。

          這或許令人驚訝,在各種流行的語言中,這一特性有許多不同的表示方式。說“想要多行字符串”很容易,但是在研究其他語言時(shí),我們發(fā)現(xiàn),它們的語法和目標(biāo)各不相同。(當(dāng)然,對(duì)于哪一種才是“正確”的方法,開發(fā)人員的觀點(diǎn)也很不一樣。)雖然沒有任何兩種語言是相同的,但是對(duì)于大多數(shù)許多語言都具有的特性(例如 for 循環(huán)),通常有一些通用的方法可供選擇;在 15 種語言中找到同一特性的 15 種不同解釋很罕見,但是,我們發(fā)現(xiàn),多行原始字符串字面量就是這樣。

          下表顯示了各種語言中字符串字面量的部分選項(xiàng)。其中,…是字符串字面量的內(nèi)容,對(duì)于轉(zhuǎn)義序列和嵌入式插值,可能會(huì)處理,也可能不會(huì)處理,xxx 表示一個(gè)分隔符,用戶可以任意定義,但不能與字符串的內(nèi)容沖突,而##表示數(shù)量可變的#號(hào)(可能是零個(gè))。

          語言語法說明Bash‘…’[span]Bash$’…’[esc] [span]Bash“…”[esc] [interp] [span]C“…”[esc]C++“…”[esc]C++R"xxx(…)xxx"[span] [delim]C#“…”[esc]C#$"…"[esc] [interp]C#@"…"Dart‘…’[esc] [interp]Dart“…”[esc] [interp]Dart‘’’…’’’[esc] [interp] [span]Dart“”"…"""[esc] [interp] [span]Dartr’…’[prefix]Go“…”[esc]Go…[span]Groovy‘…’[esc]Groovy“…”[esc] [interp]Groovy‘’’…’’’[esc] [span]Groovy“”"…"""[esc] [interp] [span]Haskell“…”[esc]Java“…”[esc]Javascript‘…’[esc] [span]Javascript“…”[esc] [span]Javascript…[esc] [interp] [span]Kotlin“…”[esc] [interp]Kotlin“”"…"""[interp] [span]Perl‘…’Perl“…”[esc] [interp]Perl<<‘xxx’[here]Perl<<“xxx”[esc] [interp] [here]Perlq{…}[span]Perlqq{…}[esc] [interp] [span]Python‘…’[esc]Python“…”[esc]Python‘’’…’’’[esc] [span]Python“”"…"""[esc] [span]Pythonr’…’[esc] [prefix]Pythonf’…’[esc] [interp] [prefix]Ruby‘…’[span]Ruby“…”[esc] [interp] [span]Ruby%q{…}[span] [delim]Ruby%Q{…}[esc] [interp] [span] [delim]Ruby<<-xxx[here] [interp]Ruby<<~xxx[here] [interp] [strip]Rust“…”[esc] [span]Rustr##"…"##[span] [delim]Scala“…”[esc]Scala“”"…"""[span]Scalas"…"[esc] [interp]Scalaf"…"[esc] [interp]Scalaraw"…"[interp]Swift##"…"##[esc] [interp] [delim]Swift##"""…"""##[esc] [interp] [delim] [span]

          說明:

          • esc:某種程度的轉(zhuǎn)義序列處理,轉(zhuǎn)義符通常由 C 語言樣式衍生而來(例如,\n);
          • interp:變量或任意表達(dá)式插值的部分支持;
          • span:多行字符串可以通過簡(jiǎn)單地跨越多個(gè)源代碼行來表示;
          • here:在 "here-doc" 樣式中,后面的行,一直到用戶定義的分隔符所在的行,都被視為字符串字面量;
          • prefix:前綴形式對(duì)所有其他形式的字符串字面量也都是有效的,只是為了簡(jiǎn)潔起見而省略了;
          • delim:在某種程度上可定制分隔符,比如通過包含 nonce(C++)、不同數(shù)量的#字符(Rust、Swift),或者將花括號(hào)替換為其他匹配的括號(hào)(Ruby)。
          • strip:在某種程度上支持刪除附帶縮進(jìn)。

          雖然這個(gè)表說明了字符串字面量方法的多樣性,但它實(shí)際上只觸及了表面,因?yàn)檎Z言解釋字符串字面量的方法有各種細(xì)節(jié)的差異,不可能通過這樣一個(gè)簡(jiǎn)單的表格完全說明。雖然大多數(shù)語言受 C 語言啟發(fā),使用一個(gè)轉(zhuǎn)義字符,但它們支持的轉(zhuǎn)義字符不同,是否支持以及如何支持 Unicode 轉(zhuǎn)義(例如,\unnnn),不完全支持轉(zhuǎn)義語言的形式是否仍然支持一些有限形式的分隔符字符轉(zhuǎn)義(比如,對(duì)于嵌入式引用,使用兩個(gè)引號(hào)而不是結(jié)束字符串。)簡(jiǎn)單起見,該表還省略了一些其他形式(如 C++ 中用于控制字符編碼的各種前綴)。

          語言之間最明顯的差別是分隔符的選擇,而不同的分隔符意味著不同形式的字符串字面量(有或沒有轉(zhuǎn)義字符,單行或多行,有或沒有插值,字符編碼選擇,等等),但從中我們可以看到,這些語法的選擇常常反映了該語言在設(shè)計(jì)哲學(xué)上的差異——如何平衡等各種目標(biāo),如簡(jiǎn)潔性、表現(xiàn)力和用戶便利性。

          毫不奇怪,腳本語言(bash、Perl、Ruby、Python)已經(jīng)將“用戶選擇權(quán)”放在最高優(yōu)先級(jí),它們提供多種形式的字面量,可以用多種方式表示相同的東西。但是,一般來說,在鼓勵(lì)用戶如何看待字符串字面量、提供多少種形式以及這些形式的正交性方面,各種語言都不相同。我們還看到了一些關(guān)于多行字符串的理論。有一些(比如 JavaScript 和 Go)只是將行結(jié)束符看作另一個(gè)字符,允許所有形式的字符串字面量跨越多個(gè)行,有一些(比如 C++)把它們作為一種特殊的“原始”字符串,其他語言(比如 Kotlin)則將字符串分成“簡(jiǎn)單”和“復(fù)雜”兩類,并將多行字符串歸為“復(fù)雜”這一類,還有一些提供的選項(xiàng)太多,甚至都無法這樣簡(jiǎn)單的分類。同樣,它們對(duì)“原始字符串”的解釋也各不相同。真正的原始需要某種形式的用戶可控制的分隔符(如 C++、Swift 和 Rust 所具有的形式),盡管其他語言也號(hào)稱其字符串是“原始的”,但它們?nèi)匀粸槠浣Y(jié)束(固定)分隔符保留著某種形式的轉(zhuǎn)義。

          盡管有各種各樣的方法和觀點(diǎn),但是從平衡原則性設(shè)計(jì)和表現(xiàn)力的角度來看,這項(xiàng)調(diào)查中有一個(gè)明顯的“贏家”: Swift 。它通過一種靈活的機(jī)制(單行變體和多行變體)提供轉(zhuǎn)義、插值和真正的原始字符串。在這些語言中,最新的語言擁有最動(dòng)聽的故事,這并不奇怪,因?yàn)樗泻笠娭鳎梢詮钠渌说某晒湾e(cuò)誤中汲取經(jīng)驗(yàn)教訓(xùn)。(這里,關(guān)鍵的創(chuàng)新是,雖然轉(zhuǎn)義分隔符各不相同,但都與字符串分隔符保持一致,可以避免在“熟(cooked)”和“生(raw)”模式之間進(jìn)行選擇,同時(shí)仍然跨所有形式的字符串字面量共享轉(zhuǎn)義語言——這種方法“事后看來”是很值得贊揚(yáng)的。)由于現(xiàn)有的語言限制,Java 無法大規(guī)模地采用 Swift 的方法,但是,Java 已盡可能地從 Swift 社區(qū)所做的優(yōu)秀工作中汲取靈感——并為將來開展進(jìn)一步的工作預(yù)留了空間。

          差點(diǎn)采用的做法

          文本塊并不是該特性的第一次迭代;第一次迭代是原始字符串字面量。與 Rust 的原始字符串一樣,它使用了一個(gè)大小可變的分隔符(任意數(shù)量的反單引號(hào)字符),并且完全不解釋其內(nèi)容。在完成了全部設(shè)計(jì)和原型之后,這個(gè)提議被撤回了。當(dāng)時(shí)的判斷是,這種做法雖然足夠合理,但總讓人覺得太過于死板——與傳統(tǒng)字符串字面量沒什么共同之處,因此,如果我們將來想擴(kuò)展這個(gè)特性,就沒法把它們一起擴(kuò)展。(由于現(xiàn)在的發(fā)布節(jié)奏很快,這只是讓該特性推遲了 6 個(gè)月,但我們會(huì)得到一個(gè)更好的特性。)

          JEP 326 方法的一個(gè)主要缺點(diǎn)是,原始字符串的工作方式與傳統(tǒng)的字符串字面量完全不同:不同的分隔符字符、變長(zhǎng)分隔符 vs 固定分隔符、單行 vs 多行、轉(zhuǎn)義 vs 非轉(zhuǎn)義。總會(huì)有人想要一些不同選項(xiàng)的組合,想要更多不同的形式,因此,我們走了 Bash 所走的路。最重要的是,它沒有解決“附加縮進(jìn)”的問題,這顯然會(huì)成為 Java 程序脆弱性的根源。基于此,文本塊與傳統(tǒng)的字符串字面量(分隔符語法、轉(zhuǎn)義語言)有非常多的相似之處,只在一個(gè)關(guān)鍵方面有所不同——字符串是一維字符序列還是二維文本塊。

          風(fēng)格指南

          Oracle Java 團(tuán)隊(duì)的 Jim Laskey 和 Stuart Marks 發(fā)布了一份程序員指南,詳細(xì)介紹了文本塊,并提供了樣式建議。

          在可以提高代碼清晰度時(shí)使用文本塊。連接、轉(zhuǎn)義換行和轉(zhuǎn)義引號(hào)分隔符使字符串字面量的內(nèi)容變得混亂;文本塊解決了這個(gè)問題,內(nèi)容更清晰,但在語法上,它們比傳統(tǒng)的字符串字面量更重量級(jí)。務(wù)必要在好處大于額外成本的地方使用文本塊;如果一個(gè)字符串可以放在一行中,沒有轉(zhuǎn)義換行,那么最好還是使用傳統(tǒng)的字符串字面量。

          避免復(fù)雜表達(dá)式中的內(nèi)聯(lián)文本塊。文本塊是字符串值表達(dá)式,因此,可以在任何需要字符串的地方使用,但將文本塊嵌套在復(fù)雜的表達(dá)式中有時(shí)候并不好,把它放到一個(gè)單獨(dú)的變量中會(huì)更好。當(dāng)閱讀下面的代碼時(shí),其中的文本塊會(huì)打斷代碼流,迫使讀者轉(zhuǎn)換思維:

          復(fù)制代碼

          String poem=new String(Files.readAllBytes(Paths.get("jabberwocky.txt")));String middleVerses=Pattern.compile("\n\n")                             .splitAsStream(poem)                             .match(verse -> !"""                                   ’Twas brillig, and the slithy toves                                   Did gyre and gimble in the wabe;                                   All mimsy were the borogoves,                                   And the mome raths outgrabe.                                   """.equals(verse))                             .collect(Collectors.joining("\n\n"));

          如果我們把文本塊放入它自己的變量中,讀者就更容易理解計(jì)算流程:

          復(fù)制代碼

          String firstLastVerse="""    ’Twas brillig, and the slithy toves    Did gyre and gimble in the wabe;    All mimsy were the borogoves,    And the mome raths outgrabe.    """;String poem=new String(Files.readAllBytes(Paths.get("jabberwocky.txt")));String middleVerses=Pattern.compile("\n\n")                             .splitAsStream(poem)                             .match(verse -> !firstLastVerse.equals(verse))                             .collect(Collectors.joining("\n\n"));

          避免在文本塊縮進(jìn)中混合使用空格和制表符。刪除附加縮進(jìn)的算法會(huì)計(jì)算公共空格前綴,因此,如果混合使用空格和制表符進(jìn)行縮進(jìn),該算法仍然有效。不過,這顯然更容易出錯(cuò),所以最好避免混合使用它們,而只使用其中的一種。
          將文本塊與相鄰的 Java 代碼對(duì)齊。由于附加空格會(huì)被自動(dòng)刪除,所以我們應(yīng)該利用這一點(diǎn)使代碼更易于閱讀。雖然我們可能會(huì)忍不住寫成下面這樣:

          復(fù)制代碼

          void printPoem() {    String poem="""’Twas brillig, and the slithy tovesDid gyre and gimble in the wabe;All mimsy were the borogoves,And the mome raths outgrabe.""";    System.out.print(poem);

          因?yàn)槲覀儾幌胱址杏腥魏吻皩?dǎo)縮進(jìn),但大多數(shù)時(shí)候,我們應(yīng)該寫成下面這樣:

          復(fù)制代碼

          void printPoem() {    String poem="""        ’Twas brillig, and the slithy toves        Did gyre and gimble in the wabe;        All mimsy were the borogoves,        And the mome raths outgrabe.        """;    System.out.print(poem);}

          因?yàn)檫@樣讀者更容易理解。

          不是一定要將文本與開始分隔符對(duì)齊。不過,我們可以選擇將文本塊內(nèi)容與開始分隔符對(duì)齊:

          復(fù)制代碼

          String poem="""              ’Twas brillig, and the slithy toves              Did gyre and gimble in the wabe;              All mimsy were the borogoves,              And the mome raths outgrabe.              """;

          這樣看起來很漂亮,但是如果行很長(zhǎng),或者分隔符從距離左邊距很遠(yuǎn)的地方開始,就會(huì)很麻煩,因?yàn)楝F(xiàn)在文本會(huì)一直插入到右邊距。但是,這樣的縮進(jìn)并不是必需的;我們可以使用任何縮進(jìn)方式,只要前后一致:

          復(fù)制代碼

          String poem="""    ’Twas brillig, and the slithy toves    Did gyre and gimble in the wabe;    All mimsy were the borogoves,    And the mome raths outgrabe.    """;

          當(dāng)文本塊中嵌入了三重引號(hào)時(shí),只轉(zhuǎn)義第一個(gè)引號(hào)。雖然可以每個(gè)引號(hào)都轉(zhuǎn)義,但這沒必要,并且會(huì)降低可讀性;只需要轉(zhuǎn)義第一個(gè)引號(hào):

          復(fù)制代碼

          String code="""    String source=\"""        String message="Hello, World!";        System.out.println(message);        \""";    """;

          使用\分割非常長(zhǎng)的行。除了文本塊之外,我們還獲得了兩個(gè)新的轉(zhuǎn)義序列,\s(空格字面量)和 <newline>(續(xù)行指示符)。如果文本的行非常長(zhǎng),可以使用 <newline> 在源代碼中放一個(gè)換行符,它會(huì)在字符串編譯時(shí)轉(zhuǎn)義處理期間被刪除。

          小結(jié)

          Java 程序中的字符串字面量并不局限于像“yes”和“no”這樣的短字符串;它們通常對(duì)應(yīng)于結(jié)構(gòu)化語言(如 HTML、SQL、XML、JSON 甚至 Java)的整段“程序”。保留嵌入的那段程序的二維結(jié)構(gòu),而又不引入轉(zhuǎn)義字符和其他語言上的干擾,這樣程序更不容易出錯(cuò),而且更易于閱讀。


          主站蜘蛛池模板: 国产婷婷一区二区三区| 亚洲高清成人一区二区三区| 国产一区二区四区在线观看| 亚洲熟妇AV一区二区三区浪潮| 99久久精品国产高清一区二区| 国产精品电影一区| 99精品一区二区三区| 久久国产三级无码一区二区| 色欲AV蜜桃一区二区三| 日韩一区二区视频在线观看| 一区高清大胆人体| 制服丝袜一区在线| 肉色超薄丝袜脚交一区二区| 亚洲成在人天堂一区二区| 亚洲国产精品一区第二页 | 精品深夜AV无码一区二区| 久久国产三级无码一区二区| 日本精品一区二区久久久| 国产伦精品一区二区三区免费迷| 中文字幕无码不卡一区二区三区| 日本视频一区在线观看免费 | 精品熟人妻一区二区三区四区不卡| 成人日韩熟女高清视频一区| 中文字幕人妻丝袜乱一区三区| 国产一区二区三区在线看| 国产一区二区精品| 亚洲av乱码一区二区三区香蕉| 中文字幕一区二区三区人妻少妇| 日本激情一区二区三区| 人妻精品无码一区二区三区 | 日韩爆乳一区二区无码| 91亚洲一区二区在线观看不卡| 国产精品一区在线播放| 国产综合精品一区二区| 日韩精品一区二区三区不卡| 成人一区二区免费视频| 无码人妻精品一区二区三区在线| 一本大道在线无码一区| 精品国产一区二区三区免费看 | 亲子乱av一区区三区40岁| 精品一区二区三区中文|