2011-08-24 58 views
9

我知道我可以通过添加一个逗号可能在不插入新行的情况下获得用户输入?

print "Hello, world!", 

从写一个换行符停止打印,但我怎么停止raw_input从写一个换行符?

print "Hello, ", 
name = raw_input() 
print ", how do you do?" 

结果:

你好,托马斯
,你怎么办?

结果我想:

你好,托马斯,你怎么办?

+0

为了记录,如果'raw_input'中的换行符是由于用户单击返回键,那么这就是我的预期。我仍然需要一种方法来避免它:-) – Hubro

+2

第一个结果不会是“你好,托马斯
托马斯,你好吗?” ;) – Jacob

+0

我之前写过,我认为 – Hubro

回答

6

但是,如何阻止raw_input写入换行符?

总之:你不能。

raw_input()将始终回显用户输入的文本,包括尾随的换行符。这意味着用户输入的任何内容都将打印到标准输出。

如果您想防止这种情况发生,您必须使用终端控制库,例如curses模块。但这不是可移植的,例如,在Windows系统上不可用的curses

+0

'raw_input()'根据自己的文档字符串去除尾随的换行符。 – Wieland

+1

@Wieland H:但它仍然会**回声**,例如将其写入标准输出。这就是我所说的。返回值将删除换行符,但这与此问题无关。在投票前阅读。 –

0

getpass.getpass()做你想做的事情吗?

+0

看起来像一个很好的建议。不幸的是,'getpass'也打印一个换行符。 –

7

这就避免它,有点,但不分配任何变量name

print("Hello, {0}, how do you do?".format(raw_input("Enter name here: "))) 

它会提示输入名称的用户,虽然打印整个消息之前。

6

我看到没有人提供工作解决方案,所以我决定我可以放弃它。 由于Ferdinand Beyer表示,在用户输入后不可能得到raw_input()不打印新行。但是,有可能回到以前的路线。 我把它变成了一行。你可以使用:

print '\033[{}C\033[1A'.format(len(x) + y), 

其中x是给定用户输入和yraw_input()的字符串的长度的整数的长度的整数。虽然它可能不适用于所有终端(正如我在了解这种方法时阅读的),但它对我的工作正常。我正在使用Kubuntu 14.04。
字符串'\033[4C'用于向右跳4个索引,因此它将相当于' ' * 4。以同样的方式,字符串'\033[1A'用于跳转1排。通过在字符串的最后一个索引处使用字母AB,CD,您可以分别向上,向下,向右和向左移动。

请注意,排队将删除该点上的现有打印字符(如果有的话)。

+0

这太棒了,谢谢! – Matt

1

像Nick K.说的,你需要将文本光标移回到换行符之前。问题在于,为了向右移动,不容易获得前一行的长度,以免存储打印,提示并输入到其自己的变量中的每个字符串。

下面是一个类(用于Python 3),它通过自动存储来自终端(提供您使用其方法)的最后一行来修复此类。与使用终端控制库相比,这样做的好处是可以在标准终端中使用最新版本的Windows以及* NIX操作系统。在获得输入之前,它还会打印“你好,”提示。

如果您使用Windows而不是Windows 10的v1511,那么您需要安装colorama模块,否则这将不起作用,因为它们在该版本中引入了ANSI光标移动支持。

# For the sys.stdout file-like object 
import sys 
import platform 

if platform.system() == 'Windows': 
    try: 
     import colorama 
    except ImportError: 
     import ctypes 
     kernel32 = ctypes.windll.kernel32 
     # Enable ANSI support on Windows 10 v1511 
     kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7) 
    else: 
     colorama.init() 
else: 
    # Fix Linux arrow key support in Python scripts 
    import readline 


