I have an import task that parses a huge .dat file as an Aspose Workbook.
This was the original code:
public static List<QueryResult> GetAsposeExcelDocumentWithSheetIndex(string filename, string propertyNames, int sheetIndex = 0, int skipRows = 0, int columnToScanForValidEntries = 0, Stream stream = null)
{
Sitecore.Diagnostics.Log.Info("Begin GetAsposeExcelDocumentWithSheetIndex: Filename: " + filename, typeof(UploadHelper));
var loadOptions = new Aspose.Cells.LoadOptions(LoadFormat.Excel97To2003);
if (filename.EndsWith(".xlsx"))
{
loadOptions = new Aspose.Cells.LoadOptions(LoadFormat.Xlsx);
}
else if (filename.EndsWith(".dat") || filename.EndsWith(".csv"))
{
loadOptions = new Aspose.Cells.LoadOptions(LoadFormat.CSV);
}
//var fsStats = new StreamWriter(HttpContext.Current.Server.MapPath("/_uploads/ftp/import.txt"), false);
//fsStats.WriteLine(DateTime.Now.ToString("h:mm:ss") + " - Opening file: " + filename);
//fsStats.Flush();
loadOptions.ParsingFormulaOnOpen = false;
try
{
var workbook = stream != null ? new Workbook(stream: stream, loadOptions: loadOptions) : new Aspose.Cells.Workbook(filename, loadOptions);
Worksheet worksheet = workbook.Worksheets[sheetIndex];
//fsStats.WriteLine(DateTime.Now.ToString("h:mm:ss") + " - Opened sheet");
//fsStats.Flush();
var list = new List<QueryResult>();
for (int i = skipRows, loopTo = worksheet.Cells.Rows.Count + skipRows - 1; i <= loopTo; i++)
{
//fsStats.WriteLine(DateTime.Now.ToString("h:mm:ss") + " - Reading row: " + i);
//fsStats.Flush();
if (worksheet.Cells.GetCell(i, columnToScanForValidEntries) != null &&
worksheet.Cells.GetCell(i, columnToScanForValidEntries).Value != null)
{
NumberFormatInfo nfi = new CultureInfo("en-US", false).NumberFormat;
var obj = new QueryResult();
var propNames = propertyNames.Split(',');
for (int j = 0, loopTo1 = propNames.Length - 1; j <= loopTo1; j++)
{
Cell cellInfo = worksheet.Cells.GetCell(i, j);
if (cellInfo != null)
{
// specific excluded properties
var propName = propNames[j];
var parseAsString = PropNameShouldNeverBeParsedAsDate(propName);
if ((cellInfo.Type == CellValueType.IsDateTime) && !parseAsString && cellInfo.DateTimeValue.Year > 2010 && !cellInfo.Name.StartsWith("X") && !cellInfo.Name.StartsWith("Y") && !cellInfo.Name.StartsWith("AC")) // this second check is bc some dates weren't return as a DateTime type in the Iplot file
{
// Colmun X and Y should NEVER be a date time
obj.Properties.Add(propNames[j], cellInfo.DateTimeValue);
}
else
{
obj.Properties.Add(propNames[j], cellInfo.StringValue);
}
}
else
{
obj.Properties.Add(propNames[j], null);
}
}
list.Add(obj);
}
}
//fsStats.WriteLine(DateTime.Now.ToString("h:mm:ss") + " - Done reading");
//fsStats.Flush();
//fsStats.Close();
return list;
}
catch (Exception ex)
{
Sitecore.Diagnostics.Error.LogError("Error GetAsposeExcelDocumentWithSheetIndex:" + ex.ToString() + ex.StackTrace);
//fsStats.WriteLine(DateTime.Now.ToString("h:mm:ss") + " - Error: " + ex.ToString());
//fsStats.Flush();
//fsStats.Close();
}
return null;
}
This worked as expected on my local, however the production website is hosted in Germany. The code runs correctly when manually triggered from the admin page (which requires being logged into Sitecore), but when run automatically as a scheduled task, it fails because it's not parsing numbers correctly
public static List<QueryResult> GetAsposeExcelDocumentWithSheetIndex(string filename, string propertyNames, int sheetIndex = 0, int skipRows = 0, int columnToScanForValidEntries = 0, Stream stream = null)
{
//var fsStats = new StreamWriter(HttpContext.Current.Server.MapPath("/_uploads/ftp/import.txt"), false);
//fsStats.WriteLine(DateTime.Now.ToString("h:mm:ss") + " - Opening file: " + filename);
//fsStats.Flush();
loadOptions.ParsingFormulaOnOpen = false;
try
{
var workbook = stream != null ? new Workbook(stream: stream, loadOptions: loadOptions) : new Aspose.Cells.Workbook(filename, loadOptions);
Worksheet worksheet = workbook.Worksheets[sheetIndex];
//fsStats.WriteLine(DateTime.Now.ToString("h:mm:ss") + " - Opened sheet");
//fsStats.Flush();
var list = new List<QueryResult>();
for (int i = skipRows, loopTo = worksheet.Cells.Rows.Count + skipRows - 1; i <= loopTo; i++)
{
//fsStats.WriteLine(DateTime.Now.ToString("h:mm:ss") + " - Reading row: " + i);
//fsStats.Flush();
if (worksheet.Cells.GetCell(i, columnToScanForValidEntries) != null &&
worksheet.Cells.GetCell(i, columnToScanForValidEntries).Value != null)
{
NumberFormatInfo nfi = new CultureInfo("en-US", false).NumberFormat;
var obj = new QueryResult();
var propNames = propertyNames.Split(',');
for (int j = 0, loopTo1 = propNames.Length - 1; j <= loopTo1; j++)
{
Cell cellInfo = worksheet.Cells.GetCell(i, j);
if (cellInfo != null)
{
// specific excluded properties
var propName = propNames[j];
var parseAsString = PropNameShouldNeverBeParsedAsDate(propName);
if ((cellInfo.Type == CellValueType.IsDateTime) && !parseAsString && cellInfo.DateTimeValue.Year > 2010 && !cellInfo.Name.StartsWith("X") && !cellInfo.Name.StartsWith("Y") && !cellInfo.Name.StartsWith("AC")) // this second check is bc some dates weren't return as a DateTime type in the Iplot file
{
// Colmun X and Y should NEVER be a date time
obj.Properties.Add(propNames[j], cellInfo.DateTimeValue);
}
else
{
var value = cellInfo.StringValue;
if (cellInfo.Type == CellValueType.IsNumeric || propName.Contains("NUM_VALUE"))
{
try
{
if (cellInfo.StringValue == "2.074.927" || cellInfo.DoubleValue == 2074927 || cellInfo.DoubleValue == 2074.927)
{
Sitecore.Diagnostics.Log.Info("Value: " + cellInfo.Value.ToString() + " StringValue: " + cellInfo.StringValue + " DoubleValue: " + cellInfo.DoubleValue.ToString(nfi), typeof(UploadHelper));
Sitecore.Diagnostics.Log.Info("Convert.ToDouble Value: " + Convert.ToDouble(cellInfo.Value) + ", " + Convert.ToDouble(cellInfo.Value).ToString(nfi), typeof(UploadHelper));
Sitecore.Diagnostics.Log.Info("DoubleValue: " + (cellInfo.DoubleValue) + ", " + (cellInfo.DoubleValue).ToString(nfi), typeof(UploadHelper));
Sitecore.Diagnostics.Log.Info("StringValue: " + (cellInfo.StringValue), typeof(UploadHelper));
Sitecore.Diagnostics.Log.Info("FloatValue: " + (cellInfo.FloatValue) + ", " + (cellInfo.FloatValue).ToString(nfi), typeof(UploadHelper));
Sitecore.Diagnostics.Log.Info("StringValueWithoutFormat: " + (cellInfo.StringValueWithoutFormat), typeof(UploadHelper));
Sitecore.Diagnostics.Log.Info("IntValue: " + (cellInfo.IntValue), typeof(UploadHelper));
}
if (Convert.ToDouble(cellInfo.Value).ToString() != value)
{
Double val;
if (Double.TryParse(value, System.Globalization.NumberStyles.Any, nfi, out val))
{
Sitecore.Diagnostics.Log.Info("Using value " + val, typeof(UploadHelper));
obj.Properties.Add(propNames[j], val);
}
else
{
Sitecore.Diagnostics.Log.Info("Failed to parse as decimal, using stringValue " + value, typeof(UploadHelper));
obj.Properties.Add(propNames[j], value);
}
}
else
{
Sitecore.Diagnostics.Log.Info("Value as double matches StringValue", typeof(UploadHelper));
obj.Properties.Add(propNames[j], Convert.ToDouble(cellInfo.Value));
}
}catch(Exception ex)
{
Sitecore.Diagnostics.Log.Info(ex.Message, typeof(UploadHelper));
obj.Properties.Add(propNames[j], cellInfo.StringValue);
}
}
else
{
obj.Properties.Add(propNames[j], cellInfo.StringValue);
}
}
}
else
{
obj.Properties.Add(propNames[j], null);
}
}
list.Add(obj);
}
}
The reason why I added all the additional code for numeric values is because on the German server, cellInfo.StringValue returns an improperly formatted number as a string - it uses periods instead of commas, so large decimal values end up being returned like 12.703.1005 instead of 12,703.1005.
So, I tried to return cellInfo.DoubleValue instead when the cell is numeric, but that ALSO returns wrong. Some decimals like 12.7 are being interpreted as dates, so cellInfo.DoubleValue is entirely wrong, but cellInfo.StringValue gives me the correct number (as a string). But the one thing that's still not working either as strings or decimals is large numbers that have a comma and a decimal.
In the second block of code you can see all the logging I added in. Here is the output on my local, for parsing a value of 2074.927
27908 10:47:59 INFO Value: 2074.927 StringValue: 2074.927 DoubleValue: 2074.927
27908 10:48:02 INFO Convert.ToDouble Value: 2074.927, 2074.927
27908 10:48:04 INFO DoubleValue: 2074.927, 2074.927
27908 10:48:07 INFO StringValue: 2074.927
27908 10:48:10 INFO FloatValue: 2074.927, 2074.927
27908 10:48:21 INFO StringValueWithoutFormat: 2074.9270000000001
27908 10:48:24 INFO IntValue: 2074
and here is the output on the German server:
ManagedPoolThread #12 15:36:04 INFO Value: 2074927 StringValue: 2.074.927 DoubleValue: 2074927
ManagedPoolThread #12 15:36:04 INFO Convert.ToDouble Value: 2074927, 2074927
ManagedPoolThread #12 15:36:04 INFO DoubleValue: 2074927, 2074927
ManagedPoolThread #12 15:36:04 INFO StringValue: 2.074.927
ManagedPoolThread #12 15:36:04 INFO FloatValue: 2074927, 2074927
ManagedPoolThread #12 15:36:04 INFO StringValueWithoutFormat: 2074927
ManagedPoolThread #12 15:36:04 INFO IntValue: 2074927
On the German server, I don't have a single instance of the correct number. It early strips the decimal place (returning the number several orders of magnitude larger than it should be), or it gives me a string value that has a period instead of a comma, which I cannot parse as a decimal.
Why is the German server messing this up? How can I change my code to parse correctly on the German server? As you can see in the code I'm already trying to use NumberFormatInfo to give me the correct delimiters but it doesn't matter since the DoubleValue is returning with no delimeter to begin with.
#Anshul Samaiyar,
If you could specify your custom formatting, i.e., "## ###.##%" for the given value to the cell in MS Excel manually, you will also get this result ("1231123.%"). So, Aspose.Cells works the same way as MS Excel does. Your custom formatting is not right. Please see the following sample code with (updated) custom formatting and give it a try, it will give you expected results.
e.g.
Sample code:
//Create a new (empty) workbook
Workbook workbook = new Workbook();
//Get the first (default) worksheet
Worksheet worksheet = workbook.getWorksheets().get(0);
//Get A1 cell
Cell cell = worksheet.getCells().get("A1");
//Specify double value
Double dataValue = 12311.23;
dataValue = Double.parseDouble(dataValue.toString());
cell.putValue(dataValue);
//Create a style and set the custom formattings
Style cellStyle = workbook.createStyle();
cellStyle.setCustom("## ###.##\\%");
//Apply the style to the cell
cell.setStyle(cellStyle);
//Autofit column to show all values
worksheet.autoFitColumn(0);
//Save the Excel file
workbook.save("f:\\files\\out1.xlsx");
//Save to PDF file
workbook.save("f:\\files\\out12.pdf");
Hope this helps, you may also post queries in the dedicated forums.
Facing similar issue where
dataValue = 12311.23;
dataValue = Double.parseDouble(dataValue.toString());
cellStyle.setCustom("## ###.##%");
is not getting change to 12 311.23 but getting g converted to 1231123.% any solution for the issue is welcome.
I have a MongoDB collection PH_location, and this is one document in it:
> db.PH_location.findOne({})
{
"_id" : ObjectId("579662fec773d83e625f71e8"),
"Postal Code" : 2800,
"town" : "Bangued",
"province" : "Abra",
"metro" : ""
}
I am having trouble referring to the field "Postal Code". Find and Update operations that use the field simply cannot locate it. For instance:
> db.PH_location.findOne({},{"Postal Code":1})
{ "_id" : ObjectId("579662fec773d83e625f71e8") }
and
> db.PH_location.updateMany({}, {$rename:{"Postal Code":"ZIP_code"}})
{ "acknowledged" : true, "matchedCount" : 2271, "modifiedCount" : 0 }
Any ideas what the problem could be?
I'll bet it is a non-breaking space and not a regular space.
Try this:
db.PH_location.find({"Postal\u00a0Code" : 2800})
You can debug the characters in the keys using this:
function codepoints(str) {
var result = "";
for (var i = 0, len = str.length; i < len; i++) {
result += "'" + str[i] + "':" + str.charCodeAt(i) + ", ";
}
return result;
}
function walk(obj) {
for (prop in obj) {
if (obj.hasOwnProperty(prop) && isNaN(prop)) {
print(prop + " " + codepoints(prop));
walk(obj[prop]);
}
}
}
var cursor = db. PH_location.find()
while ( cursor.hasNext() ) {
obj = cursor.next();
walk(obj);
}
Look for the spaces ' ' in the output. You will see:
' ':32
if it is a space, or
' ':160
if it is a non- breaking space, or some other code if it is some other char.
func getListing(var qty: Int) -> String{
if(qty < qtyInStock) {
return title + ", by " + author + " ($" + NSString(format:"%\(price).2d", price) + ")...In Stock"
}
else {
//return NSString(format: "%\(price)d") + author
return title + ", by " + author + " ($" + NSString(format:"%\(price).2d") + ")...Sold out"
}
}
Output : The Great Gatsby, by F. Scott Fitzgerald ($ 00)...In Stock"
I am unable to get the price value in the output. Also I don't to have an extra spacing in between the $ sign and the price of type double. Can you please help me with this.
Use %.2f instead of %\(price).2d
You should use NSNumberFormatter to format your numbers. Just create a read-only computed property Double extension as follow:
extension Double {
var currency: String {
let styler = NSNumberFormatter()
styler.minimumFractionDigits = 2
styler.maximumFractionDigits = 2
styler.numberStyle = .CurrencyStyle
styler.currencySymbol = "$"
return styler.stringFromNumber(self)!
}
}
199.99.currency // "$199.99"
I have a function that stores the coordinates and the name of that chunk, but the problem is that this function gets called everytime new chunks get generated, but the old ones keep being loaded if they are not far. So the result is that in the textfile, the function writes the chunks sometimes 2 times.
I don't want this to happen and have the function only write every chunk once.
The main problem is that I can't use StreamwWriter and StreamReader at the same time.
This is my code:
function saveLoadedChunk() {
var loadedChunks : GameObject[] = FindObjectsOfType(GameObject) as GameObject[];
var fileName = "C:/Reactor Games/chunks.txt";
var sw : System.IO.StreamWriter = new System.IO.StreamWriter(fileName, true);
for (var i = 0; i < loadedChunks.length ; i++) {
if(loadedChunks[i].name.Substring(0,5) == "Chunk" || loadedChunks[i].name.Substring(0,5) == "_TERR") {
if(loadedChunks[i].tag != "Player") {
var xco = loadedChunks[i].transform.position.x;
var yco = loadedChunks[i].transform.position.y;
var zco = loadedChunks[i].transform.position.z;
var stringToWrite = "Chunk (" + xco + ", " + yco + ", " + zco + ")";
sw.WriteLine(stringToWrite);
}
}
}
sw.Flush();
sw.Close();
}
Preamble
I mostly work with C#, so there could be some mistakes in syntax.
Solution
You can store all your chunks in memory set. Inside saveLoadedChunk you are going to update this set and then write it's contents to file:
var chunks: HashSet<Vector3> = new HashSet<Vector3>();
function saveLoadedChunk() {
var loadedChunks : GameObject[] = FindObjectsOfType(GameObject) as GameObject[];
for (var i = 0; i < loadedChunks.length ; i++)
if(loadedChunks[i].name.Substring(0,5) == "Chunk" || loadedChunks[i].name.Substring(0,5) == "_TERR")
if(loadedChunks[i].tag != "Player")
chunks.Add(loadedChunks[i].transform.position);
var fileName = "C:/Reactor Games/chunks.txt";
var sw : System.IO.StreamWriter = new System.IO.StreamWriter(fileName, false);
for (var chunk: Vector3 in chunks) {
var stringToWrite = "Chunk (" + chunk.x + ", " + chunk.y + ", " + chunk.z + ")";
sw.WriteLine(stringToWrite);
}
}
The drawback here is:
That you start with blank chunks set every time app is launched.
You store all the chunks' coords in memory. But I wouldn't say that this is drawback until you have really-really lot of chunks.
In order to solve first problem you need to fill chunks with corrds stored in C:/Reactor Games/chunks.txt once the app is started.
In a given sentence i want to split into 10 character string. The last word should not be incomplete in the string. Splitting should be done based on space or , or .
For example:
this is ram.he works at mcity.
now the substring of 10 chars is,
this is ra.
but the output should be,
this is.
Last word should not be incomplete
You can use a regular expression that checks that the character after the match is not a word character:
string input = "this is ram.he";
Match match = Regex.Match(input, #"^.{0,10}(?!\w)");
string result;
if (match.Success)
{
result = match.Value;
}
else
{
result = string.Empty;
}
Result:
this is
An alternative approach is to build the string up token by token until adding another token would exceed the character limit:
StringBuilder sb = new StringBuilder();
foreach (Match match in Regex.Matches(input, #"\w+|\W+"))
{
if (sb.Length + match.Value.Length > 10) { break; }
sb.Append(match.Value);
}
string result = sb.ToString();
Not sure if this is the sort of thing you were looking for. Note that this could be done a lot cleaner, but should get you started ... (may want to use StringBuilder instead of String).
char[] delimiterChars = { ',', '.',' ' };
string s = "this is ram.he works at mcity.";
string step1 = s.Substring(0, 10); // Get first 10 chars
string[] step2a = step1.Split(delimiterChars); // Get words
string[] step2b = s.Split(delimiterChars); // Get words
string sFinal = "";
for (int i = 0; i < step2a.Count()-1; i++) // copy count-1 words
{
if (i == 0)
{
sFinal = step2a[i];
}
else
{
sFinal = sFinal + " " + step2a[i];
}
}
// Check if last word is a complete word.
if (step2a[step2a.Count() - 1] == step2b[step2a.Count() - 1])
{
sFinal = sFinal + " " + step2b[step2a.Count() - 1] + ".";
}
else
{
sFinal = sFinal + ".";
}