Input a string from console using Objective C - objective-c

I am trying to enter a string (or a number of integers) from the command line using Objective C. These numbers are separated by a space.
Sample Input: 1 2 3 4 5
I am trying the code
char input[100] = {0};
NSString *inputString;
scanf("%s", input);
inputString = [NSString stringWithCString:input encoding:NSUTF8StringEncoding];
The resulting value of inputString is 1.
How do I get the entire value into the string ?

NSLog(#"Enter the string : ");
NSFileHandle *input = [NSFileHandle fileHandleWithStandardInput];
NSData *inputData = [NSData dataWithData:[input availableData]];
NSString *inputString = [[NSString alloc] initWithData:inputData encoding:NSUTF8StringEncoding];
inputString = [inputString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
NSLog(#"%#", inputString);
Here try this more precise when talking objective C as the working language

When you use %s in scanf it truncate the input at the first space. See here:
Any number of non-whitespace characters, stopping at the first
whitespace character found. A terminating null character is
automatically added at the end of the stored sequence.
You can use this according to this source:
scanf("%[^\n]s", intpu);
You can also use gets() as an alternative.

Related

Comparing strings is giving false in Objective-C

I am comparing two strings using isEqual method and it is giving NO.
if ([trie.name isEqual:string]) {
isFound = YES;
break;
}
The trie.name is a string initialized using [[NSString alloc] initWithCharacters:&uchar length:ucharLen], where uchar is a unichar. The string is a constant string #"N". I have checked the hash values and both differ. I am not sure why creating a string using different initializer gives different hash for the same string value. In this case, how to check for string equality?
Please see the attached screenshot of the debug variables.
Currently, I am using the code:
NSString *string = #"Pirates";
unichar uchar = [string characterAtIndex:0];
size_t ucharLen = sizeof(uchar);
NSString *str = [[NSString alloc] initWithCharacters:&uchar length:ucharLen];
XCTAssertTrue([str isEqual:#"P"]);
If I given the length: as 1, it works properly. How to get the length of the unichar. Will it always be 1 so that I can hardcode 1 in this case?
The problem is with:
unichar uchar = [string characterAtIndex:0];
size_t ucharLen = sizeof(uchar);
NSString *str = [[NSString alloc] initWithCharacters:&uchar length:ucharLen];
You are creating a string from a single unichar which means the length needs to be 1. But the sizeof(unichar) returns 2 since unichar is a 16-bit (2-byte) value so you end up telling the string you are passing 2 characters, not 1.
So the resulting string contains two characters - the one you actually want and a second, random bit of garbage that happens to be at that memory address.
Just hardcode 1:
unichar uchar = [string characterAtIndex:0];
NSString *str = [[NSString alloc] initWithCharacters:&uchar length:1];

ObjectiveC - Read from console string with multiple lines (Breaks\newLine)

Is there a way in objectiveC to read a string with multiple lines from user input in the console?
I'm making a simple console app in Xcode, which i want people to be able to paste SQL queries which may be in few lines.
Currently i'm using:
char strIn[512];
NSMutableString* str;
scanf("%[^\n]", strIn);
str = [NSString stringWithUTF8String:strIn];
NSLog(#"\n%#",str);
But this only gets the first line.
Remember - this is not about reading from file, but only from the console.
thanks.
The following code should help you to get started. It reads from standard input and collects the input lines in the inputString variable, until a semi-colon is found.
NSFileHandle *inputFile = [NSFileHandle fileHandleWithStandardInput];
NSMutableString *inputString = [NSMutableString string];
do {
// Read from stdin, check for EOF:
NSData *data = [inputFile availableData];
if ([data length] == 0) {
NSLog(#"EOF");
break;
}
// Convert to NSString, replace newlines by spaces, append to current input:
NSMutableString *tmp = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding];
[tmp replaceOccurrencesOfString:#"\n" withString:#" " options:0 range:NSMakeRange(0, [tmp length])];
[inputString appendString:tmp];
// Check for semi-colon:
} while ([inputString rangeOfString:#";"].location == NSNotFound);
NSLog(#"input=%#", inputString);
(Note that this sample code simple checks for a semi-colon somewhere in the input. It does not check if the semi-colon is e.g. embedded in a string.)

Get a string with ascii code objective-c

I have a ascii code, for the letter 'a', and I want to get a string by its ascii code, is it possible with NSString?
This could also work:
NSString *foo = [NSString stringWithFormat:#"%c", 97];
Didn’t test it.
If you mean you have a byte that represents an ASCII-encoded character and you want to make a string out of it, NSString has an initializer just for that.
char characterCodeInASCII = 97;
NSString *stringWithAInIt = [[NSString alloc] initWithBytes:&characterCodeInASCII length:1 encoding:NSASCIIStringEncoding];

Reading ints from NSData?

I think I am getting a little confused here, what I have is a plain text file with the numbers "5 10 2350" in it. As you can see below I am trying to read the first value using readDataOfLength, I think maybe where I am getting muddled is that I should be reading as chars, but then 10 is 2 chars and 2350 is 4. Can anyone point m in the right direction to reading these.
NSString *dataFile_IN = #"/Users/FGX/Documents/Xcode/syntax_FileIO/inData.txt";
NSFileHandle *inFile;
NSData *readBuffer;
int intBuffer;
int bufferSize = sizeof(int);
inFile = [NSFileHandle fileHandleForReadingAtPath:dataFile_IN];
if(inFile != nil) {
readBuffer = [inFile readDataOfLength:bufferSize];
[readBuffer getBytes: &intBuffer length: bufferSize];
NSLog(#"BUFFER: %d", intBuffer);
[inFile closeFile];
}
EDIT_001
Both excellent answers from Jarret and Ole, here is what I have gone with. One final question "METHOD 02" picks up a carriage return to a blank line at the bottom of the text file, returns it as a subString, which in turn gets converted to "0" can I set the NSCharacterSet to stop that, currently I just added a length check on the string.
NSInteger intFromFile;
NSScanner *scanner;
NSArray *subStrings;
NSString *eachString;
// METHOD 01 Output: 57 58 59
strBuffer = [NSString stringWithContentsOfFile:dataFile_IN encoding:NSUTF8StringEncoding error:&fileError];
scanner = [NSScanner scannerWithString:strBuffer];
while ([scanner scanInteger:&intFromFile]) NSLog(#"%d", intFromFile);
// METHOD 02 Output: 57 58 59 0
strBuffer = [NSString stringWithContentsOfFile:dataFile_IN encoding:NSUTF8StringEncoding error:&fileError];
subStrings = [strBuffer componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
for(eachString in subStrings) {
if ([eachString length] != 0) {
NSLog(#"{%#} %d", eachString, [eachString intValue]);
}
}
gary
There are several conveniences in Cocoa that can make your life a bit easier here:
NSString *dataFile_IN = #"/Users/FGX/Documents/Xcode/syntax_FileIO/inData.txt";
// Read all the data at once into a string... an convenience around the
// need the open a file handle and convert NSData
NSString *s = [NSString stringWithContentsOfFile:dataFile_IN
encoding:NSUTF8StringEncoding
error:nil];
// Use a scanner to loop over the file. This assumes there is nothing in
// the file but integers separated by whitespace and newlines
NSInteger anInteger;
NSScanner *scanner = [NSScanner scannerWithString:s];
while (![scanner isAtEnd]) {
if ([scanner scanInteger:&anInteger]) {
NSLog(#"Found an integer: %d", anInteger);
}
}
Otherwise, using your original approach, you'd pretty much have to read character-by-character, adding each character to a "buffer" and then evaluating your integer when you encounter a space (or newline, or some other separator).
If you read the file's contents into a string as Jaret suggested, and assuming the string only contains numbers and whitespace, you can also call:
NSArray *substrings = [s componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
This will split the string at whitespace and newline characters and return an array of the substrings. You would then have to convert the substrings to integers by looping over the array and calling [substring integerValue].
One way to do it would be first to first turn your readBuffer into a string as follows:
NSString * dataString = [[NSString alloc] initWithData:readBuffer encoding:NSUTF8StringEncoding];
Then split the string into values:
NSString *dataString=#"5 10 2350"; // example string to split
NSArray * valueStrings = [dataString componentsSeparatedByString:#" "];
for(NSString *valueString in valueStrings)
{
int value=[valueString intValue];
NSLog(#"%d",value);
}
Output of this is
5
10
2350

Convert NSData bytes to NSString?

I'm trying to use the BEncoding ObjC class to decode a .torrent file.
NSData *rawdata = [NSData dataWithContentsOfFile:#"/path/to/the.torrent"];
NSData *torrent = [BEncoding objectFromEncodedData:rawdata];
When I NSLog torrent I get the following:
{
announce = <68747470 3a2f2f74 6f727265 6e742e75 62756e74 752e636f 6d3a3639 36392f61 6e6e6f75 6e6365>;
comment = <5562756e 74752043 44207265 6c656173 65732e75 62756e74 752e636f 6d>;
"creation date" = 1225365524;
info = {
length = 732766208;
name = <7562756e 74752d38 2e31302d 6465736b 746f702d 69333836 2e69736f>;
"piece length" = 524288;
....
How do I convert the name into a NSString? I have tried..
NSData *info = [torrent valueForKey:#"info"];
NSData *name = [info valueForKey:#"name"];
unsigned char aBuffer[[name length]];
[name getBytes:aBuffer length:[name length]];
NSLog(#"File name: %s", aBuffer);
..which retrives the data, but seems to have additional unicode rubbish after it:
File name: ubuntu-8.10-desktop-i386.iso)
I have also tried (from here)..
NSString *secondtry = [NSString stringWithCharacters:[name bytes] length:[name length] / sizeof(unichar)];
..but this seems to return a bunch of random characters:
扵湵畴㠭ㄮⴰ敤歳潴⵰㍩㘸椮潳
The fact the first way (as mentioned in the Apple documentation) returns most of the data correctly, with some additional bytes makes me think it might be an error in the BEncoding library.. but my lack of knowledge about ObjC is more likely to be at fault..
That's an important point that should be re-emphasized I think. It turns out that,
NSString *content = [NSString stringWithUTF8String:[responseData bytes]];
is not the same as,
NSString *content = [[NSString alloc] initWithBytes:[responseData bytes]
length:[responseData length] encoding: NSUTF8StringEncoding];
the first expects a NULL terminated byte string, the second doesn't. In the above two cases content will be NULL in the first example if the byte string isn't correctly terminated.
How about
NSString *content = [[[NSString alloc] initWithData:myData
encoding:NSUTF8StringEncoding] autorelease];
NSData *torrent = [BEncoding objectFromEncodedData:rawdata];
When I NSLog torrent I get the following:
{
⋮
}
That would be an NSDictionary, then, not an NSData.
unsigned char aBuffer[[name length]];
[name getBytes:aBuffer length:[name length]];
NSLog(#"File name: %s", aBuffer);
..which retrives the data, but seems to have additional unicode rubbish after it:
File name: ubuntu-8.10-desktop-i386.iso)
No, it retrieved the filename just fine; you simply printed it incorrectly. %s takes a C string, which is null-terminated; the bytes of a data object are not null-terminated (they are just bytes, not necessarily characters in any encoding, and 0—which is null as a character—is a perfectly valid byte). You would have to allocate one more character, and set the last one in the array to 0:
size_t length = [name length] + 1;
unsigned char aBuffer[length];
[name getBytes:aBuffer length:length];
aBuffer[length - 1] = 0;
NSLog(#"File name: %s", aBuffer);
But null-terminating the data in an NSData object is wrong (except when you really do need a C string). I'll get to the right way in a moment.
I have also tried […]..
NSString *secondtry = [NSString stringWithCharacters:[name bytes] length:[name length] / sizeof(unichar)];
..but this seems to return random Chinese characters:
扵湵畴㠭ㄮⴰ敤歳潴⵰㍩㘸椮潳
That's because your bytes are UTF-8, which encodes one character in (usually) one byte.
unichar is, and stringWithCharacters:length: accepts, UTF-16. In that encoding, one character is (usually) two bytes. (Hence the division by sizeof(unichar): it divides the number of bytes by 2 to get the number of characters.)
So you said “here's some UTF-16 data”, and it went and made characters from every two bytes; each pair of bytes was supposed to be two characters, not one, so you got garbage (which turned out to be mostly CJK ideographs).
You answered your own question pretty well, except that stringWithUTF8String: is simpler than stringWithCString:encoding: for UTF-8-encoded strings.
However, when you have the length (as you do when you have an NSData), it is even easier—and more proper—to use initWithBytes:length:encoding:. It's easier because it does not require null-terminated data; it simply uses the length you already have. (Don't forget to release or autorelease it.)
A nice quick and dirty approach is to use NSString's stringWithFormat initializer to help you out. One of the less-often used features of string formatting is the ability to specify a mximum string length when outputting a string. Using this handy feature allows you to convert NSData into a string pretty easily:
NSData *myData = [self getDataFromSomewhere];
NSString *string = [NSString stringWithFormat:#"%.*s", [myData length], [myData bytes]];
If you want to output it to the log, it can be even easier:
NSLog(#"my Data: %.*s", [myData length], [myData bytes]);
Aha, the NSString method stringWithCString works correctly:
With the bencoding.h/.m files added to your project, the complete .m file:
#import <Foundation/Foundation.h>
#import "BEncoding.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
// Read raw file, and de-bencode
NSData *rawdata = [NSData dataWithContentsOfFile:#"/path/to/a.torrent"];
NSData *torrent = [BEncoding objectFromEncodedData:rawdata];
// Get the file name
NSData *infoData = [torrent valueForKey:#"info"];
NSData *nameData = [infoData valueForKey:#"name"];
NSString *filename = [NSString stringWithCString:[nameData bytes] encoding:NSUTF8StringEncoding];
NSLog(#"%#", filename);
[pool drain];
return 0;
}
..and the output:
ubuntu-8.10-desktop-i386.iso
In cases where I don't have control over the data being transformed into a string, such as reading from the network, I prefer to use NSString -initWithBytes:length:encoding: so that I'm not dependent upon having a NULL terminated string in order to get defined results. Note that Apple's documentation says if cString is not a NULL terminated string, that the results are undefined.
Use a category on NSData:
NSData+NSString.h
#interface NSData (NSString)
- (NSString *)toString;
#end
NSData+NSString.m
#import "NSData+NSString.h"
#implementation NSData (NSString)
- (NSString *)toString
{
Byte *dataPointer = (Byte *)[self bytes];
NSMutableString *result = [NSMutableString stringWithCapacity:0];
NSUInteger index;
for (index = 0; index < [self length]; index++)
{
[result appendFormat:#"0x%02x,", dataPointer[index]];
}
return result;
}
#end
Then just NSLog(#"Data is %#", [nsData toString])"
You can try this. Fine with me.
DLog(#"responeData: %#", [[[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSASCIIStringEncoding] autorelease]);
Sometimes you need to create Base64 encoded string from NSData. For instance, when you create a e-mail MIME. In this case use the following:
#import "NSData+Base64.h"
NSString *string = [data base64EncodedString];
This will work.
NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];