Javatpoint Logo
Javatpoint Logo

ungetc() function in C++

In C++, the ungetc() function is used to push a character back into the input stream. This function is part of the Standard Input/Output Library and is typically used with file input streams (FILE* streams).

The ungetc() function in C++ is part of the Standard Input/Output Library and is used to push a character back into an input stream. Its syntax is int ungetc(int c, FILE* stream);. The parameters are the character c to be pushed back and a pointer to the file stream.This function is useful when you want to "unread" a character, allowing it to be read again by subsequent input operations.

The syntax for ungetc() is as follows:

Here, c is the character to be pushed back into the stream, and stream is a pointer to the file stream.

The ungetc() function pushes the character c back into the input stream associated with the given file stream. After a character has been pushed back using ungetc(), the next input operation on that stream will read the pushed-back character instead of fetching a new character from the input source.

Program:

Output:

Read character: H
Read character: e
Read character: l
Read character: l
Read character: o
Read character: 
Read character: w
Read character: o
Read character: r
Read character: l
Read character: d
End of file reached

Explanation:

  • The program opens a file named "example.txt" for reading using std::ifstream. The file stream is checked for successful opening.
  • The while loop reads characters from the file using inputFile.get(ch). For each character read, it is processed and printed.
  • Inside the loop, there is a check for whether the character is a digit using isdigit(ch). If the character is a digit, it is pushed back into the stream using unget().
  • After processing the characters in the loop, the program attempts to read the last character again using get(ch). This demonstrates that the digits push back into the stream are read again.
  • The program prints the characters and messages to the console to illustrate the flow of characters and the use of ungetc().
  • This example simulates a scenario where you may want to process certain characters differently and push back others into the stream for further processing. The program reads characters from the file, identifies digits, and pushes them back into the stream, demonstrating the practical use of ungetc() in a more complex context.

Complexity Analysis:

Time Complexity:

  • The time complexity of the given code is primarily determined by the number of characters in the file and the operations performed within the loop.
  • The while loop iterates over each character in the file using get(ch). The time complexity of reading each character is O(1).
  • The processing and printing operations inside the loop are constant-time operations (O(1)) since they involve simple assignments and print statements.
  • The check for whether a character is a digit (isdigit(ch)) and the subsequent call to inputFile.unget() are also constant-time operations.
  • The attempt to read the last character again (get(ch)) is also a constant-time operation.
  • Considering these factors, the overall time complexity of the code is O(N), where N is the number of characters in the file.

Space Complexity:

The space complexity of the code is determined by the memory used for variables and the file input stream. Let's break it further:

  • The program uses a single character variable ch. The space complexity for variables is constant (O(1)).
  • The std::ifstream object inputFile represents the file input stream. Its space complexity is generally determined by the underlying file size, but for each iteration of the loop, only one character is read at a time, and the stream itself doesn't consume additional space proportional to the file size.
  • Considering these factors, the overall space complexity of the code is O(1) since it uses a constant amount of memory regardless of the file size.

Approach-1: Error handling

In error handling scenarios, the ungetc() function is used in C++ to handle unexpected characters that are read from the input stream. The basic idea is to read a character, perform some checks, and if the character doesn't meet the expected conditions (indicating an error), push it back into the stream for further processing or reporting.

Here's a more detailed explanation of how ungetc() can be employed for error handling:

Output:

Read character: H