class TempHistory: 
    """Record one line from the terminal. 

    It is necessary to keep track of the last line on the terminal so we 
    can move the text cursor rightward and upward back into the position 
    before the newline from the `input` function was echoed. 

    Note: I use the term 'echo' to refer to when text is 
    shown on the terminal but might not be written to `sys.stdout`. 

    """ 

    def __init__(self): 
     """Initialise `line` and save the `print` and `input` functions. 

     `line` is initially set to '\n' so that the `record` method 
     doesn't raise an error about the string index being out of range. 

     """ 
     self.line = '\n' 
     self.builtin_print = print 
     self.builtin_input = input 

    def _record(self, text): 
     """Append to `line` or overwrite it if it has ended.""" 
     if text == '': 
      # You can't record nothing 
      return 
     # Take into account `text` being multiple lines 
     lines = text.split('\n') 
     if text[-1] == '\n': 
      last_line = lines[-2] + '\n' 
      # If `text` ended with a newline, then `text.split('\n')[-1]` 
      # would have merely returned the newline, and not the text 
      # preceding it 
     else: 
      last_line = lines[-1] 
     # Take into account return characters which overwrite the line 
     last_line = last_line.split('\r')[-1] 
     # `line` is considered ended if it ends with a newline character 
     if self.line[-1] == '\n': 
      self.line = last_line 
     else: 
      self.line += last_line 

    def _undo_newline(self): 
     """Move text cursor back to its position before echoing newline. 

     ANSI escape sequence: `\x1b[{count}{command}` 
     `\x1b` is the escape code, and commands `A`, `B`, `C` and `D` are 
     for moving the text cursor up, down, forward and backward {count} 
     times respectively. 

     Thus, after having echoed a newline, the final statement tells 
     the terminal to move the text cursor forward to be inline with 
     the end of the previous line, and then move up into said line 
     (making it the current line again). 

     """ 
     line_length = len(self.line) 
     # Take into account (multiple) backspaces which would 
     # otherwise artificially increase `line_length` 
     for i, char in enumerate(self.line[1:]): 
      if char == '\b' and self.line[i-1] != '\b': 
       line_length -= 2 
     self.print('\x1b[{}C\x1b[1A'.format(line_length), 
        end='', flush=True, record=False) 

    def print(self, *args, sep=' ', end='\n', file=sys.stdout, flush=False, 
       record=True): 
     """Print to `file` and record the printed text. 

     Other than recording the printed text, it behaves exactly like 
     the built-in `print` function. 

     """ 
     self.builtin_print(*args, sep=sep, end=end, file=file, flush=flush) 
     if record: 
      text = sep.join([str(arg) for arg in args]) + end 
      self._record(text) 

    def input(self, prompt='', newline=True, record=True): 
     """Return one line of user input and record the echoed text. 

     Other than storing the echoed text and optionally stripping the 
     echoed newline, it behaves exactly like the built-in `input` 
     function. 

     """ 
     if prompt == '': 
      # Prevent arrow key overwriting previously printed text by 
      # ensuring the built-in `input` function's `prompt` argument 
      # isn't empty 
      prompt = ' \b' 
     response = self.builtin_input(prompt) 
     if record: 
      self._record(prompt) 
      self._record(response) 
     if not newline: 
      self._undo_newline() 
     return response 


record = TempHistory() 
# For convenience 
print = record.print 
input = record.input 

print('Hello, ', end='', flush=True) 
name = input(newline=False) 
print(', how do you do?) 
0

以回溯的新行定义自己的功能的替代方案,模拟内置input功能,呼应和每个按键追加到response变量除了输入(这将返回响应),同时也处理Backspace键德尔首页结束,箭头键,行历史,一个KeyboardInterrupt,引发EOFError,SIGTSTP和粘贴从剪贴板。这是很简单

请注意,在Windows上,如果您想通过箭头键(如通常的input函数)使用线历史记录,则需要安装pyreadline,尽管它不完整,所以功能仍不完全正确。另外,如果您不在Windows 10的v1511或更高版本中,则需要安装colorama模块(如果您使用的是Linux或macOS,则无需执行任何操作)。

此外,由于msvcrt.getwch使用'\ xe0'表示特殊字符,因此您将无法键入'à'。你应该能够粘贴它。

下面是代码,使上更新的Windows 10系统(至少v1511),基于Debian的Linux发行版,也许MacOS和其他* NIX操作系统这项工作。无论您是否在Windows上安装pyreadline,它都应该可以工作,尽管它缺少一些功能。

windows_specific.py

"""Windows-specific functions and variables for input_no_newline.""" 
import ctypes 
from msvcrt import getwch # pylint: disable=import-error, unused-import 
from shared_stuff import ANSI 

try: 
    import colorama # pylint: disable=import-error 
except ImportError: 
    kernel32 = ctypes.windll.kernel32 
    # Enable ANSI support to move the text cursor 
    kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7) 
