Xcode創(chuàng)建終端命令

典型的Mac用戶交互都是基于鼠標在屏幕上操作圖形元素來交互的.在這種交互方式之前,是用命令行和電腦交流的.命令行基于文本信息,鍵入程序名來運行,可選地帶上參數(shù).
盡管圖形界面很方便,但命令行程序依然在今天有很重要的作用.ImageMagickffmpeg在服務(wù)器中是很重要的命令行程序.實際上大多數(shù)網(wǎng)絡(luò)服務(wù)都只運行命令行程序.
這篇文章會教你寫一個叫Panagram的命令行程序.他會根據(jù)你傳入的參數(shù),來判斷是回文還是變位詞.它可以以預(yù)定義的參數(shù)開始鼻由,或者交互模式下呆万,提示用戶輸入所需的值。
通常,命令行程序從shell啟動,比macOS中的終端的bash shell.簡單起見和方便學習,大多數(shù)時間我們都在Xcode啟動命令行程序.最后才講從終端啟動命令行程序.

Getting Started

Swift相對于傳統(tǒng)的C, Perl, Ruby 或者 Java語言創(chuàng)建命令行程序是比較奇特的選擇.
但是選他肯定是有原因的:
1.Swift可以作為解釋型的腳本語言,也可以作為編譯語言.作為腳本語言可以省去編譯,易于維護.作為編譯語言可以提高運行效率,或者打包出售給社會.
2.不必切換語言.和多人說程序員每年都要學一門新語言.這是個好想法,但是如果你已經(jīng)熟悉了Swift和它的標準庫,你用Swift就是節(jié)約了時間.

這個教程會教你創(chuàng)建一個的編譯項目.

打開 Xcode 來到 File/New/Project. 找到macOS組, 選擇Application/Command Line Tool,點擊Next:

在Product Name, 輸入Panagram. 確保語言是Swift, 然后點擊Next:

選擇一個位置存儲你的項目,然后點擊Create.
在Project Navigator area你會看到由末模板創(chuàng)建的main.swift文件


很多類C語言都有一個main函數(shù)作為程序入口.也就意味著程序啟動一啟動就執(zhí)行這個函數(shù)的第一行代碼.相反, Swift沒有main函數(shù),但是它有一個main文件.
當你運行程序的時候,會執(zhí)行第一行不是方法或者類聲明的代碼.所以保持main.swift文件整潔很重要.把類和結(jié)構(gòu)放到他們自己的文件里.這樣不僅合理,而且容易理解程序的執(zhí)行路徑.

The Output Stream

大多數(shù)命令行程序都會打印一些信息給用戶看.比如一個視屏格式轉(zhuǎn)換器就會打印當前的進度或者錯誤信息.
類unix比如macOS就定義兩種不一樣的輸出流:
1.標準輸出流(stdout),通常定向到顯示器,顯示信息給用戶.
2.標準錯誤流(stdout),用來顯示轉(zhuǎn)臺和錯誤信息.一般定向到顯示器,也可以定向到一個文件.

注意:無論是從Xcode還是終端啟動命令行程序,默認情況下stdout 和 stderr是一樣的,而且輸出的信息,都會寫到控制臺里.實際當中都會把stderr定向到文件中,便于之后查看.這樣可以把用戶不必知道的錯誤信息隱藏存儲起來,之后可以再慢慢根據(jù)這些錯誤信息修復(fù)bug.

在Project navigator選中Panagram,然后按 Cmd + N創(chuàng)建新文件,選擇Source/Swift File,按Next:

把文件存儲為ConsoleIO.swift,之后會封裝input和output方法到一個小小的類,命名為ConsoleIO.
添加下面的代碼到ConsoleIO.swift的最后面:

class ConsoleIO {
}

解析來的任務(wù)就是讓Panagram使用這兩個輸出流.
在ConsoleIO.swift添加下面這個枚舉類型到import行和ConsoleIO類實現(xiàn)之間:

enum OutputType {
  case error
  case standard
}

這樣就定義了輸出信息時使用的輸出流.
接下來把下面這個方法到ConsoleIO 到類里(在這個類的花括號里):

func writeMessage(_ message: String, to: OutputType = .standard) {
  switch to {
  case .standard:
    print("\(message)")
  case .error:
    fputs("Error: \(message)\n", stderr)
  }
}

這個方法有兩個參數(shù),第一個是要輸出的信息,第二個是輸出的目標地址,默認值是.standard.
.standard選項使用print,會寫入到stdout. .error選項會使用c函數(shù)fputs寫入信息到全局并且指向標準錯誤流的stderr
再增加以下代碼到ConsoleIO類里:

func printUsage() {

  let executableName = (CommandLine.arguments[0] as NSString).lastPathComponent
        
  writeMessage("usage:")
  writeMessage("\(executableName) -a string1 string2")
  writeMessage("or")
  writeMessage("\(executableName) -p string")
  writeMessage("or")
  writeMessage("\(executableName) -h to show usage information")
  writeMessage("Type \(executableName) without an option to enter interactive mode.")
}

printUsage()方法會打印使用信息到控制臺.每次運行程序,可執(zhí)行文件的路徑都在 argument[0]里,而 argument[0]可以通過全局枚舉的CommandLine訪問到.CommandLine是圍繞argc和argv參數(shù)的Swift標準庫中的封裝的代碼
注意:實際當中,當用戶使用錯了參數(shù),都會打印使用信息到控制臺.
創(chuàng)建另一個文件Panagram.swift .添加以下代碼:

class Panagram {

  let consoleIO = ConsoleIO()

  func staticMode() {
    consoleIO.printUsage()
  }

}

定義了一個有一個方法的Panagram類.這個類會處理程序的主要邏輯.當前他只是簡單地打印了使用信息.
現(xiàn)在打開main文件,把print語句替換成:

let panagram = Panagram()
panagram.staticMode()

注意:如之前描述,這些代碼會成為程序最開始執(zhí)行的代碼.
編譯運行項目,會顯示如下信息在控制臺:

usage:
Panagram -a string1 string2
or
Panagram -p string
or
Panagram -h to show usage information
Type Panagram without an option to enter interactive mode.
Program ended with exit code: 0

好了,到現(xiàn)在為止,你應(yīng)該知道什么是命令行工具,從哪里開始執(zhí)行,注意發(fā)送信息到stdout和stdout.如何有組織地安排代碼到各個文件.
下一節(jié)會處理參數(shù),完成Panagram的static mode

Command-Line Arguments

運行命令行程序的時候,打在名字之后的東西都會成為參數(shù)傳給程序,參數(shù)可以用空格分開.通常都使用兩種參數(shù):options 和 strings.
Options的起始是一個破折號,之后跟著一個字符,或者兩個破折號跟著一個單詞.比如很多程序都有-h 或者 --help選項,前者是后者的簡化.為了簡單化,我們使用前者.
打開Panagram.swift,添加下面代碼到文件的最上面,在Panagram類的范圍之外:

enum OptionType: String {
  case palindrome = "p"
  case anagram = "a"
  case help = "h"
  case unknown
  
  init(value: String) {
    switch value {
    case "a": self = .anagram
    case "p": self = .palindrome
    case "h": self = .help
    default: self = .unknown
    }
  }
}

上面代碼里用字符定義了一個枚舉類型,可以把option參數(shù)直接傳到init(_:)方法里.Panagram有三種options:-p檢測回文,-a檢測變位字,-h顯示使用信息.除此之外的都為作為一個錯誤.
接下來添加下面代碼到Panagram類里:

func getOption(_ option: String) -> (option:OptionType, value: String) {
  return (OptionType(value: option), option)
}

上面的方法接受一個option參數(shù)作為一個String,然后返回String和OptionType的一個元組.