Explanation:

  • The program opens a file for reading.
  • It reads a character from the file using fgetc().
  • It checks if the character is unexpected or indicates an error condition (in this case, if it's the character 'X').
  • If an unexpected character is encountered, the program handles the error appropriately (printing an error message to stderr in this case) and then uses ungetc() to push the character back into the stream.
  • If the character is not unexpected, the program continues with the regular processing.
  • This approach allows for graceful error handling by detecting unexpected input and providing an opportunity to handle the error without losing the character read from the stream. Pushing the character back with ungetc() gives the program flexibility in deciding how to proceed with the unexpected input.

Complexity Analysis:

Time Complexity:

  • The time complexity of opening a file is typically constant or very close to constant time, as it involves operating system calls to obtain file access. So, we can consider this operation O(1).
  • The time complexity of reading a character from a file is also generally considered O(1), as it involves fetching the next character from an internal buffer or the file system.
  • The conditional statement checking if ch is equal to 'X' is O(1), as it's a simple comparison.
  • The fprintf and ungetc operations are also typically O(1), involving simple operations without dependence on the size of the input.
  • The fclose operation is generally considered O(1), involving closing the file handle.
  • Overall, the time complexity of the provided code is primarily determined by constant-time operations, making it O(1) in most practical cases.

Space Complexity:

  • The space complexity of storing the file pointer is constant, or O(1), as it only requires memory to store the address of the file structure.
  • The space complexity of storing the character ch is also constant, or O(1), as it's a single variable.
  • The space complexity related to error handling and ungetc is also constant, involving only a few variables and not dependent on the input size.
  • The space complexity of closing the file is O(1), as it involves releasing resources associated with the file handle.
  • In summary, the space complexity of the provided code is primarily determined by constant-size variables and file-related structures, making it O(1) in most practical cases.

Approach-2: Parsing Numbers

Parsing numbers involves extracting numeric values from a stream of characters, such as reading a sequence of digits to form an integer or a floating-point number. The use of ungetc() in this context is a way to perform lookahead and handle scenarios where the initially read character doesn't mark the start of a numeric value.

Output:

Error opening file: No such file or directory

Explanation:

  • The program opens a file named "txt" in read mode.
  • It checks if the file was opened successfully. If there's an issue with opening the file, an error message is displayed, and the program exits with a non-zero status.
  • The program reads the first character from the file using fgetc(). This character will be examined to determine if it marks the start of a numeric value.
  • The program checks if the read character is a digit using isdigit(). If the character is a digit, it implies that it might be the start of a numeric value, and the program proceeds to process it accordingly. If the character is not a digit, indicating it's not the start of a numeric value, the program uses ungetc() to push the character back into the stream for further processing.
  • After handling the initial character, the program can continue processing the rest of the numeric value based on the specific parsing logic. This step might involve reading more characters from the file to complete the numeric value.
  • Finally, the program closes the file to release associated resources.
  • In summary, the code snippet demonstrates a strategy for parsing numbers by inspecting the initial character read from a file. Suppose the character does not indicate the start of a numeric value. In that case, it is pushed back into the stream using ungetc(), allowing for additional processing or handling of non-numeric cases. This approach is useful for scenarios where the parsing logic requires flexibility based on the context of the input data.

Complexity Analysis:

Time Complexity:

  • The time complexity of opening a file is typically constant or very close to constant time, as it involves operating system calls to obtain file access. So, we can consider this operation O(1).
  • The time complexity of reading a character from a file is generally considered O(1), as it involves fetching the next character from an internal buffer or the file system.
  • The operations within this block are also constant time. The check using isdigit() is O(1), and ungetc() is generally O(1).
  • After the initial check, the program may involve further processing based on the specific parsing logic. The time complexity of this part depends on the details of the parsing logic and can vary.
  • The time complexity of closing a file is generally considered O(1), as it involves releasing resources associated with the file handle.
  • Overall, the time complexity of the provided code is primarily determined by constant-time operations.

Space Complexity:

  • The space complexity of storing the file pointer and the character ch is constant, or O(1), as they are single variables.
  • The space complexity related to error handling and ungetc is also constant, involving only a few variables and not dependent on the input size.
  • The space complexity of closing the file is O(1), as it involves releasing resources associated with the file handle.
  • In summary, the space complexity of the provided code is primarily determined by constant-size variables and file-related structures, making it O(1) in most practical cases.

Approach-3: Tokenization

Tokenization is the process of breaking a sequence of characters into smaller units called tokens. Tokens can represent individual words, symbols, or other meaningful elements in a programming language or a natural language. In C++, the ungetc() function can be employed during tokenization to handle situations where a character doesn't belong to the current token, allowing for better control over the parsing process.

Let's break down the process of tokenization and the use of ungetc() in detail:

Output:

Error opening file: No such file or directory

Explanation:

  • The provided C++ code illustrates the process of tokenization, which is the act of breaking down a sequence of characters into smaller units called tokens. The primary goal is to identify meaningful elements, such as words or symbols, within the input stream. Here's a detailed explanation of the key concepts and steps in the code:
  • The program begins by attempting to open a file name "input.txt" in read mode.
  • The code checks if the file was opened successfully. If not, it prints an error message and exits the program. A buffer (token) is initialized to store the characters of the current token.
  • The first character from this film is read. The program enters a loop that iterates through the characters in the file.
  • For each character, it checks if the character belongs to the current token based on the isalnum() function (alphanumeric check). If the character is alphanumeric, it is added to the current token buffer.
  • If the character is not alphanumeric, indicating the completion of a token, the character is pushed back into the stream using ungetc(). The completed token is then processed, and the token buffer is reset for the next iteration.
  • If the token buffer is not empty, it signifies the completion of a token. The program processes the completed token, such as printing its contents.
  • The token buffer is reset, preparing it for the next iteration. Once the entire file has been processed, the program closes the file.

In summary, the code reads characters from a file, identifies tokens based on alphanumeric characters, and processes each completed token. The use of ungetc() allows the program to handle situations where a character does not belong to the current token, facilitating controlled tokenization of the input stream. This approach is valuable for scenarios where flexible, and customizable parsing of input is required.

Complexity Analysis:

Time Complexity:

  • The time complexity of opening a file is typically constant or very close to constant time. It involves operating system calls to obtain file access. This operation can be considered O(1).
  • The time complexity of reading characters from the file and processing them in the tokenization loop depends on the length of the input file (n). In each iteration, characters are read and processed once. Therefore, the time complexity of this part is O(n).
  • The time complexity of closing the file is generally considered constant, or O(1), as it involves releasing resources associated with the file handle.
  • Overall, the dominant factor for time complexity in this code is the linear processing of characters during file reading and tokenization, resulting in a time complexity of O(n), where n is the length of the input file.

Space Complexity:

  • The space complexity is primarily determined by the space required for the token buffer (token) and the individual characters read from the file. The space complexity for these components is O(1).
  • The space complexity is also influenced by variables such as the file pointer (file), loop control variables (ch, tokenIndex), and other constant-size variables. These contribute to a constant amount of space, yielding O(1) space complexity.
  • The space complexity related to error handling, pushing characters back, and processing completed tokens involves only a few variables and is not dependent on the input size. It is also O(1).
  • In summary, the space complexity of the provided code is primarily determined by constant-size variables and the token buffer, resulting in O(1) space complexity. The overall efficiency of the code is good, with linear time complexity during file reading and tokenization.

Approach-4: Backtracking

Backtracking is a technique used in parsing algorithms, such as recursive descent parsing, to handle situations where a parsing branch doesn't succeed. When a parser encounters an unexpected token or fails to match a particular grammar rule, it may need to backtrack and explore alternative parsing paths. The ungetc() function in C++ can be used in this context to undo a character read from the input stream, allowing the parser to explore alternative paths.

Output:

Error opening file: No such file or directory

Explanation:

  • The provided C++ code implements a recursive descent parser for a simplified expression language containing variables (single alphabetical characters) and parentheses. The parseExpression function attempts to parse expressions, utilizing the parseAtomicExpression function for handling atomic expressions and backtracking using ungetc in case of parsing errors.
  • The parseAtomicExpression function checks if the current character is an alphabetical character (variable) and either successfully parses and prints the variable or backtracks, prints an error message, and returns
  • The parseExpression function handles expressions enclosed in parentheses by recursively calling itself for the content inside parentheses. If successful, it expects a closing parenthesis, and if not found, it backtracks, prints an error, and returns false. If the current character is not an opening parenthesis, it backtracks, attempting to parse an atomic expression.
  • In the main function, the program opens a file, attempts to parse the entire expression, prints success or failure messages, and closes the file. The sample input "(a(b)c)" demonstrates the successful parsing of variables and parentheses, producing the expected output.

Complexity Analysis:

Time Complexity:

  • The time complexity of opening a file is typically constant or very close to constant time, denoted as O(1).
  • Printing statements and error handling involve constant-time operations for each character, and the overall impact is linearly proportional to the input size. In practical terms, their contribution to overall time complexity is relatively small compared to the parsing logic.
  • Overall, the dominant factor for time complexity is the linear processing of characters during file reading and parsing, resulting in a time complexity of O(n), where n is the length of the input file.

Space Complexity:

  • The space complexity is determined by variables such as the file pointer (file), loop control variables (ch), and other constant-size variables. The space complexity for these components is O(1).
  • The recursion in parseExpression can lead to a stack of recursive calls. The maximum depth of the recursion is bounded by the nesting level of parentheses in the input expression. In the worst case, it is O(n), where n is the length of the input file.
  • In summary, the space complexity is primarily determined by constant-size variables and the depth of the recursion stack, making it O(n) in the worst case. The overall efficiency of the code is reasonable, with linear time and space complexity proportional to the length of the input file.






Youtube For Videos Join Our Youtube Channel: Join Now

Feedback


Help Others, Please Share

facebook twitter pinterest

Learn Latest Tutorials


Preparation


Trending Technologies


B.Tech / MCA