else: 
    colorama.init() 


def get_clipboard_data(): 
    """Return string previously copied from Windows clipboard. 

    Adapted from <http://stackoverflow.com/a/23285159/6379747>. 

    """ 
    CF_TEXT = 1 
    user32 = ctypes.windll.user32 
    user32.OpenClipboard(0) 
    try: 
     if user32.IsClipboardFormatAvailable(CF_TEXT): 
      data = user32.GetClipboardData(CF_TEXT) 
      data_locked = kernel32.GlobalLock(data) 
      text = ctypes.c_char_p(data_locked) 
      kernel32.GlobalUnlock(data_locked) 
    finally: 
     user32.CloseClipboard() 
    return text.value 


def sigtstp(): 
    """Raise EOFError from Ctrl-Z since SIGTSTP doesn't exist on Windows.""" 
    raise EOFError 


input_code = { 
    **ANSI, 
    'CSI': [['\xe0', '\x00'], ''], 
    'up': 'H', 
    'down': 'P', 
    'right': 'M', 
    'left': 'K', 
    'end': 'O', 
    'home': 'G', 
    'backspace': '\b', 
    'del': 'S', 
} 

unix_specific.py

"""Functions and variables for Debian-based Linux distros and macOS.""" 
import sys 
import os 
import tty 
import signal 
import termios 
from shared_stuff import ANSI 

def getwch(): 
    """Return a single character from user input without echoing. 

    ActiveState code, adapted from 
    <http://code.activestate.com/recipes/134892> by Danny Yoo under 
    the Python Software Foundation license. 

    """ 
    file_descriptor = sys.stdin.fileno() 
    old_settings = termios.tcgetattr(file_descriptor) 
    try: 
     tty.setraw(file_descriptor) 
     char = sys.stdin.read(1) 
    finally: 
     termios.tcsetattr(file_descriptor, termios.TCSADRAIN, old_settings) 
    return char 


def get_clipboard_data(): 
    """Return nothing; *NIX systems automagically change sys.stdin.""" 
    return '' 


def sigtstp(): 
    """Suspend the script.""" 
    os.kill(os.getpid(), signal.SIGTSTP) 


input_code = { 
    **ANSI, 
    'CSI': ['\x1b', '['], 
    'backspace': '\x7f', 
    'del': ['3', '~'], 
} 

readline_available.py

"""Provide functions for up and down arrows if readline is installed. 

Basically to prevent duplicate code and make it work on systems without 
readline. 

""" 
try: 
    import readline 
except ImportError: 
    import pyreadline as readline 
from shared_stuff import move_cursor 


def init_history_index(): 
    """Return index for last element of readline.get_history_item.""" 
    # readline.get_history_item is one-based 
    return readline.get_current_history_length() + 1 


def restore_history(history_index, replaced, cursor_position): 
    """Replace 'replaced' with history and return the replacement.""" 
    try: 
     replacement = readline.get_history_item(history_index) 
    except IndexError: 
     replacement = None 
    if replacement is not None: 
     move_cursor('right', len(replaced) - cursor_position) 
     print('\b \b' * len(replaced), end='', flush=True) 
     print(replacement, end='', flush=True) 
     return replacement 
    return replaced 


def store_and_replace_history(history_index, replacement, old_history): 
    """Store history and then replace it.""" 
    old_history[history_index] = readline.get_history_item(history_index) 
    try: 
     readline.replace_history_item(history_index - 1, replacement) 
    except AttributeError: 
    # pyreadline is incomplete 
     pass 


