2011-08-24 15 views
9

So che posso smettere di stampa di scrivere una nuova riga con l'aggiunta di una virgolaÈ possibile ottenere l'input dell'utente senza inserire una nuova linea?

print "Hello, world!", 

Ma come posso impedire che raw_input di scrivere una nuova riga?

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

Risultato:

Ciao, Tomas
, come si fa?

risultato che voglio:

Ciao, Tomas, come si fa?

+0

Per il record, se il newline in 'raw_input' è dovuto all'utente che fa clic sulla chiave di ritorno, allora è quello che mi aspettavo. Ho ancora bisogno di un modo per evitarlo però :-) – Hubro

+2

Non sarebbe il primo risultato essere "Ciao, Tomas
Tomas, come si fa?" ;) – Jacob

+0

Ho scritto prima che pensassi – Hubro

risposta

6

Ma come si interrompe raw_input dalla scrittura di una nuova riga?

In breve: non è possibile.

raw_input() fa sempre eco al testo immesso dall'utente, inclusa la riga finale finale. Ciò significa che qualsiasi cosa venga scritta dall'utente verrà stampata sullo standard output.

Se si desidera evitare ciò, sarà necessario utilizzare una libreria di controllo terminale come il modulo curses. Questo non è tuttavia portatile, ad esempio, curses in non disponibile sui sistemi Windows.

+0

'raw_input()' rimuove la newline finale in base alla propria docstring. – Wieland

+1

@Wieland H .: Ma rimarrà comunque ** echo **, ad es. scrivilo sullo standard output. Questo è tutto ciò che ho detto. Il valore restituito avrà la nuova riga spogliata, ma questo è irrilevante per questa domanda. Leggi prima di votare. –

0

getpass.getpass() fa ciò che vuoi?

+0

Sembra un buon suggerimento. Sfortunatamente, 'getpass' stampa anche una nuova riga. –

7

Questo elude che, in qualche modo, ma non assegna nulla alla variabile name:

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

Esso richiederà all'utente per un nome prima di stampare l'intero messaggio però.

6

Vedo che nessuno ha dato una soluzione funzionante, quindi ho deciso che potevo provarlo. Come detto Ferdinand Beyer, è impossibile ottenere raw_input() per non stampare una nuova riga dopo l'input dell'utente. Tuttavia, è possibile tornare alla linea che eri prima. L'ho trasformato in un unico rivestimento. È possibile utilizzare:

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

dove x è un numero intero della lunghezza della proposta input dell'utente e y un numero intero della lunghezza della stringa raw_input() s'. Anche se potrebbe non funzionare su tutti i terminali (come ho letto quando ho imparato a conoscere questo metodo), funziona bene sul mio. Sto usando Kubuntu 14.04.
La stringa '\033[4C' viene utilizzata per saltare 4 indici a destra, quindi sarebbe equivalente a ' ' * 4. Allo stesso modo, la stringa '\033[1A' viene utilizzata per saltare 1 riga in su. Utilizzando le lettere A, B, C o D sull'ultimo indice della stringa, è possibile salire rispettivamente in basso, a destra e a sinistra.

Nota che andare una line up sarà cancellare il carattere stampato esistente in quel punto, se ce n'è uno.

+0

Questo è geniale, grazie! – Matt

1

come Nick K. Detto questo, è necessario spostare il cursore del testo risale a prima del ritorno a capo è stato ripreso. Il problema è che non si può facilmente ottenere la lunghezza della riga precedente al fine di spostare verso destra, per non memorizzare ogni corda stampata, richiamato ed immesso nella propria variabile.

seguito è una classe (per Python 3) che risolve questo memorizzando automaticamente l'ultima linea dal terminale (a patto di utilizzare i suoi metodi). Il vantaggio di questo rispetto all'utilizzo di una libreria di controllo terminale è che funzionerà nel terminale standard sia per l'ultima versione di Windows sia per i sistemi operativi * NIX. Inoltre, stamperà il prompt "Ciao" prima di ricevere input.

Se si è su Windows ma non su v1511 di Windows 10, è necessario installare il modulo colorama altrimenti non funzionerà, poiché hanno portato il supporto del movimento del cursore ANSI in quella versione.

# 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

Un'alternativa al backtracking il ritorno a capo sta definendo la propria funzione che emula il built-in input funzioni, facendo eco e aggiungendo ogni battitura ad una variabile response tranne Inserisci (che restituirà la risposta), mentre anche movimentazione Backspace, del, casa, Fine, i tasti freccia, la storia linea, KeyboardInterrupt, EOFError, SIGTSTP e incollare dagli appunti. È molto semplice.

Si noti che su Windows, è necessario installare pyreadline se si desidera utilizzare la cronologia delle linee con i tasti freccia come nella consueta funzione input, anche se è incompleta, quindi la funzionalità non è ancora corretta. Inoltre, se non sei su v1511 o superiore di Windows 10, è necessario installare il modulo colorama (se siete su Linux o MacOS, nulla deve essere fatto).

Inoltre, a causa di msvcrt.getwch utilizzando '\ xe0' per indicare caratteri speciali, non sarà possibile digitare 'à'. Si dovrebbe essere in grado di incollarlo però.

Di seguito è riportato il codice che rende questo lavoro su aggiornato sistemi Windows 10 (almeno v1511), distribuzioni Linux basate su Debian e forse macOS e altri sistemi operativi * NIX. Dovrebbe anche funzionare indipendentemente dal fatto che sia installato pyreadline su Windows, anche se mancherà di alcune funzionalità.

In 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', 
} 

In 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', '~'], 
} 

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

In 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 

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

E, infine, in 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() 

Come si può vedere, si tratta di un sacco di lavoro per non più di tanto in quanto è necessario per affrontare il diverso funzionamento i sistemi e fondamentalmente reimplementano una funzione built-in in Python piuttosto che in C. Vorrei raccomandare che tu usi semplicemente la più semplice classe TempHistory che ho creato in un'altra risposta, il che lascia tutta la complicata gestione della logica alla funzione built-in.

2

È possibile utilizzare getpass anziché raw_input se non si desidera che crei una nuova riga!

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 
Problemi correlati