注意:如果你不熟悉元組,看看我們的視頻[PART 5: Tuples](https://videos.raywenderlich.com/courses/51-beginning-swift-3/lessons/5?_ga=2.76197205.1321793565.1518402701-549624587.1518402701)

在Panagram里,用下面代碼替換staticMode()里面的內(nèi)容:

//1
let argCount = CommandLine.argc
//2
let argument = CommandLine.arguments[1]
//3
let (option, value) = getOption(argument.substring(from: argument.index(argument.startIndex, offsetBy: 1)))
//4
consoleIO.writeMessage("Argument count: \(argCount) Option: \(option) value: \(value)")

解釋一下代碼:
1.取得參數(shù)的數(shù)量,因為執(zhí)行路徑總是存在(CommandLine.arguments[0]),所以數(shù)量總是大于等于1

  1. 從arguments數(shù)組獲取真正的參數(shù).
    3.解析參數(shù),轉(zhuǎn)換成OptionType類型.index(_:offsetBy:)忽略了第一個字符,因為我們總是這里總是破折號.
    4.輸出解析結(jié)果到控制臺.

在main文件里,替換panagram.staticMode()這一行:

if CommandLine.argc < 2 {
  //TODO: Handle interactive mode
} else {
  panagram.staticMode()
}

如果少于兩個參數(shù),就會開啟交互模式(之后會講).否則就是非交互模式靜態(tài)模式.

現(xiàn)在,為了弄明白怎么用Xcode傳參數(shù)到命令行工具.在Toolbar點擊Panagram的Scheme:


選擇Edit Scheme:


Edit_Scheme.png

確保在左面板里選擇的是Run,點擊Arguments頁煎谍,在Arguments Passed On Launch下點+號,添加-p作為參數(shù)净赴,然后關(guān)閉哩陕。


然后運行程序,你會看到如下信息在控制臺

Argument count: 2 Option: Palindrome value: p
Program ended with exit code: 0

現(xiàn)在,你已經(jīng)添加了一個option系統(tǒng),明白如何處理參數(shù)和通過Xcode傳參數(shù).接下來會介紹Panagram的主要功能.

Anagrams and Palindromes

在你寫代碼檢測回文和變位詞之前,你得知道什么是回文和變位詞.
回文就是從前往后的或者從后往前讀都是一樣的,比如:
level
noon
A man, a plan, a canal - Panama!
可以看到,標點符號和大小寫是忽略的,所以在程序里面我們也會忽略.

變位字就是用其他單詞或者句子里的字符生成的單詞或者句子,比如:
silent <-> listen
Bolivia <-> Lobivia(一種仙人掌)

新建一個StringExtension.swift文件,添加以下代碼:

extension String {
}

講一下檢測變位字的基本流程:
1.忽略大小寫和空格
2.檢查是否包含同樣的字符,所有字符出現(xiàn)的次數(shù)一樣.

添加下面方法到StringExtension.swift:

func isAnagramOf(_ s: String) -> Bool {
  //1
  let lowerSelf = self.lowercased().replacingOccurrences(of: " ", with: "")
  let lowerOther = s.lowercased().replacingOccurrences(of: " ", with: "")
  //2
  return lowerSelf.sorted() == lowerOther.sorted()
}

闡述一下上面的邏輯:
1.移除大小寫和空格
2.比較排過序的字符

檢測回文就簡單了
1.忽略大小寫和空格
2.反轉(zhuǎn)字符比較,如果一樣就是回文

添加如下方法檢測回文:

func isPalindrome() -> Bool {
  //1
  let f = self.lowercased().replacingOccurrences(of: " ", with: "")
  //2
  let s = String(f.reversed())
  //3
  return  f == s
}

邏輯是這樣的
1.忽略大小寫和空格
2.用反轉(zhuǎn)字符
3.比較一致性,一樣就是回文

把所有都拼起來,實現(xiàn)Panagram的功能
打開Panagram.swift,替換staticMode()里面的writeMessage(_:to:):

//1
switch option {
case .anagram:
    //2
    if argCount != 4 {
        if argCount > 4 {
            consoleIO.writeMessage("Too many arguments for option \(option.rawValue)", to: .error)
        } else {
            consoleIO.writeMessage("Too few arguments for option \(option.rawValue)", to: .error)
        }        
        consoleIO.printUsage()
    } else {
        //3
        let first = CommandLine.arguments[2]
        let second = CommandLine.arguments[3]
        
        if first.isAnagramOf(second) {
            consoleIO.writeMessage("\(second) is an anagram of \(first)")
        } else {
            consoleIO.writeMessage("\(second) is not an anagram of \(first)")
        }
    }
case .palindrome:
    //4
    if argCount != 3 {
        if argCount > 3 {
            consoleIO.writeMessage("Too many arguments for option \(option.rawValue)", to: .error)
        } else {
            consoleIO.writeMessage("Too few arguments for option \(option.rawValue)", to: .error)
        }
        consoleIO.printUsage()
    } else {
        //5
        let s = CommandLine.arguments[2]
        let isPalindrome = s.isPalindrome()
        consoleIO.writeMessage("\(s) is \(isPalindrome ? "" : "not ")a palindrome")
    }
//6
case .help:
    consoleIO.printUsage()
case .unknown:
    //7
    consoleIO.writeMessage("Unknown option \(value)")
    consoleIO.printUsage()
}

1.根據(jù)參數(shù)決定執(zhí)行哪種操作
2.anagram情況下,必須有四個參數(shù),可執(zhí)行文件的路徑,-a option和兩個需要檢測的詞.如果不是四個參數(shù)就會報錯.
3.如果參數(shù)正確,就是存儲字符到本地變量,看他們是否是變位詞,然后打印結(jié)果
4.palindrome情況下,必須有三個參數(shù),第一個是執(zhí)行路徑,第二個是-p,第三個是是檢查的詞,如果不是三個,同樣也會報錯
5.檢查是否是回文,打印結(jié)果
6.-h option傳進來了,就會輸出使用信息
7.傳入未知option就會打印使用信息

編輯scheme里面的參數(shù),添加level參數(shù)到scheme:


運行程序:

level is a palindrome
Program ended with exit code: 0

Handle Input Interactively

現(xiàn)在你有了一個Panagram的基本版本.我們可以添加額外的功能,讓他可以通過輸入?yún)?shù)帶輸入流來交互.
這一節(jié),會添加代碼,不傳入一個參數(shù)讓Panagram啟動,進入交互模式,提示用戶輸入需要的內(nèi)容.
首先你需要獲得鍵盤的輸入流, stdin就指向了鍵盤.
打開ConsoleIO.swift,增加下面方法到這個類:

func getInput() -> String {
  // 1
  let keyboard = FileHandle.standardInput
  // 2
  let inputData = keyboard.availableData
  // 3
  let strData = String(data: inputData, encoding: String.Encoding.utf8)!
  // 4
  return strData.trimmingCharacters(in: CharacterSet.newlines)
}

代碼邏輯:
1.獲取鍵盤輸入
2.讀取數(shù)據(jù)
3.數(shù)據(jù)轉(zhuǎn)換成字符
4.移除換行返回文字

然后,打開Panagram.swift,添加下面方法:

func interactiveMode() {
  //1
  consoleIO.writeMessage("Welcome to Panagram. This program checks if an input string is an anagram or palindrome.")
  //2
  var shouldQuit = false
  while !shouldQuit {
    //3
    consoleIO.writeMessage("Type 'a' to check for anagrams or 'p' for palindromes type 'q' to quit.")
    let (option, value) = getOption(consoleIO.getInput())
     
    switch option {
    case .anagram:
      //4
      consoleIO.writeMessage("Type the first string:")
      let first = consoleIO.getInput()
      consoleIO.writeMessage("Type the second string:")
      let second = consoleIO.getInput()
        
      //5
      if first.isAnagramOf(second) {
        consoleIO.writeMessage("\(second) is an anagram of \(first)")
      } else {
        consoleIO.writeMessage("\(second) is not an anagram of \(first)")
      }
    case .palindrome:
      consoleIO.writeMessage("Type a word or sentence:")
      let s = consoleIO.getInput()
      let isPalindrome = s.isPalindrome()
      consoleIO.writeMessage("\(s) is \(isPalindrome ? "" : "not ")a palindrome")
    default:
      //6
      consoleIO.writeMessage("Unknown option \(value)", to: .error)
    }
  }
}

解釋代碼:
1.打印歡迎信息
2.shouldQuit打破死循環(huán)
3.提示用戶選擇模式
4.提升提示用戶輸入一個詞或者兩個詞
5.輸出結(jié)果
6.如果輸入了未知option,提示錯誤,重新開始循環(huán)
現(xiàn)在你還沒辦法打斷這個while循環(huán).在Panagram.swift添加下面這一行到OptionType,枚舉里面:

case quit = "q"

然后添加下面這行到枚舉的init(_:)里:

case "q": self = .quit

同一個文件添加一個.quit case到interactiveMode()的switch語句里:

case .quit:
  shouldQuit = true

然后修改staticMode()里.unknown case的定義成下面的樣子:

case .unknown, .quit:

打開main.swift 替換注釋成:

panagram.interactiveMode()

檢測交互模式,你要把參數(shù)都清空掉:


運行:

Welcome to Panagram. This program checks if an input string is an anagram or palindrome.
Type 'a' to check for anagrams or 'p' for palindromes type 'q' to quit.

試一試不同的模式:在每個輸入之后回車:

a
Type the first string:
silent
Type the second string:
listen
listen is an anagram of silent
Type 'a' to check for anagrams or 'p' for palindromes type 'q' to quit.
p
Type a word or sentence:
level
level is a palindrome
Type 'a' to check for anagrams or 'p' for palindromes type 'q' to quit.
f
Error: Unknown option f
Type 'a' to check for anagrams or 'p' for palindromes type 'q' to quit.
q
Program ended with exit code: 0

Launching Outside Xcode

通常命令行程序都是在通過終端里面運行的.
有很多種通過終端運行的方式.通過編譯后的二進制包直接通過終端運行,或者讓Xcode幫你.

Launch your app in Terminal from Xcode
創(chuàng)建一個會打開終端并且運行Panagram的Scheme:


命名為Panagram on Terminal:


選中Panagram on Terminal為激活狀態(tài),點擊Edit Scheme.選擇info,在Executable找到到Terminal.app,并選擇.選擇就會使用終端運行了,取消勾選Debug executable.
如下圖:


接下來選擇Arguments面板,添加一個新參數(shù)${BUILT_PRODUCTS_DIR}/${FULL_PRODUCT_NAME}:



最后關(guān)閉.
確認選擇的是Panagram on Terminal scheme,運行程序.Xcode會打開終端:


Launch your app directly from Terminal

打開Applications/Utilities文件夾.
Project Navigator 里選擇Products,拷貝右邊的路徑,不包括"Panagram":


打開finder,選擇前往文件夾(快捷鍵 shift+command+G ),粘貼拷貝的路徑并前往:


把Panagram可執(zhí)行文件拖到終端的窗口里,按回車.Panagram就會進入交互模式,因為沒有參數(shù)傳入:


注意:如果想通過這種方式進入靜態(tài)模式,就需要在按回車之前輸入?yún)?shù)比如:-p level 或則 -a silent listen.

Displaying Errors

最后會添加用來顯示紅色錯誤信息的代碼.
打開ConsoleIO.swift,到writeMessage(_:to:),替換兩個case成:

    case .standard:
      // 1
      print("\u{001B}[;m\(message)")
    case .error:
      // 2
      fputs("\u{001B}[0;31m\(message)\n", stderr)

1.\u{001B}[;m用來設(shè)置正常情況下的文本顏色
2.\u{001B}[0;31m把文本顏色變成紅色
運行,鍵入-f,就會顯示紅色信息:


項目下載

ps:1.ncurses用來寫GUI風格程序的C庫
2.Scripting in Swift is pretty awesome Swift寫腳本

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市危虱,隨后出現(xiàn)的幾起案子羊娃,更是在濱河造成了極大的恐慌,老刑警劉巖埃跷,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蕊玷,死亡現(xiàn)場離奇詭異,居然都是意外死亡弥雹,警方通過查閱死者的電腦和手機垃帅,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來剪勿,“玉大人挺智,你說我怎么就攤上這事。” “怎么了赦颇?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵二鳄,是天一觀的道長。 經(jīng)常有香客問我媒怯,道長订讼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任扇苞,我火速辦了婚禮欺殿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鳖敷。我一直安慰自己脖苏,他們只是感情好,可當我...
    茶點故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布定踱。 她就那樣靜靜地躺著棍潘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪崖媚。 梳的紋絲不亂的頭發(fā)上亦歉,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天,我揣著相機與錄音畅哑,去河邊找鬼肴楷。 笑死,一個胖子當著我的面吹牛荠呐,可吹牛的內(nèi)容都是我干的赛蔫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼泥张,長吁一口氣:“原來是場噩夢啊……” “哼濒募!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起圾结,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤瑰剃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后筝野,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晌姚,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年歇竟,在試婚紗的時候發(fā)現(xiàn)自己被綠了挥唠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡焕议,死狀恐怖宝磨,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤唤锉,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布世囊,位于F島的核電站,受9級特大地震影響窿祥,放射性物質(zhì)發(fā)生泄漏株憾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一晒衩、第九天 我趴在偏房一處隱蔽的房頂上張望嗤瞎。 院中可真熱鬧,春花似錦听系、人聲如沸贝奇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽掉瞳。三九已至,卻和暖如春髓帽,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脑豹。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工郑藏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瘩欺。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓必盖,卻偏偏與公主長得像,于是被迫代替她去往敵國和親俱饿。 傳聞我的和親對象是個殘疾皇子歌粥,可洞房花燭夜當晚...
    茶點故事閱讀 45,585評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件拍埠、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,120評論 4 61
  • 公元前五世紀失驶,這個地球上發(fā)生了了不得的事情,全人類最聰明枣购、最智慧嬉探、最偉大的一群人一起誕生了,科技發(fā)展到今天棉圈,走到了...
    塵世知行者閱讀 332評論 0 1
  • 月太亮涩堤,照得天空的顏色,好淺分瘾。 風胎围,輕輕地來,在湖面留下圈圈的漣漪,湖復(fù)平時白魂,風已遠汽纤。 縱是大漠孤煙,長河落日碧聪,終...
    李夢白閱讀 212評論 0 0
  • 1.積極暗示 很多人無法堅持健身或者閱讀冒版,是因為沒有一個明確的暗示,讓他們意識到自己完成這件事的感覺棒極了逞姿。 晨讀...
    枷行柵言閱讀 249評論 2 1
  • 1 最近在上寫作課辞嗡,每天的朋友圈分享炸出了許多在朋友圈銷聲匿跡的同學朋友,有一位許久不聯(lián)系的大學同學阿敏私聊我滞造,問...
    英十八你要克制你寄幾閱讀 375評論 0 1