def handle_prev_history(history_index, replaced, old_history, 
         input_replaced, history_modified): 
    """Handle some up-arrow logic.""" 
    try: 
     history = readline.get_history_item(history_index - 1) 
    except IndexError: 
     history = None 
    if history is not None: 
     if history_index > readline.get_current_history_length(): 
      readline.add_history(replaced) 
      input_replaced = True 
     else: 
      store_and_replace_history(
       history_index, replaced, old_history) 
      history_modified = True 
     history_index -= 1 
    return (history_index, input_replaced, history_modified) 


def handle_next_history(history_index, replaced, old_history, 
         input_replaced, history_modified): 
    """Handle some down-arrow logic.""" 
    try: 
     history = readline.get_history_item(history_index + 1) 
    except IndexError: 
     history = None 
    if history is not None: 
     store_and_replace_history(history_index, replaced, old_history) 
     history_modified = True 
     history_index += 1 
     input_replaced = (not history_index 
          == readline.get_current_history_length()) 
    return (history_index, input_replaced, history_modified) 


def finalise_history(history_index, response, old_history, 
        input_replaced, history_modified): 
    """Change history before the response will be returned elsewhere.""" 
    try: 
     if input_replaced: 
      readline.remove_history_item(history_index - 1) 
     elif history_modified: 
      readline.remove_history_item(history_index - 1) 
      readline.add_history(old_history[history_index - 1]) 
    except AttributeError: 
    # pyreadline is also missing remove_history_item 
     pass 
    readline.add_history(response) 

readline_unavailable.py

"""Provide dummy functions for if readline isn't available.""" 
# pylint: disable-msg=unused-argument 


def init_history_index(): 
    """Return an index of 1 which probably won't ever change.""" 
    return 1 


def restore_history(history_index, replaced, cursor_position): 
    """Return the replaced thing without replacing it.""" 
    return replaced 


def store_and_replace_history(history_index, replacement, old_history): 
    """Don't store history.""" 
    pass 


def handle_prev_history(history_index, replaced, old_history, 
         input_replaced, history_modified): 
    """Return 'input_replaced' and 'history_modified' without change.""" 
    return (history_index, input_replaced, history_modified) 


def handle_next_history(history_index, replaced, old_history, 
         input_replaced, history_modified): 
    """Also return 'input_replaced' and 'history_modified'.""" 
    return (history_index, input_replaced, history_modified) 


def finalise_history(history_index, response, old_history, 
        input_replaced, history_modified): 
    """Don't change nonexistent history.""" 
    pass 

shared_stuff.py

"""Provide platform-independent functions and variables.""" 
ANSI = { 
    'CSI': '\x1b[', 
    'up': 'A', 
    'down': 'B', 
    'right': 'C', 
    'left': 'D', 
    'end': 'F', 
    'home': 'H', 
    'enter': '\r', 
    '^C': '\x03', 
    '^D': '\x04', 
    '^V': '\x16', 
    '^Z': '\x1a', 
} 


def move_cursor(direction, count=1): 
    """Move the text cursor 'count' times in the specified direction.""" 
    if direction not in ['up', 'down', 'right', 'left']: 
     raise ValueError("direction should be either 'up', 'down', 'right' " 
         "or 'left'") 
    # A 'count' of zero still moves the cursor, so this needs to be 
    # tested for. 
    if count != 0: 
     print(ANSI['CSI'] + str(count) + ANSI[direction], end='', flush=True) 


def line_insert(text, extra=''): 
    """Insert text between terminal line and reposition cursor.""" 
    if not extra: 
    # It's not guaranteed that the new line will completely overshadow 
    # the old one if there is no extra. Maybe something was 'deleted'? 
     move_cursor('right', len(text) + 1) 
     print('\b \b' * (len(text)+1), end='', flush=True) 
    print(extra + text, end='', flush=True) 
    move_cursor('left', len(text)) 

最后,在input_no_newline.py

#!/usr/bin/python3 
"""Provide an input function that doesn't echo a newline.""" 
try: 
from windows_specific import getwch, get_clipboard_data, sigtstp, input_code 
except ImportError: 
    from unix_specific import getwch, get_clipboard_data, sigtstp, input_code 
try: 
    from readline_available import (init_history_index, restore_history, 
            store_and_replace_history, 
            handle_prev_history, handle_next_history, 
            finalise_history) 
except ImportError: 
    from readline_unavailable import (init_history_index, restore_history, 
             store_and_replace_history, 
             handle_prev_history, handle_next_history, 
             finalise_history) 
from shared_stuff import ANSI, move_cursor, line_insert 


def input_no_newline(prompt=''): # pylint: disable=too-many-branches, too-many-statements 
    """Echo and return user input, except for the newline.""" 
    print(prompt, end='', flush=True) 
    response = '' 
    position = 0 
    history_index = init_history_index() 
    input_replaced = False 
    history_modified = False 
    replacements = {} 

    while True: 
     char = getwch() 
     if char in input_code['CSI'][0]: 
      char = getwch() 
      # Relevant input codes are made of two to four characters 
      if char == input_code['CSI'][1]: 
       # *NIX uses at least three characters, only the third is 
       # important 
       char = getwch() 
      if char == input_code['up']: 
       (history_index, input_replaced, history_modified) = (
        handle_prev_history(
         history_index, response, replacements, input_replaced, 
         history_modified)) 
       response = restore_history(history_index, response, position) 
       position = len(response) 
      elif char == input_code['down']: 
       (history_index, input_replaced, history_modified) = (
        handle_next_history(
         history_index, response, replacements, input_replaced, 
         history_modified)) 
       response = restore_history(history_index, response, position) 
       position = len(response) 
      elif char == input_code['right'] and position < len(response): 
       move_cursor('right') 
       position += 1 
      elif char == input_code['left'] and position > 0: 
       move_cursor('left') 
       position -= 1 
      elif char == input_code['end']: 
       move_cursor('right', len(response) - position) 
       position = len(response) 
      elif char == input_code['home']: 
       move_cursor('left', position) 
       position = 0 
      elif char == input_code['del'][0]: 
       if ''.join(input_code['del']) == '3~': 
        # *NIX uses '\x1b[3~' as its del key code, but only 
        # '\x1b[3' has currently been read from sys.stdin 
        getwch() 
       backlog = response[position+1 :] 
       response = response[:position] + backlog 
       line_insert(backlog) 
     elif char == input_code['backspace']: 
      if position > 0: 
       backlog = response[position:] 
       response = response[: position-1] + backlog 
       print('\b', end='', flush=True) 
       position -= 1 
       line_insert(backlog) 
     elif char == input_code['^C']: 
      raise KeyboardInterrupt 
     elif char == input_code['^D']: 
      raise EOFError 
     elif char == input_code['^V']: 
      paste = get_clipboard_data() 
      backlog = response[position:] 
      response = response[:position] + paste + backlog 
      position += len(paste) 
      line_insert(backlog, extra=paste) 
     elif char == input_code['^Z']: 
      sigtstp() 
     elif char == input_code['enter']: 
      finalise_history(history_index, response, replacements, 
          input_replaced, history_modified) 
      move_cursor('right', len(response) - position) 
      return response 
     else: 
      backlog = response[position:] 
      response = response[:position] + char + backlog 
      position += 1 
      line_insert(backlog, extra=char) 


def main(): 
    """Called if script isn't imported.""" 
    # "print(text, end='')" is equivalent to "print text,", and 'flush' 
    # forces the text to appear, even if the line isn't terminated with 
    # a '\n' 
    print('Hello, ', end='', flush=True) 
    name = input_no_newline() # pylint: disable=unused-variable 
    print(', how do you do?') 


if __name__ == '__main__': 
    main() 

正如你可以看到,这是一个很大的不工作那么多,因为你需要处理不同的操作系统,并且基本上用Python而不是C来重新实现一个内置函数。我建议你只使用我在另一个答案中所做的更简单的TempHistory类,这会将所有复杂的逻辑处理都留给内置函数。

2

如果您不想让它换新行,您可以使用getpass而不是raw_input

import sys, getpass 

def raw_input2(value="",end=""): 
    sys.stdout.write(value) 
    data = getpass.getpass("") 
    sys.stdout.write(data) 
    sys.stdout.write(end) 
    return data 
相关问题